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,2 +1,6 @@
from .ad import * # noqa
from .category import * # noqa
from .ad import *
from .category import *
from .variant import *
from .image import *
from .option import *
from .size import *

View File

@@ -1,45 +1,43 @@
# type: ignore
from django.db import models
from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model
from core.apps.api.choices.ad_type import AdType, AdCategoryType
from model_bakery import baker
class AdModel(AbstractBaseModel):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User"), related_name="ad")
name = models.CharField(verbose_name=_("Name"), max_length=255)
ad_type = models.CharField(verbose_name=_("Type"), max_length=255, choices=AdType)
category = models.ForeignKey("api.Category", on_delete=models.CASCADE, verbose_name=_("Category"))
ad_category_type = models.CharField(verbose_name=_("Type"), max_length=255, choices=AdCategoryType)
price = models.DecimalField(verbose_name=_("Price"), max_digits=10, decimal_places=2, null=True, blank=True)
is_available = models.BooleanField(verbose_name=_("Is available"), default=True, blank=True, null=True)
physical_product = models.BooleanField(verbose_name=_("Physical product"), default=False)
plan = models.ForeignKey("api.AdTopPlan", on_delete=models.CASCADE, verbose_name=_("Plan"))
tags = models.ManyToManyField("api.Tags", verbose_name=_("Tags"))
image = models.ImageField(verbose_name=_("Image"))
description = models.TextField(verbose_name=_("Description"))
@classmethod
def _baker(cls):
return baker.make(cls)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User"), related_name="ads")
name = models.CharField(_("Name"), max_length=255)
ad_type = models.CharField(_("Type"), max_length=255, choices=AdType)
category = models.ForeignKey("api.CategoryModel", on_delete=models.CASCADE, verbose_name=_("Category"))
ad_category_type = models.CharField(_("Category Type"), max_length=255, choices=AdCategoryType)
_price = models.DecimalField(_("Price"), max_digits=10, decimal_places=2, null=True, blank=True, db_column="price")
discount = models.DecimalField(_("Discount"), max_digits=10, decimal_places=2, default=-1)
is_available = models.BooleanField(_("Is available"), default=True)
physical_product = models.BooleanField(_("Physical product"), default=False)
plan = models.ForeignKey("api.AdTopPlanModel", on_delete=models.CASCADE, verbose_name=_("Plan"))
tags = models.ManyToManyField("api.TagsModel", verbose_name=_("Tags"), blank=True)
image = models.ImageField(_("Image"), upload_to="ads/")
description = models.TextField(_("Description"))
def __str__(self):
return str(self.pk)
return self.name
@property
def price(self):
"""Get actual price - either from variant or direct price"""
if self.ad_category_type == AdCategoryType.PRODUCT.value:
variant = self.variants.order_by("price").first()
return variant.price if variant else 0
return self._price
class Meta:
db_table = "ad"
verbose_name = _("Ad")
verbose_name_plural = _("Ads")
class Color(AbstractBaseModel):
name = models.CharField(verbose_name=_("Name"), max_length=255)
def __str__(self):
return str(self.pk)
class Meta:
db_table = "color"
verbose_name = _("Color")
verbose_name_plural = _("Colors")
ordering = ["-created_at"]
indexes = [
models.Index(fields=["-created_at"]),
models.Index(fields=["category", "is_available"]),
]

View File

@@ -2,26 +2,45 @@ from django.db import models
from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
from core.apps.api.choices import AdCategoryType
from model_bakery import baker
class Category(AbstractBaseModel):
name = models.CharField(max_length=255, verbose_name=_('Category Name'))
parent = models.ForeignKey('self', null=True, blank=True, related_name='children', on_delete=models.CASCADE)
show_home = models.BooleanField(default=False, verbose_name=_('Show Home'))
level = models.IntegerField(default=0, verbose_name=_('Level'))
image = models.ImageField(verbose_name=_('Image'), null=True, blank=True)
category_type = models.CharField(max_length=255, verbose_name=_('Category Type'), choices=AdCategoryType,
default=AdCategoryType.PRODUCT)
@classmethod
def _baker(cls):
return baker.make(cls)
class CategoryModel(AbstractBaseModel):
name = models.CharField(_("Category Name"), max_length=255)
parent = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="children",
on_delete=models.CASCADE,
verbose_name=_("Parent Category")
)
show_home = models.BooleanField(_("Show on Home"), default=False)
level = models.IntegerField(_("Level"), default=0, editable=False)
image = models.ImageField(_("Image"), upload_to="categories/", null=True, blank=True)
category_type = models.CharField(
_("Category Type"),
max_length=255,
choices=AdCategoryType,
default=AdCategoryType.PRODUCT
)
def __str__(self):
return str(self.pk)
return self.name
def save(self, *args, **kwargs):
"""Auto-calculate level based on parent"""
if self.parent:
self.level = self.parent.level + 1
else:
self.level = 0
super().save(*args, **kwargs)
class Meta:
db_table = 'category'
verbose_name = _('Category')
verbose_name_plural = _('Categories')
db_table = "category"
verbose_name = _("Category")
verbose_name_plural = _("Categories")
ordering = ["level", "name"]
indexes = [
models.Index(fields=["parent", "show_home"]),
models.Index(fields=["level"]),
]

View File

@@ -0,0 +1,35 @@
from django.db import models
from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
class AdImageModel(AbstractBaseModel):
ad = models.ForeignKey(
"api.AdModel",
on_delete=models.CASCADE,
verbose_name=_("Ad"),
related_name="images"
)
ad_variant = models.ForeignKey(
"api.AdVariantModel",
on_delete=models.CASCADE,
verbose_name=_("Ad Variant"),
related_name="images",
null=True,
blank=True
)
image = models.ImageField(_("Image"), upload_to="ads/images/")
order = models.PositiveIntegerField(_("Display Order"), default=0)
is_primary = models.BooleanField(_("Is Primary"), default=False)
def __str__(self):
return f"Image for {self.ad.name}"
class Meta:
db_table = "ad_images"
verbose_name = _("Ad Image")
verbose_name_plural = _("Ad Images")
ordering = ["order", "-created_at"]
indexes = [
models.Index(fields=["ad", "is_primary"]),
]

View File

@@ -0,0 +1,23 @@
from django.db import models
from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
class AdOptionModel(AbstractBaseModel):
ad = models.ForeignKey(
"api.AdModel",
on_delete=models.CASCADE,
related_name="options",
verbose_name=_("Ad")
)
name = models.CharField(_("Name"), max_length=255)
value = models.CharField(_("Value"), max_length=255)
def __str__(self):
return f"{self.name}: {self.value}"
class Meta:
db_table = "ad_option"
verbose_name = _("Ad Option")
verbose_name_plural = _("Ad Options")
unique_together = [["ad", "name"]]

View File

@@ -0,0 +1,24 @@
from django.db import models
from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
class AdSizeModel(AbstractBaseModel):
ad = models.ForeignKey(
"api.AdModel",
on_delete=models.CASCADE,
related_name="size_info",
verbose_name=_("Ad")
)
weight = models.PositiveIntegerField(_("Weight (g)"))
width = models.PositiveIntegerField(_("Width (cm)"))
height = models.PositiveIntegerField(_("Height (cm)"))
length = models.PositiveIntegerField(_("Length (cm)"))
def __str__(self):
return f"{self.width}x{self.height}x{self.length}cm, {self.weight}g"
class Meta:
db_table = "ad_size"
verbose_name = _("Ad Size")
verbose_name_plural = _("Ad Sizes")

View File

@@ -0,0 +1,26 @@
from django.db import models
from django_core.models.base import AbstractBaseModel
from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator
class AdVariantModel(AbstractBaseModel):
ad = models.ForeignKey("api.AdModel", on_delete=models.CASCADE, related_name="variants", verbose_name=_("Ad"))
color = models.ForeignKey(
"api.ColorModel", on_delete=models.CASCADE, verbose_name=_("Color"), null=True, blank=False
)
size = models.ForeignKey("api.SizeModel", on_delete=models.CASCADE, verbose_name=_("Size"), null=True, blank=False)
is_available = models.BooleanField(_("Is Available"), default=True)
price = models.DecimalField(_("Price"), max_digits=10, decimal_places=2, validators=[MinValueValidator(0)])
stock_quantity = models.PositiveIntegerField(_("Stock Quantity"), default=0)
def __str__(self):
return f"{self.color} - {self.size}"
class Meta:
db_table = "ad_variant"
verbose_name = _("Ad Variant")
verbose_name_plural = _("Ad Variants")
indexes = [
models.Index(fields=["ad", "is_available"]),
]