update
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from .banner import * # noqa
|
||||
from .feedback import * # noqa
|
||||
from .ad import * # noqa
|
||||
from .ad_items import * # noqa
|
||||
from .order import * # noqa
|
||||
from .banner import *
|
||||
from .feedback import *
|
||||
from .ad import *
|
||||
from .common import *
|
||||
from .order import *
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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"]),
|
||||
]
|
||||
|
||||
@@ -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"]),
|
||||
]
|
||||
|
||||
35
core/apps/api/models/ad/image.py
Normal file
35
core/apps/api/models/ad/image.py
Normal 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"]),
|
||||
]
|
||||
23
core/apps/api/models/ad/option.py
Normal file
23
core/apps/api/models/ad/option.py
Normal 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"]]
|
||||
24
core/apps/api/models/ad/size.py
Normal file
24
core/apps/api/models/ad/size.py
Normal 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")
|
||||
26
core/apps/api/models/ad/variant.py
Normal file
26
core/apps/api/models/ad/variant.py
Normal 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"]),
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
from .ad_top_plan import * # noqa
|
||||
from .tags import * # noqa
|
||||
from .ad_images import * # noqa
|
||||
from .ad_option import * # noqa
|
||||
from .ad_size import * # noqa
|
||||
from .ad_variant import * # noqa
|
||||
@@ -1,22 +0,0 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.apps.api.models import AdModel
|
||||
|
||||
|
||||
class AdImage(AbstractBaseModel):
|
||||
image = models.ImageField(verbose_name=_("Image"), upload_to="ads/images/")
|
||||
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):
|
||||
return str(self.pk)
|
||||
|
||||
class Meta:
|
||||
db_table = "ad_images"
|
||||
verbose_name = _("Ad_Image")
|
||||
verbose_name_plural = _("Ad_Images")
|
||||
@@ -1,18 +0,0 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from core.apps.api.models import AdModel
|
||||
|
||||
|
||||
class AdOption(AbstractBaseModel):
|
||||
name = models.CharField(_("Name"), max_length=255)
|
||||
value = models.CharField(_("Value"), max_length=255)
|
||||
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, related_name="options", verbose_name=_("Ad"))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
|
||||
class Meta:
|
||||
db_table = "ad_option"
|
||||
verbose_name = _("Ad_Option")
|
||||
verbose_name_plural = _("Ad_Options")
|
||||
@@ -1,20 +0,0 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from core.apps.api.models import AdModel
|
||||
|
||||
|
||||
class AdSize(AbstractBaseModel):
|
||||
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE)
|
||||
weight = models.PositiveIntegerField(verbose_name=_("Weight"))
|
||||
width = models.PositiveIntegerField(verbose_name=_("Width"))
|
||||
height = models.PositiveIntegerField(verbose_name=_("Height"))
|
||||
length = models.PositiveIntegerField(verbose_name=_("Length"))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
|
||||
class Meta:
|
||||
db_table = "ad_size"
|
||||
verbose_name = _("AdSize")
|
||||
verbose_name_plural = _("AdSizes")
|
||||
@@ -1,16 +0,0 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class AdTopPlan(AbstractBaseModel):
|
||||
name = models.CharField(verbose_name=_('Name'), max_length=255)
|
||||
price = models.DecimalField(verbose_name=_('Price'), max_digits=10, decimal_places=2)
|
||||
duration = models.IntegerField(verbose_name=_('Duration'))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
class Meta:
|
||||
db_table = 'ad_top_plan'
|
||||
verbose_name = _('AdTop Plan')
|
||||
verbose_name_plural = _('AdTop Plan')
|
||||
@@ -1,22 +0,0 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from core.apps.api.models import AdModel
|
||||
from core.apps.api.choices.ad_variant_type import AdVariantType
|
||||
|
||||
|
||||
class AdVariant(AbstractBaseModel):
|
||||
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, related_name="variants")
|
||||
variant = models.CharField(max_length=255, choices=AdVariantType, db_index=True)
|
||||
value = models.CharField(max_length=255)
|
||||
is_available = models.CharField(max_length=255)
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2)
|
||||
discount = models.DecimalField(max_digits=10, decimal_places=2, default=-1)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
|
||||
class Meta:
|
||||
db_table = "ad_variant"
|
||||
verbose_name = _("Ad_Variant")
|
||||
verbose_name_plural = _("Ad_Variants")
|
||||
@@ -1,15 +0,0 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class Tags(AbstractBaseModel):
|
||||
name = models.CharField(verbose_name=_("Name"), max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
|
||||
class Meta:
|
||||
db_table = 'tags'
|
||||
verbose_name = _("Tags")
|
||||
verbose_name_plural = _("Tags")
|
||||
@@ -1 +1 @@
|
||||
from .banner import * # noqa
|
||||
from .banner import *
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from model_bakery import baker
|
||||
|
||||
|
||||
class Banner(AbstractBaseModel):
|
||||
title = models.CharField(max_length=255, verbose_name=_("Title"))
|
||||
description = models.TextField(verbose_name=_("Description"))
|
||||
mobile_image = models.ImageField(verbose_name=_("Mobile Image"), upload_to="banner/mobile_image/")
|
||||
desktop_image = models.ImageField(verbose_name=_("Desktop Image"), upload_to="banner/desktop_image/")
|
||||
link = models.URLField(verbose_name=_("Link"))
|
||||
bg_color = models.CharField(verbose_name=_("BG Color"), max_length=255)
|
||||
text_color = models.CharField(verbose_name=_("Text Color"), max_length=255)
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
return baker.make(cls)
|
||||
class BannerModel(AbstractBaseModel):
|
||||
title = models.CharField(_("Title"), max_length=255)
|
||||
description = models.TextField(_("Description"))
|
||||
mobile_image = models.ImageField(
|
||||
_("Mobile Image"),
|
||||
upload_to="banners/mobile/"
|
||||
)
|
||||
desktop_image = models.ImageField(
|
||||
_("Desktop Image"),
|
||||
upload_to="banners/desktop/"
|
||||
)
|
||||
link = models.URLField(_("Link"))
|
||||
bg_color = models.CharField(_("Background Color"), max_length=7, default="#FFFFFF")
|
||||
text_color = models.CharField(_("Text Color"), max_length=7, default="#000000")
|
||||
is_active = models.BooleanField(_("Is Active"), default=True)
|
||||
order = models.PositiveIntegerField(_("Display Order"), default=0)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
return self.title
|
||||
|
||||
class Meta:
|
||||
db_table = "banner"
|
||||
verbose_name = _("Banner")
|
||||
verbose_name_plural = _("Banners")
|
||||
ordering = ["order", "-created_at"]
|
||||
|
||||
4
core/apps/api/models/common/__init__.py
Normal file
4
core/apps/api/models/common/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .tags import *
|
||||
from .plan import *
|
||||
from .size import * # noqa
|
||||
from .color import * # noqa
|
||||
17
core/apps/api/models/common/color.py
Normal file
17
core/apps/api/models/common/color.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class ColorModel(AbstractBaseModel):
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
color = models.CharField(_("Color"), max_length=255, null=True, blank=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
db_table = "color"
|
||||
verbose_name = _("Color")
|
||||
verbose_name_plural = _("Colors")
|
||||
ordering = ["name"]
|
||||
26
core/apps/api/models/common/plan.py
Normal file
26
core/apps/api/models/common/plan.py
Normal 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 AdTopPlanModel(AbstractBaseModel):
|
||||
name = models.CharField(_("Plan Name"), max_length=255, unique=True)
|
||||
price = models.DecimalField(
|
||||
_("Price"),
|
||||
max_digits=10,
|
||||
decimal_places=2,
|
||||
validators=[MinValueValidator(0)]
|
||||
)
|
||||
duration = models.PositiveIntegerField(_("Duration (days)"))
|
||||
description = models.TextField(_("Description"), blank=True)
|
||||
is_active = models.BooleanField(_("Is Active"), default=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} - {self.duration} days"
|
||||
|
||||
class Meta:
|
||||
db_table = "ad_top_plan"
|
||||
verbose_name = _("Ad Top Plan")
|
||||
verbose_name_plural = _("Ad Top Plans")
|
||||
ordering = ["price"]
|
||||
16
core/apps/api/models/common/size.py
Normal file
16
core/apps/api/models/common/size.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class SizeModel(AbstractBaseModel):
|
||||
name = models.CharField(_("Name"), max_length=100, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
db_table = "size"
|
||||
verbose_name = _("Size")
|
||||
verbose_name_plural = _("Sizes")
|
||||
ordering = ["name"]
|
||||
17
core/apps/api/models/common/tags.py
Normal file
17
core/apps/api/models/common/tags.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.db import models
|
||||
from django_core.models.base import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class TagsModel(AbstractBaseModel):
|
||||
name = models.CharField(_("Tag Name"), max_length=255, unique=True)
|
||||
slug = models.SlugField(_("Slug"), max_length=255, unique=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
db_table = "tags"
|
||||
verbose_name = _("Tag")
|
||||
verbose_name_plural = _("Tags")
|
||||
ordering = ["name"]
|
||||
@@ -1 +1 @@
|
||||
from .feedback import * # noqa
|
||||
from .feedback import *
|
||||
|
||||
@@ -2,33 +2,57 @@ 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.models.ad import AdModel
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
|
||||
class Feedback(AbstractBaseModel):
|
||||
star = models.IntegerField(default=0, verbose_name=_("Star"))
|
||||
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User"))
|
||||
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, verbose_name=_("Ad"), related_name="feedback")
|
||||
comment = models.CharField(max_length=255, verbose_name=_("Comment"))
|
||||
class FeedbackModel(AbstractBaseModel):
|
||||
star = models.IntegerField(
|
||||
_("Rating"),
|
||||
default=5,
|
||||
validators=[MinValueValidator(1), MaxValueValidator(5)]
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
get_user_model(),
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("User"),
|
||||
related_name="feedbacks"
|
||||
)
|
||||
ad = models.ForeignKey(
|
||||
"api.AdModel",
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("Ad"),
|
||||
related_name="feedbacks"
|
||||
)
|
||||
comment = models.TextField(_("Comment"), max_length=1000)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
return f"{self.user.username} - {self.ad.name} ({self.star}★)"
|
||||
|
||||
class Meta:
|
||||
db_table = "feedback"
|
||||
verbose_name = _("Feedback")
|
||||
verbose_name_plural = _("Feedbacks")
|
||||
ordering = ["-created_at"]
|
||||
unique_together = [["user", "ad"]]
|
||||
indexes = [
|
||||
models.Index(fields=["ad", "-created_at"]),
|
||||
models.Index(fields=["user"]),
|
||||
]
|
||||
|
||||
|
||||
class FeedbackImages(AbstractBaseModel):
|
||||
feedback = models.ForeignKey(Feedback, on_delete=models.CASCADE, verbose_name=_("Feedback"))
|
||||
image = models.ImageField(verbose_name=_("Image"), upload_to="feedback/"
|
||||
"images/")
|
||||
class FeedbackImageModel(AbstractBaseModel):
|
||||
feedback = models.ForeignKey(
|
||||
FeedbackModel,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("Feedback"),
|
||||
related_name="images"
|
||||
)
|
||||
image = models.ImageField(_("Image"), upload_to="feedbacks/")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
return f"Image for {self.feedback}"
|
||||
|
||||
class Meta:
|
||||
db_table = "feedback_images"
|
||||
verbose_name = _("Feedback Images")
|
||||
verbose_name = _("Feedback Image")
|
||||
verbose_name_plural = _("Feedback Images")
|
||||
|
||||
@@ -1 +1 @@
|
||||
from .order import * # noqa
|
||||
from .order import *
|
||||
|
||||
@@ -4,33 +4,82 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from core.apps.api.choices import OrderStatus
|
||||
from core.apps.accounts.models import Address
|
||||
from core.apps.api.models import AdModel
|
||||
|
||||
|
||||
class Order(AbstractBaseModel):
|
||||
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User"))
|
||||
status = models.CharField(max_length=255, choices=OrderStatus)
|
||||
address = models.ForeignKey(Address, on_delete=models.CASCADE, verbose_name=_("Address"))
|
||||
class OrderModel(AbstractBaseModel):
|
||||
user = models.ForeignKey(
|
||||
get_user_model(),
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("User"),
|
||||
related_name="orders"
|
||||
)
|
||||
status = models.CharField(
|
||||
_("Status"),
|
||||
max_length=255,
|
||||
choices=OrderStatus,
|
||||
db_index=True
|
||||
)
|
||||
address = models.ForeignKey(
|
||||
Address,
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_("Address")
|
||||
)
|
||||
total_amount = models.DecimalField(
|
||||
_("Total Amount"),
|
||||
max_digits=10,
|
||||
decimal_places=2,
|
||||
default=0
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
return f"Order #{self.pk} - {self.user.username}"
|
||||
|
||||
def calculate_total(self):
|
||||
"""Calculate total from order items"""
|
||||
total = sum(item.subtotal for item in self.items.all())
|
||||
self.total_amount = total
|
||||
return total
|
||||
|
||||
class Meta:
|
||||
db_table = "order"
|
||||
verbose_name = _("Order")
|
||||
verbose_name_plural = _("Orders")
|
||||
ordering = ["-created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=["user", "-created_at"]),
|
||||
models.Index(fields=["status"]),
|
||||
]
|
||||
|
||||
|
||||
class OrderItem(models.Model):
|
||||
order = models.ForeignKey(Order, on_delete=models.CASCADE, verbose_name=_("Order"))
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_("Price"))
|
||||
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, verbose_name=_("Ad"))
|
||||
count = models.PositiveIntegerField(default=0, verbose_name=_("Count"))
|
||||
class OrderItemModel(AbstractBaseModel):
|
||||
order = models.ForeignKey(
|
||||
OrderModel,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name=_("Order"),
|
||||
related_name="items"
|
||||
)
|
||||
ad = models.ForeignKey(
|
||||
"api.AdModel",
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_("Ad")
|
||||
)
|
||||
price = models.DecimalField(
|
||||
_("Price"),
|
||||
max_digits=10,
|
||||
decimal_places=2
|
||||
)
|
||||
quantity = models.PositiveIntegerField(_("Quantity"), default=1)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.pk)
|
||||
return f"{self.ad.name} x {self.quantity}"
|
||||
|
||||
@property
|
||||
def subtotal(self):
|
||||
"""Calculate item subtotal"""
|
||||
return self.price * self.quantity
|
||||
|
||||
class Meta:
|
||||
db_table = "order_item"
|
||||
verbose_name = _("Order Item")
|
||||
verbose_name_plural = _("Order Items")
|
||||
unique_together = [["order", "ad"]]
|
||||
|
||||
Reference in New Issue
Block a user