Home api lari chiqarildi #3

Merged
admin merged 1 commits from feat/home_api into main 2025-11-25 11:41:25 +00:00
30 changed files with 355 additions and 13 deletions

View File

@@ -1 +1,4 @@
from .category import * # noqa from .category import * # noqa
from .ad import * # noqa
from .ad_items import * # noqa
from .feedback import * # noqa

View File

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

View File

@@ -0,0 +1,23 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.api.models import AdModel, AdImage, AdVariant
class AdImageInline(admin.TabularInline):
model = AdImage
extra = 1
class AdVariantInline(admin.TabularInline):
model = AdVariant
extra = 1
@admin.register(AdModel)
class AdModelAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
)
inlines = [AdImageInline, AdVariantInline]

View File

@@ -0,0 +1,4 @@
from .tags import * # noqa
from .ad_top_plan import * # noqa
from .ad_images import * # noqa
from .ad_variant import * # noqa

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.api.models import Feedback, FeedbackImages
@admin.register(Feedback)
class FeedbackAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
)
@admin.register(FeedbackImages)
class FeedbackImagesAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
)

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-11-25 10:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0004_category_category_type'),
]
operations = [
migrations.AddField(
model_name='admodel',
name='star',
field=models.FloatField(default=0.0, verbose_name='Star'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.7 on 2025-11-25 10:30
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0005_admodel_star'),
]
operations = [
migrations.AlterField(
model_name='adimage',
name='ad',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='api.admodel', verbose_name='Ad'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.7 on 2025-11-25 10:35
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0006_alter_adimage_ad'),
]
operations = [
migrations.AlterField(
model_name='advariant',
name='ad',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variants', to='api.admodel'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.7 on 2025-11-25 10:43
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0007_alter_advariant_ad'),
]
operations = [
migrations.AddField(
model_name='adimage',
name='ad_variant',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='api.advariant', verbose_name='Ad Variant'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.7 on 2025-11-25 10:45
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0008_adimage_ad_variant'),
]
operations = [
migrations.AlterField(
model_name='adimage',
name='ad_variant',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='api.advariant', verbose_name='Ad Variant'),
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.7 on 2025-11-25 11:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0009_alter_adimage_ad_variant'),
]
operations = [
migrations.RemoveField(
model_name='admodel',
name='star',
),
migrations.AddField(
model_name='admodel',
name='image',
field=models.ImageField(default=1, upload_to='', verbose_name='Image'),
preserve_default=False,
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.2.7 on 2025-11-25 11:23
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0010_remove_admodel_star_admodel_image'),
]
operations = [
migrations.AlterField(
model_name='feedback',
name='ad',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='feedback', to='api.admodel', verbose_name='Ad'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2025-11-25 11:38
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('api', '0011_alter_feedback_ad'),
]
operations = [
migrations.RenameField(
model_name='feedback',
old_name='command',
new_name='comment',
),
]

View File

@@ -16,6 +16,7 @@ class AdModel(AbstractBaseModel):
physical_product = models.BooleanField(verbose_name=_("Physical product"), default=False) physical_product = models.BooleanField(verbose_name=_("Physical product"), default=False)
plan = models.ForeignKey("api.AdTopPlan", on_delete=models.CASCADE, verbose_name=_("Plan")) plan = models.ForeignKey("api.AdTopPlan", on_delete=models.CASCADE, verbose_name=_("Plan"))
tags = models.ManyToManyField("api.Tags", verbose_name=_("Tags")) tags = models.ManyToManyField("api.Tags", verbose_name=_("Tags"))
image = models.ImageField(verbose_name=_("Image"))
def __str__(self): def __str__(self):
return str(self.pk) return str(self.pk)

View File

@@ -13,13 +13,6 @@ class Category(AbstractBaseModel):
category_type = models.CharField(max_length=255, verbose_name=_('Category Type'), choices=AdCategoryType, category_type = models.CharField(max_length=255, verbose_name=_('Category Type'), choices=AdCategoryType,
default=AdCategoryType.PRODUCT) default=AdCategoryType.PRODUCT)
def save(self, *args, **kwargs):
if self.parent:
self.level = self.parent.level + 1
else:
self.level = 0
super().save(*args, **kwargs)
def __str__(self): def __str__(self):
return str(self.pk) return str(self.pk)

View File

@@ -1,12 +1,17 @@
from django.db import models from django.db import models
from django_core.models.base import AbstractBaseModel from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.apps.api.models.ad.ad import AdModel
from core.apps.api.models import AdModel
class AdImage(AbstractBaseModel): class AdImage(AbstractBaseModel):
image = models.ImageField(verbose_name=_("Image"), upload_to="ads/images/") image = models.ImageField(verbose_name=_("Image"), upload_to="ads/images/")
ad = models.ForeignKey(AdModel, verbose_name=_("Ad"), on_delete=models.CASCADE) ad = models.ForeignKey(AdModel, verbose_name=_("Ad"), related_name="images",
on_delete=models.CASCADE)
ad_variant = models.ForeignKey("api.AdVariant", verbose_name=_("Ad Variant"), null=True, blank=True,
related_name="images",
on_delete=models.CASCADE)
def __str__(self): def __str__(self):
return str(self.pk) return str(self.pk)

View File

@@ -6,7 +6,7 @@ from core.apps.api.choices.ad_variant_type import AdVariantType
class AdVariant(AbstractBaseModel): class AdVariant(AbstractBaseModel):
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE) ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, related_name="variants")
variant = models.CharField(max_length=255, choices=AdVariantType, db_index=True) variant = models.CharField(max_length=255, choices=AdVariantType, db_index=True)
value = models.CharField(max_length=255) value = models.CharField(max_length=255)
is_available = models.CharField(max_length=255) is_available = models.CharField(max_length=255)

View File

@@ -8,8 +8,8 @@ from core.apps.api.models.ad import AdModel
class Feedback(AbstractBaseModel): class Feedback(AbstractBaseModel):
star = models.IntegerField(default=0, verbose_name=_("Star")) star = models.IntegerField(default=0, verbose_name=_("Star"))
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User")) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User"))
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, verbose_name=_("Ad")) ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, verbose_name=_("Ad"), related_name="feedback")
command = models.CharField(max_length=255, verbose_name=_("Command")) comment = models.CharField(max_length=255, verbose_name=_("Comment"))
def __str__(self): def __str__(self):
return str(self.pk) return str(self.pk)

View File

@@ -1,2 +1,3 @@
from .category import * # noqa from .category import * # noqa
from .search import * # noqa from .search import * # noqa
from .ad import * # noqa

View File

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

View File

@@ -0,0 +1,59 @@
from rest_framework import serializers
from django.db.models import Avg
from core.apps.api.models import AdModel, AdVariant
class AdVariantSerializer(serializers.ModelSerializer):
class Meta:
model = AdVariant
fields = [
"variant",
"value",
"is_available",
"price",
"discount",
]
class BaseHomeAdSerializer(serializers.ModelSerializer):
variants = serializers.SerializerMethodField()
star = serializers.SerializerMethodField()
comment_count = serializers.SerializerMethodField()
class Meta:
model = AdModel
fields = [
"id",
"name",
"price",
"image",
"star",
"comment_count",
"variants",
]
def get_variants(self, obj):
variant = obj.variants.order_by("price").first()
if variant:
return AdVariantSerializer(variant).data
return []
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
class ListHomeAdSerializer(BaseHomeAdSerializer):
class Meta(BaseHomeAdSerializer.Meta): ...
class RetrieveHomeAdSerializer(BaseHomeAdSerializer):
class Meta(BaseHomeAdSerializer.Meta): ...
class CreateHomeAdSerializer(BaseHomeAdSerializer):
class Meta(BaseHomeAdSerializer.Meta): ...

View File

@@ -1,9 +1,10 @@
from django.urls import include, path from django.urls import include, path
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from core.apps.api.views import CategoryViewSet, SearchHistoryViewSet from core.apps.api.views import CategoryViewSet, SearchHistoryViewSet, HomeAdApiView
router = DefaultRouter() router = DefaultRouter()
router.register("category", CategoryViewSet, basename="category") router.register("category", CategoryViewSet, basename="category")
router.register("search-history", SearchHistoryViewSet, basename="search-history") router.register("search-history", SearchHistoryViewSet, basename="search-history")
router.register("home-ad", HomeAdApiView, basename="home-ad")
urlpatterns = [path("", include(router.urls))] urlpatterns = [path("", include(router.urls))]

View File

@@ -1,2 +1,4 @@
from .category import * # noqa from .category import * # noqa
from .search import * # noqa from .search import * # noqa
from .ad import * # noqa

View File

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

View File

@@ -0,0 +1,24 @@
from rest_framework.viewsets import ReadOnlyModelViewSet
from rest_framework.permissions import AllowAny
from drf_spectacular.utils import extend_schema
from django_core.mixins import BaseViewSetMixin
from core.apps.api.models import AdModel
from core.apps.api.serializers.ad.home_api import (
ListHomeAdSerializer,
CreateHomeAdSerializer,
RetrieveHomeAdSerializer,
)
@extend_schema(tags=["Home Ad Api"])
class HomeAdApiView(BaseViewSetMixin, ReadOnlyModelViewSet):
queryset = AdModel.objects.all()
serializer_class = ListHomeAdSerializer
permission_classes = [AllowAny]
action_permission_classes = {}
action_serializer_class = {
"list": ListHomeAdSerializer,
"retrieve": RetrieveHomeAdSerializer,
"create": CreateHomeAdSerializer,
}