Modellarga o'zgartirish kiritildi va filters qo'shildi

This commit is contained in:
2025-09-20 13:06:19 +05:00
parent d160410cd9
commit 7a2ea48a8e
39 changed files with 446 additions and 29 deletions

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,12 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.tickets.models import BadgeModel
@admin.register(BadgeModel)
class BadgeAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
)

View File

@@ -1,7 +1,7 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.tickets.models import HotelModel, HotelImagesModel
from core.apps.tickets.models import HotelModel, HotelImagesModel, HotelTypeModel
class HotelImagesInline(admin.TabularInline):
@@ -18,3 +18,11 @@ class HotelAdmin(ModelAdmin):
inlines = (
HotelImagesInline,
)
@admin.register(HotelTypeModel)
class HotelTypeModelAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
)

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,13 @@
from django_filters import rest_framework as filters
from core.apps.tickets.models import BadgeModel
class BadgeFilter(filters.FilterSet):
# name = filters.CharFilter(field_name="name", lookup_expr="icontains")
class Meta:
model = BadgeModel
fields = [
"name",
]

View File

@@ -1,23 +1,51 @@
from django_filters import rest_framework as filters
from core.apps.tickets.models import TicketorderModel, TicketsModel
from core.apps.tickets.models import TicketsModel
class TicketsFilter(filters.FilterSet):
# name = filters.CharFilter(field_name="name", lookup_expr="icontains")
title = filters.CharFilter(field_name="title", lookup_expr="icontains")
destinations = filters.CharFilter(field_name="ticket_itinerary__ticket_itinerary_destinations__name",
lookup_expr="icontains")
min_price = filters.NumberFilter(field_name="price", lookup_expr="gte")
max_price = filters.NumberFilter(field_name="price", lookup_expr="lte")
cheapest = filters.BooleanFilter(method="filter_cheapest")
most_expensive = filters.BooleanFilter(method="filter_most_expensive")
visa_required = filters.BooleanFilter(method="filter_visa_required")
duration_days = filters.NumberFilter(field_name="duration_days", lookup_expr="exact")
hotel_rating = filters.NumberFilter(field_name="ticket_hotel__rating", lookup_expr="exact")
meal_plan = filters.CharFilter(field_name="ticket_hotel__meal_plan", lookup_expr="icontains")
hotel_type = filters.CharFilter(field_name="ticket_hotel__hotel_type__name", lookup_expr="icontains")
ticket_amenities = filters.CharFilter(field_name="ticket_amenities__name", lookup_expr="icontains")
class Meta:
model = TicketsModel
fields = [
"name",
"title",
"min_price",
"max_price",
"visa_required",
"duration_days",
"destinations",
"hotel_rating",
"hotel_type",
"ticket_amenities",
]
def filter_cheapest(self, queryset, name, value):
if value:
return queryset.order_by("price")
return queryset
class TicketorderFilter(filters.FilterSet):
# name = filters.CharFilter(field_name="name", lookup_expr="icontains")
def filter_most_expensive(self, queryset, name, value):
if value:
return queryset.order_by("-price")
return queryset
class Meta:
model = TicketorderModel
fields = [
"name",
]
def filter_visa_required(self, queryset, name, value):
if value:
visa_required = queryset.filter(visa_required=value)
return visa_required
elif value is False:
visa_required = queryset.filter(visa_required=value)
return visa_required
return queryset

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,10 @@
from django import forms
from core.apps.tickets.models import BadgeModel
class BadgeForm(forms.ModelForm):
class Meta:
model = BadgeModel
fields = "__all__"

View File

@@ -0,0 +1,38 @@
# Generated by Django 5.1.3 on 2025-09-19 11:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tickets', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='BadgeModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255, verbose_name='badge name')),
('color', models.CharField(choices=[('red', 'red'), ('green', 'green'), ('blue', 'blue'), ('yellow', 'yellow'), ('orange', 'orange'), ('purple', 'purple'), ('black', 'black'), ('white', 'white'), ('gray', 'gray')], default='red', max_length=255, verbose_name='badge color')),
],
options={
'verbose_name': 'BadgeModel',
'verbose_name_plural': 'BadgeModels',
'db_table': 'badge',
},
),
migrations.AlterField(
model_name='ticketsmodel',
name='slug',
field=models.SlugField(blank=True, max_length=255, null=True, unique=True, verbose_name='slug'),
),
migrations.AddField(
model_name='ticketsmodel',
name='badge',
field=models.ManyToManyField(blank=True, null=True, to='tickets.badgemodel', verbose_name='badge'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.3 on 2025-09-20 07:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tickets', '0002_badgemodel_alter_ticketsmodel_slug_and_more'),
]
operations = [
migrations.AddField(
model_name='ticketsmodel',
name='visa_required',
field=models.BooleanField(default=True, verbose_name='visa required'),
preserve_default=False,
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2025-09-20 07:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tickets', '0003_ticketsmodel_visa_required'),
]
operations = [
migrations.AddField(
model_name='hotelmodel',
name='meal_plan',
field=models.CharField(choices=[('all_inclusive', 'All Inclusive'), ('breakfast', 'Breakfast Only'), ('half_board', 'Half Board'), ('full_board', 'Full Board')], default='breakfast', max_length=255, verbose_name='meal plan'),
),
]

View File

@@ -0,0 +1,30 @@
# Generated by Django 5.1.3 on 2025-09-20 07:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tickets', '0004_hotelmodel_meal_plan'),
]
operations = [
migrations.CreateModel(
name='HotelTypeModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='name')),
],
options={
'verbose_name': 'HotelTypeModel',
'verbose_name_plural': 'HotelTypeModels',
'db_table': 'hotel_types',
},
),
migrations.AddField(
model_name='hotelmodel',
name='hotel_type',
field=models.ManyToManyField(to='tickets.hoteltypemodel', verbose_name='hotel type'),
),
]

View File

@@ -1,4 +1,5 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa
from .tickets import * # noqa
from .tickets import * # noqa

View File

@@ -0,0 +1,34 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_core.models import AbstractBaseModel
class BadgeModel(AbstractBaseModel):
Color_Choice = (
("red", "red"),
("green", "green"),
("blue", "blue"),
("yellow", "yellow"),
("orange", "orange"),
("purple", "purple"),
("black", "black"),
("white", "white"),
("gray", "gray"),
)
name = models.CharField(max_length=255, verbose_name=_("badge name"))
color = models.CharField(max_length=255, choices=Color_Choice, verbose_name=_("badge color"), default="red")
def __str__(self):
return str(self.pk)
@classmethod
def _create_fake(self):
return self.objects.create(
name="mock",
)
class Meta:
db_table = "badge"
verbose_name = _("BadgeModel")
verbose_name_plural = _("BadgeModels")

View File

@@ -4,7 +4,32 @@ from django_core.models import AbstractBaseModel
from .tickets import TicketsModel
class HotelTypeModel(models.Model):
name = models.CharField(verbose_name=_("name"), max_length=255)
def __str__(self):
return str(self.pk)
@classmethod
def _create_fake(self):
return self.objects.create(
name="mock",
)
class Meta:
db_table = "hotel_types"
verbose_name = _("HotelTypeModel")
verbose_name_plural = _("HotelTypeModels")
class HotelModel(AbstractBaseModel):
MEAL_PLAN_CHOICES = [
("all_inclusive", "All Inclusive"),
("breakfast", "Breakfast Only"),
("half_board", "Half Board"),
("full_board", "Full Board"),
]
ticket = models.ForeignKey(TicketsModel, related_name="ticket_hotel", verbose_name=_("ticket"),
on_delete=models.CASCADE)
name = models.CharField(verbose_name=_("name"), max_length=255)
@@ -15,6 +40,9 @@ class HotelModel(AbstractBaseModel):
phone = models.CharField(verbose_name=_("phone number"), max_length=50, blank=True, null=True)
website = models.URLField(verbose_name=_("hotel website"), blank=True, null=True)
rating = models.FloatField(verbose_name=_("rating"), blank=True, null=True)
meal_plan = models.CharField(verbose_name=_("meal plan"), choices=MEAL_PLAN_CHOICES, max_length=255,
default="breakfast")
hotel_type = models.ManyToManyField(HotelTypeModel, verbose_name=_("hotel type"))
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@@ -4,7 +4,6 @@ from django_core.models import AbstractBaseModel
from core.apps.accounts.models import User
from core.apps.accounts.models.participant import ParticipantModel
from .tariff import TariffModel
from django.utils.text import slugify
@@ -29,7 +28,9 @@ class TicketsModel(AbstractBaseModel):
duration_days = models.IntegerField(verbose_name=_("duration days"))
hotel_meals = models.TextField(verbose_name=_("hotel meals"), null=True, blank=True)
tariff = models.ManyToManyField(TariffModel, related_name="ticket_tariffs", verbose_name=_("tariff"))
slug = models.SlugField(verbose_name=_("slug"), max_length=255, unique=True)
slug = models.SlugField(verbose_name=_("slug"), max_length=255, unique=True, null=True, blank=True)
badge = models.ManyToManyField("tickets.BadgeModel", verbose_name=_("badge"), null=True, blank=True)
visa_required = models.BooleanField(verbose_name=_("visa required"))
created_at = models.DateTimeField(verbose_name=_("created at"), auto_now_add=True)
updated_at = models.DateTimeField(verbose_name=_("updated at"), auto_now=True)
@@ -43,6 +44,8 @@ class TicketsModel(AbstractBaseModel):
@classmethod
def _create_fake(self):
from core.apps.tickets.models.badge import BadgeModel
return self.objects.create(
name="mock",
price=150000,
@@ -53,6 +56,7 @@ class TicketsModel(AbstractBaseModel):
rating=4.5,
hotel_info="mock",
duration_days=15,
badge=BadgeModel._create_fake(),
)
class Meta:
@@ -301,6 +305,7 @@ class TicketorderModel(AbstractBaseModel):
@classmethod
def _create_fake(self):
from core.apps.tickets.models.extra_services import ExtraServicesModel, PaidServicesModel
return self.objects.create(
user=User._create_fake(),
departure="mock",

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,12 @@
from rest_framework import permissions
class BadgePermission(permissions.BasePermission):
def __init__(self) -> None: ...
def __call__(self, *args, **kwargs):
return self
def has_permission(self, request, view):
return True

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1 @@
from .badge import * # noqa

View File

@@ -0,0 +1,29 @@
from rest_framework import serializers
from core.apps.tickets.models import BadgeModel
class BaseBadgeSerializer(serializers.ModelSerializer):
class Meta:
model = BadgeModel
fields = [
"id",
"name",
"color",
]
class ListBadgeSerializer(BaseBadgeSerializer):
class Meta(BaseBadgeSerializer.Meta): ...
class RetrieveBadgeSerializer(BaseBadgeSerializer):
class Meta(BaseBadgeSerializer.Meta): ...
class CreateBadgeSerializer(BaseBadgeSerializer):
class Meta(BaseBadgeSerializer.Meta):
fields = [
"id",
"name",
]

View File

@@ -8,7 +8,6 @@ class BaseTicketorderSerializer(serializers.ModelSerializer):
model = TicketorderModel
fields = [
"id",
"name",
]
@@ -24,5 +23,4 @@ class CreateTicketorderSerializer(BaseTicketorderSerializer):
class Meta(BaseTicketorderSerializer.Meta):
fields = [
"id",
"name",
]

View File

@@ -3,9 +3,10 @@ from core.apps.tickets.models import TicketsModel, TicketsImagesModel, TicketsAm
TicketsIncludedServicesModel, TicketsItineraryModel, TicketsItineraryImagesModel, TicketsItineraryDestinationsModel, \
TicketsHotelMealsModel, TicketsCommentsModel
from core.apps.accounts.models import User
from core.apps.accounts.serializers.user import UserSerializer
from core.apps.tickets.models.tariff import TariffModel
from core.apps.tickets.serializers.badge.badge import ListBadgeSerializer
class TicketsTariffSerializer(serializers.ModelSerializer):
@@ -108,7 +109,34 @@ class BaseTicketsSerializer(serializers.ModelSerializer):
class ListTicketsSerializer(BaseTicketsSerializer):
class Meta(BaseTicketsSerializer.Meta): ...
ticket_images = serializers.SerializerMethodField()
ticket_amenities = TicketsAmenitiesSerializer(many=True, read_only=True)
badge = ListBadgeSerializer(many=True, read_only=True)
class Meta:
model = TicketsModel
fields = [
"id",
"title",
"price",
"departure_date",
"departure",
"passenger_count",
"rating",
"duration_days",
"destination",
"ticket_images",
"ticket_amenities",
"passenger_count",
"badge",
"visa_required"
]
def get_ticket_images(self, obj):
image = obj.ticket_images.first()
if image:
return TicketsImageSerializer(image).data
return None
class RetrieveTicketsSerializer(BaseTicketsSerializer):

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,8 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from core.apps.tickets.models import BadgeModel
@receiver(post_save, sender=BadgeModel)
def BadgeSignal(sender, instance, created, **kwargs): ...

View File

@@ -1,3 +1,4 @@
from .test_badge import * # noqa
from .test_extra_services import * # noqa
from .test_hotel import * # noqa
from .test_paid_services import * # noqa

View File

@@ -0,0 +1,47 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.tickets.models import BadgeModel
class BadgeTest(TestCase):
def _create_data(self):
return BadgeModel._create_fake()
def setUp(self):
self.client = APIClient()
self.instance = self._create_data()
self.urls = {
"list": reverse("badge-list"),
"retrieve": reverse("badge-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("badge-detail", kwargs={"pk": 1000}),
}
def test_create(self):
self.assertTrue(True)
def test_update(self):
self.assertTrue(True)
def test_partial_update(self):
self.assertTrue(True)
def test_destroy(self):
self.assertTrue(True)
def test_list(self):
response = self.client.get(self.urls["list"])
self.assertTrue(response.json()["status"])
self.assertEqual(response.status_code, 200)
def test_retrieve(self):
response = self.client.get(self.urls["retrieve"])
self.assertTrue(response.json()["status"])
self.assertEqual(response.status_code, 200)
def test_retrieve_not_found(self):
response = self.client.get(self.urls["retrieve-not-found"])
self.assertFalse(response.json()["status"])
self.assertEqual(response.status_code, 404)

View File

@@ -14,9 +14,9 @@ class ExtraServicesTest(TestCase):
self.client = APIClient()
self.instance = self._create_data()
self.urls = {
"list": reverse("extra_servoces-list"),
"retrieve": reverse("extra_servoces-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("extra_servoces-detail", kwargs={"pk": 1000}),
"list": reverse("extra_services-list"),
"retrieve": reverse("extra_services-detail", kwargs={"pk": self.instance.pk}),
"retrieve-not-found": reverse("extra_services-detail", kwargs={"pk": 1000}),
}
def test_create(self):

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,8 @@
from modeltranslation.translator import TranslationOptions, register
from core.apps.tickets.models import BadgeModel
@register(BadgeModel)
class BadgeTranslation(TranslationOptions):
fields = []

View File

@@ -1,11 +1,12 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import ExtraServicesView, PaidServicesView, TariffView, TicketorderView
from .views import BadgeView, ExtraServicesView, PaidServicesView, TariffView, TicketorderView
from .views.hotel import HotelView
from .views.tickets import TicketsView
router = DefaultRouter()
router.register("badge", BadgeView, basename="badge")
router.register("ticketorder", TicketorderView, basename="ticketorder")
router.register("paid_services", PaidServicesView, basename="paid_services")
router.register("extra_services", ExtraServicesView, basename="extra_services")

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,8 @@
# from django.core.exceptions import ValidationError
class BadgeValidator:
def __init__(self): ...
def __call__(self):
return True

View File

@@ -1,3 +1,4 @@
from .badge import * # noqa
from .extra_services import * # noqa
from .hotel import * # noqa
from .tariff import * # noqa

View File

@@ -0,0 +1,21 @@
from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet
from core.apps.tickets.models import BadgeModel
from core.apps.tickets.serializers.badge import CreateBadgeSerializer, ListBadgeSerializer, RetrieveBadgeSerializer
@extend_schema(tags=["badge"])
class BadgeView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = BadgeModel.objects.all()
serializer_class = ListBadgeSerializer
permission_classes = [AllowAny]
action_permission_classes = {}
action_serializer_class = {
"list": ListBadgeSerializer,
"retrieve": RetrieveBadgeSerializer,
"create": CreateBadgeSerializer,
}

View File

@@ -14,7 +14,7 @@ from core.apps.tickets.serializers.extra_services import (
)
@extend_schema(tags=["extra_servoces"])
@extend_schema(tags=["extra_services"])
class ExtraServicesView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = ExtraServicesModel.objects.all()
serializer_class = ListExtraServicesSerializer

View File

@@ -2,7 +2,9 @@ from django_core.mixins import BaseViewSetMixin
from drf_spectacular.utils import extend_schema
from rest_framework.permissions import AllowAny
from rest_framework.viewsets import ReadOnlyModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from core.apps.tickets.filters import TicketsFilter
from core.apps.tickets.models import TicketorderModel, TicketsModel
from core.apps.tickets.serializers.tickets import (
CreateTicketorderSerializer,
@@ -16,8 +18,10 @@ from core.apps.tickets.serializers.tickets import (
@extend_schema(tags=["tickets"])
class TicketsView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = TicketsModel.objects.all()
queryset = TicketsModel.objects.all().order_by("-id")
serializer_class = ListTicketsSerializer
filterset_class = TicketsFilter
filter_backends = [DjangoFilterBackend]
permission_classes = [AllowAny]
action_permission_classes = {}