This commit is contained in:
A'zamov Samandar
2025-12-06 21:50:28 +05:00
parent 3aa20fdaa1
commit f5766aa319
140 changed files with 2376 additions and 1582 deletions

View File

@@ -1,6 +1,7 @@
from .category import * # noqa
from .search import * # noqa
from .ad import * # noqa
from .user import * # noqa
from .notification import * # noqa
from .banner import * # noqa
from .ad import *
from .banner import *
from .category import *
from .common import * # noqa
from .notification import *
from .search import *
from .user import *

View File

@@ -1,2 +1,2 @@
from .home_api import * # noqa
from .ad import * # noqa
from .home_api import *
from .ad import *

View File

@@ -1,270 +1,148 @@
from rest_framework import serializers
from django.db.models import Avg
from core.apps.accounts.choices import AccountType
from core.apps.api.models import AdModel, AdVariant, Category, AdImage, AdOption
from core.apps.api.models import AdModel, AdVariantModel, CategoryModel, AdImageModel, AdOptionModel
from core.apps.accounts.models import UserLike
from core.apps.api.choices import AdVariantType
from core.apps.api.serializers.common.color import ListColorSerializer
from core.apps.api.serializers.common.size import ListSizeSerializer
class AdOptionSerializer(serializers.ModelSerializer):
class Meta:
model = AdOption
fields = [
"id",
"name",
"value",
]
model = AdOptionModel
fields = ["id", "name", "value"]
read_only_fields = ["id"]
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
model = CategoryModel
fields = ["id", "name"]
read_only_fields = ["id"]
class AdImageSerializer(serializers.ModelSerializer):
class Meta:
model = AdImage
fields = [
"image",
"ad_variant"
]
model = AdImageModel
fields = ["id", "image", "ad_variant", "is_primary", "order"]
read_only_fields = ["id"]
def to_representation(self, instance):
data = super().to_representation(instance)
if instance.ad_variant is None:
if not instance.ad_variant:
data.pop("ad_variant", None)
return data
class AdVariantSerializer(serializers.ModelSerializer):
color = ListColorSerializer()
size = ListSizeSerializer()
class Meta:
model = AdVariant
fields = [
"id",
"variant",
"value",
"is_available",
"price",
"discount",
]
model = AdVariantModel
fields = ["id", "size", "color", "is_available", "price", "stock_quantity"]
read_only_fields = ["id", "color_name"]
class BaseAdSerializer(serializers.ModelSerializer):
class AdListSerializer(serializers.ModelSerializer):
is_liked = serializers.SerializerMethodField()
star = serializers.SerializerMethodField()
comment_count = serializers.SerializerMethodField()
rating = serializers.SerializerMethodField()
reviews_count = serializers.SerializerMethodField()
class Meta:
model = AdModel
fields = [
"id",
"name",
"price",
"image",
"is_liked",
"star",
"comment_count",
]
fields = ["id", "name", "price", "image", "discount", "is_liked", "rating", "reviews_count", "is_available"]
read_only_fields = fields
def get_star(self, obj):
avg = obj.feedback.aggregate(avg=Avg("star"))["avg"]
return avg or 0
def get_rating(self, obj):
"""Get average rating from feedbacks"""
avg = obj.feedbacks.aggregate(avg=Avg("star"))["avg"]
return round(avg, 1) if avg else 0
def get_comment_count(self, obj):
count = obj.feedback.count()
return count or 0
def get_reviews_count(self, obj):
"""Get total count of feedbacks"""
return obj.feedbacks.count()
def get_is_liked(self, obj):
"""Check if current user liked this ad"""
request = self.context.get("request")
user = getattr(request, "user", None)
if not user or not user.is_authenticated:
if not request or not request.user.is_authenticated:
return False
return UserLike.objects.filter(user=user, ad=obj).exists()
return UserLike.objects.filter(user=request.user, ad=obj).exists()
class ListAdSerializer(BaseAdSerializer):
price = serializers.SerializerMethodField()
discount = serializers.SerializerMethodField()
class Meta(BaseAdSerializer.Meta):
fields = [
"id",
"name",
"price",
"image",
"star",
"comment_count",
"discount",
"is_liked",
]
def _get_first_variant(self, obj):
if not hasattr(self, "_variant_cache"):
self._variant_cache = {}
if obj.id not in self._variant_cache:
self._variant_cache[obj.id] = obj.variants.order_by("price").first()
return self._variant_cache[obj.id]
def get_price(self, obj):
variant = self._get_first_variant(obj)
if not variant:
return obj.price
return variant.price if variant else 0
def get_discount(self, obj):
variant = self._get_first_variant(obj)
return variant.discount if variant else -1.0
class FullListAdSerializer(serializers.Serializer):
ads = ListAdSerializer(many=True)
categories = serializers.SerializerMethodField()
colors = serializers.SerializerMethodField()
sizes = serializers.SerializerMethodField()
min_price = serializers.SerializerMethodField()
max_price = serializers.SerializerMethodField()
def get_categories(self, obj):
ads = obj.get("ads", [])
category_ids = set()
categories = []
for ad in ads:
category = ad.category
if category and category.id not in category_ids:
category_ids.add(category.id)
categories.append(category)
return CategorySerializer(categories, many=True).data
def get_colors(self, obj):
ads = obj.get("ads", [])
color_values = set()
for ad in ads:
variants = getattr(ad, "variants", [])
for v in variants.all():
if v.variant == AdVariantType.COLOR:
color_values.add(v.value)
return list(color_values)
def get_sizes(self, obj):
ads = obj.get("ads", [])
size_values = set()
for ad in ads:
variants = getattr(ad, "variants", [])
for v in variants.all():
if v.variant == AdVariantType.SIZE:
size_values.add(v.value)
return list(size_values)
def get_min_price(self, obj):
ads = obj.get("ads", [])
prices = []
for ad in ads:
ad_data = ListAdSerializer(ad, context=self.context).data
price = ad_data.get("price")
if price is not None:
prices.append(price)
return min(prices) if prices else None
def get_max_price(self, obj):
ads = obj.get("ads", [])
prices = []
for ad in ads:
ad_data = ListAdSerializer(ad, context=self.context).data
price = ad_data.get("price")
if price is not None:
prices.append(price)
return max(prices) if prices else None
class RetrieveAdSerializer(BaseAdSerializer):
class AdDetailSerializer(AdListSerializer):
variants = AdVariantSerializer(many=True, read_only=True)
images = serializers.SerializerMethodField()
images = AdImageSerializer(many=True, read_only=True)
colors = serializers.SerializerMethodField()
sizes = serializers.SerializerMethodField()
creator = serializers.SerializerMethodField()
options = AdOptionSerializer(many=True, read_only=True)
category = CategorySerializer(read_only=True)
class Meta(BaseAdSerializer.Meta):
fields = [
"id",
"name",
"price",
"image",
"star",
"comment_count",
"is_liked",
class Meta(AdListSerializer.Meta):
fields = AdListSerializer.Meta.fields + [
"description",
"images",
"variants",
"colors",
"sizes",
"creator",
"description",
"options"
"options",
"category",
"ad_type",
"physical_product",
]
def get_images(self, obj):
objects = obj.images.all()
return AdImageSerializer(objects, many=True, context=self.context).data
def get_colors(self, obj):
color_values = set()
variants = getattr(obj, "variants", [])
for v in variants.all():
if v.variant == AdVariantType.COLOR:
color_values.add(v.value)
return list(color_values)
"""Get unique colors from variants"""
return list(obj.variants.values_list("color", flat=True).distinct())
def get_sizes(self, obj):
size_values = set()
variants = getattr(obj, "variants", [])
for v in variants.all():
if v.variant == AdVariantType.SIZE:
size_values.add(v.value)
return list(size_values)
"""Get unique sizes from variants"""
return list(obj.variants.values_list("size", flat=True).distinct())
def get_creator(self, obj):
"""Get creator information"""
user = obj.user
user_type = user.account_type
request = self.context.get("request")
avatar_url = request.build_absolute_uri(user.avatar.url) if user.avatar else None
avatar_url = None
if user.avatar and request:
avatar_url = request.build_absolute_uri(user.avatar.url)
if user_type == AccountType.BUSINESS:
return {
"username": user.business.name,
"avatar": avatar_url,
"create_at": user.validated_at,
"last_live": "endi qo'shamiz! waiting pls ))"
}
if user.account_type == AccountType.BUSINESS:
username = user.business.name if hasattr(user, "business") else user.username
else:
username = f"{user.first_name} {user.last_name}"
return {
"username": username,
"avatar": avatar_url,
"create_at": user.validated_at,
"last_live": "endi qo'shamiz! waiting pls ))"
}
username = f"{user.first_name} {user.last_name}".strip() or user.username
return {
"id": user.id,
"username": username,
"avatar": avatar_url,
"account_type": user.account_type,
"joined_at": user.date_joined,
}
class CreateAdSerializer(BaseAdSerializer):
class Meta(BaseAdSerializer.Meta): ...
class AdCreateSerializer(serializers.ModelSerializer):
class Meta:
model = AdModel
fields = [
"name",
"ad_type",
"category",
"ad_category_type",
"discount",
"is_available",
"physical_product",
"plan",
"tags",
"image",
"description",
]
def create(self, validated_data):
validated_data["user"] = self.context["request"].user
return super().create(validated_data)

View File

@@ -1,26 +1,22 @@
from rest_framework import serializers
from django.db.models import Avg
from core.apps.api.models import AdModel, AdVariant
from core.apps.api.models import AdModel, AdVariantModel
from core.apps.accounts.models import UserLike
class AdVariantSerializer(serializers.ModelSerializer):
color_name = serializers.CharField(source="color.name", read_only=True)
class Meta:
model = AdVariant
fields = [
"variant",
"value",
"is_available",
"price",
"discount",
]
model = AdVariantModel
fields = ["id", "value", "color_name", "is_available", "price"]
read_only_fields = fields
class BaseHomeAdSerializer(serializers.ModelSerializer):
star = serializers.SerializerMethodField()
comment_count = serializers.SerializerMethodField()
price = serializers.SerializerMethodField()
discount = serializers.SerializerMethodField()
class HomeAdListSerializer(serializers.ModelSerializer):
"""Optimized serializer for home page ad listing"""
rating = serializers.SerializerMethodField()
reviews_count = serializers.SerializerMethodField()
is_liked = serializers.SerializerMethodField()
class Meta:
@@ -30,54 +26,25 @@ class BaseHomeAdSerializer(serializers.ModelSerializer):
"name",
"price",
"image",
"star",
"comment_count",
"rating",
"reviews_count",
"discount",
"is_liked",
]
read_only_fields = fields
def _get_first_variant(self, obj):
if not hasattr(self, "_variant_cache"):
self._variant_cache = {}
if obj.id not in self._variant_cache:
self._variant_cache[obj.id] = obj.variants.order_by("price").first()
return self._variant_cache[obj.id]
def get_rating(self, obj):
"""Get average rating"""
avg = obj.feedbacks.aggregate(avg=Avg("star"))["avg"]
return round(avg, 1) if avg else 0
def get_price(self, obj):
variant = self._get_first_variant(obj)
if not variant:
return obj.price
return variant.price if variant else 0
def get_discount(self, obj):
variant = self._get_first_variant(obj)
return variant.discount if variant else -1.0
def get_star(self, obj):
avg = obj.feedback.aggregate(avg=Avg("star"))["avg"]
return avg or 0
def get_comment_count(self, obj):
count = obj.feedback.count()
return count or 0
def get_reviews_count(self, obj):
"""Get feedback count"""
return obj.feedbacks.count()
def get_is_liked(self, obj):
"""Check if user liked this ad"""
request = self.context.get("request")
user = getattr(request, "user", None)
if not user or not user.is_authenticated:
if not request or not request.user.is_authenticated:
return False
return UserLike.objects.filter(user=user, ad=obj).exists()
class ListHomeAdSerializer(BaseHomeAdSerializer):
class Meta(BaseHomeAdSerializer.Meta): ...
class RetrieveHomeAdSerializer(BaseHomeAdSerializer):
class Meta(BaseHomeAdSerializer.Meta): ...
class CreateHomeAdSerializer(BaseHomeAdSerializer):
class Meta(BaseHomeAdSerializer.Meta): ...
return UserLike.objects.filter(user=request.user, ad=obj).exists()

View File

@@ -1 +1 @@
from .banner import * # noqa
from .banner import *

View File

@@ -1,11 +1,12 @@
from rest_framework import serializers
from core.apps.api.models import Banner
from core.apps.api.models import BannerModel
class BaseBannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
model = BannerModel
fields = [
"id",
"title",
"description",
"mobile_image",

View File

@@ -1 +1 @@
from .category import * # noqa
from .category import *

View File

@@ -1,13 +1,13 @@
from rest_framework import serializers
from core.apps.api.models import Category
from core.apps.api.models import CategoryModel
class BaseCategorySerializer(serializers.ModelSerializer):
children = serializers.SerializerMethodField()
class Meta:
model = Category
model = CategoryModel
fields = [
"id",
"name",

View File

@@ -0,0 +1,2 @@
from .color import * # noqa
from .size import * # noqa

View File

@@ -0,0 +1,29 @@
from rest_framework import serializers
from core.apps.api.models import ColorModel
class BaseColorSerializer(serializers.ModelSerializer):
class Meta:
model = ColorModel
fields = [
"id",
"name",
"color",
]
class ListColorSerializer(BaseColorSerializer):
class Meta(BaseColorSerializer.Meta): ...
class RetrieveColorSerializer(BaseColorSerializer):
class Meta(BaseColorSerializer.Meta): ...
class CreateColorSerializer(BaseColorSerializer):
class Meta(BaseColorSerializer.Meta):
fields = [
"id",
"name",
]

View File

@@ -0,0 +1,28 @@
from rest_framework import serializers
from core.apps.api.models import SizeModel
class BaseSizeSerializer(serializers.ModelSerializer):
class Meta:
model = SizeModel
fields = [
"id",
"name",
]
class ListSizeSerializer(BaseSizeSerializer):
class Meta(BaseSizeSerializer.Meta): ...
class RetrieveSizeSerializer(BaseSizeSerializer):
class Meta(BaseSizeSerializer.Meta): ...
class CreateSizeSerializer(BaseSizeSerializer):
class Meta(BaseSizeSerializer.Meta):
fields = [
"id",
"name",
]

View File

@@ -0,0 +1 @@
from .feedback import *

View File

@@ -0,0 +1,84 @@
from rest_framework import serializers
from django.db.models import Avg
from core.apps.api.models import FeedbackModel, FeedbackImageModel, AdModel
class FeedbackImageSerializer(serializers.ModelSerializer):
class Meta:
model = FeedbackImageModel
fields = ["id", "image"]
read_only_fields = ["id"]
class FeedbackListSerializer(serializers.ModelSerializer):
"""List serializer for feedback with user and ad info"""
user_name = serializers.CharField(source="user.username", read_only=True)
ad_name = serializers.CharField(source="ad.name", read_only=True)
images = FeedbackImageSerializer(many=True, read_only=True)
class Meta:
model = FeedbackModel
fields = [
"id",
"star",
"user",
"user_name",
"ad",
"ad_name",
"comment",
"images",
"created_at"
]
read_only_fields = ["id", "user", "user_name", "ad_name", "created_at"]
class FeedbackDetailSerializer(FeedbackListSerializer):
"""Detailed feedback serializer"""
class Meta(FeedbackListSerializer.Meta):
fields = FeedbackListSerializer.Meta.fields + ["updated_at"]
class FeedbackCreateSerializer(serializers.ModelSerializer):
"""Create feedback serializer"""
images = serializers.ListField(
child=serializers.ImageField(),
write_only=True,
required=False
)
class Meta:
model = FeedbackModel
fields = ["ad", "star", "comment", "images"]
def validate_star(self, value):
"""Validate star rating is between 1 and 5"""
if not 1 <= value <= 5:
raise serializers.ValidationError("Rating must be between 1 and 5")
return value
def create(self, validated_data):
images_data = validated_data.pop("images", [])
validated_data["user"] = self.context["request"].user
feedback = FeedbackModel.objects.create(**validated_data)
# Create feedback images
for image_data in images_data:
FeedbackImageModel.objects.create(
feedback=feedback,
image=image_data
)
return feedback
class FeedbackUpdateSerializer(serializers.ModelSerializer):
"""Update feedback serializer"""
class Meta:
model = FeedbackModel
fields = ["star", "comment"]
def validate_star(self, value):
if not 1 <= value <= 5:
raise serializers.ValidationError("Rating must be between 1 and 5")
return value

View File

@@ -1 +1 @@
from .natification import * # noqa
from .notification import *

View File

@@ -0,0 +1 @@
from .order import *

View File

@@ -0,0 +1,149 @@
from rest_framework import serializers
from core.apps.api.models import OrderModel, OrderItemModel, AdModel
from core.apps.accounts.models import Address
class OrderItemSerializer(serializers.ModelSerializer):
"""Order item serializer"""
ad_name = serializers.CharField(source="ad.name", read_only=True)
ad_image = serializers.ImageField(source="ad.image", read_only=True)
subtotal = serializers.DecimalField(
max_digits=10,
decimal_places=2,
read_only=True
)
class Meta:
model = OrderItemModel
fields = [
"id",
"ad",
"ad_name",
"ad_image",
"price",
"quantity",
"subtotal"
]
read_only_fields = ["id", "ad_name", "ad_image", "subtotal"]
class OrderListSerializer(serializers.ModelSerializer):
"""List serializer for orders"""
items_count = serializers.SerializerMethodField()
status_display = serializers.CharField(source="get_status_display", read_only=True)
class Meta:
model = OrderModel
fields = [
"id",
"status",
"status_display",
"total_amount",
"items_count",
"created_at"
]
read_only_fields = fields
def get_items_count(self, obj):
return obj.items.count()
class OrderDetailSerializer(serializers.ModelSerializer):
"""Detailed order serializer"""
items = OrderItemSerializer(many=True, read_only=True)
user_name = serializers.CharField(source="user.username", read_only=True)
address_details = serializers.SerializerMethodField()
status_display = serializers.CharField(source="get_status_display", read_only=True)
class Meta:
model = OrderModel
fields = [
"id",
"user",
"user_name",
"status",
"status_display",
"address",
"address_details",
"total_amount",
"items",
"created_at",
"updated_at"
]
read_only_fields = [
"id", "user", "user_name", "status_display",
"total_amount", "created_at", "updated_at"
]
def get_address_details(self, obj):
if obj.address:
return {
"id": obj.address.id,
"street": obj.address.street,
"city": obj.address.city,
"country": obj.address.country,
}
return None
class OrderItemCreateSerializer(serializers.Serializer):
"""Serializer for creating order items"""
ad = serializers.PrimaryKeyRelatedField(queryset=AdModel.objects.all())
quantity = serializers.IntegerField(min_value=1, default=1)
class OrderCreateSerializer(serializers.ModelSerializer):
"""Create order serializer"""
items = OrderItemCreateSerializer(many=True, write_only=True)
class Meta:
model = OrderModel
fields = ["address", "items"]
def validate_items(self, value):
if not value:
raise serializers.ValidationError("Order must have at least one item")
return value
def create(self, validated_data):
items_data = validated_data.pop("items")
validated_data["user"] = self.context["request"].user
# Create order
order = OrderModel.objects.create(**validated_data)
# Create order items and calculate total
total = 0
for item_data in items_data:
ad = item_data["ad"]
quantity = item_data.get("quantity", 1)
price = ad.price
OrderItemModel.objects.create(
order=order,
ad=ad,
price=price,
quantity=quantity
)
total += price * quantity
# Update order total
order.total_amount = total
order.save()
return order
class OrderUpdateSerializer(serializers.ModelSerializer):
"""Update order serializer"""
class Meta:
model = OrderModel
fields = ["status", "address"]
def validate_status(self, value):
# Add business logic for status transitions
current_status = self.instance.status
# Example: Can't change completed orders
if current_status == "completed":
raise serializers.ValidationError("Cannot modify completed orders")
return value

View File

@@ -1,2 +1,2 @@
from .search import * # noqa
from .search_ads import * # noqa
from .history import *
from .ad import *

View File

@@ -0,0 +1 @@
from .tags import *

View File

@@ -0,0 +1,43 @@
from rest_framework import serializers
from django.utils.text import slugify
from core.apps.api.models import TagsModel, ColorModel
class TagSerializer(serializers.ModelSerializer):
"""Serializer for tags"""
ads_count = serializers.SerializerMethodField()
class Meta:
model = TagsModel
fields = ["id", "name", "slug", "ads_count"]
read_only_fields = ["id", "slug", "ads_count"]
def get_ads_count(self, obj):
"""Get count of ads using this tag"""
return obj.admodel_set.count()
def create(self, validated_data):
# Auto-generate slug from name
if "slug" not in validated_data:
validated_data["slug"] = slugify(validated_data["name"])
return super().create(validated_data)
def update(self, instance, validated_data):
# Update slug if name changes
if "name" in validated_data:
validated_data["slug"] = slugify(validated_data["name"])
return super().update(instance, validated_data)
class ColorSerializer(serializers.ModelSerializer):
"""Serializer for colors"""
variants_count = serializers.SerializerMethodField()
class Meta:
model = ColorModel
fields = ["id", "name", "variants_count"]
read_only_fields = ["id", "variants_count"]
def get_variants_count(self, obj):
"""Get count of variants using this color"""
return obj.advariantmodel_set.count()

View File

@@ -1 +1 @@
from .ad_like import * # noqa
from .like import *

View File

@@ -1,13 +1,13 @@
from rest_framework import serializers
from core.apps.accounts.models import UserLike
from core.apps.api.models import AdModel
from core.apps.api.serializers.ad.home_api import ListHomeAdSerializer
from core.apps.api.serializers.ad.home_api import HomeAdListSerializer
from rest_framework.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
class BaseUserLikeSerializer(serializers.ModelSerializer):
ad = ListHomeAdSerializer(many=False, read_only=True)
ad = HomeAdListSerializer(many=False, read_only=True)
class Meta:
model = UserLike
@@ -28,18 +28,7 @@ class RetrieveUserLikeSerializer(BaseUserLikeSerializer):
class CreateUserLikeSerializer(BaseUserLikeSerializer):
ad = serializers.PrimaryKeyRelatedField(queryset=AdModel.objects.all())
class Meta(BaseUserLikeSerializer.Meta): ...
def validate(self, data):
user = self.context["request"].user
ad = data["ad"]
if UserLike.objects.filter(user=user, ad=ad).exists():
raise ValidationError({"detail": _("Siz bu elonga allaqachon like bosgansiz.")})
return data
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
like = UserLike.objects.create(**validated_data)
return like
class Meta(BaseUserLikeSerializer.Meta):
fields = [
"ad",
]