Как реализовать категорию, подкатегорию и продукт в django

class Category(models.Model):
    name= models.CharField(max_length=50)

    @staticmethod
    def get_all_categories():
        return Category.objects.all()

    def __str__(self):
        return self.name


class Products(models.Model):
    name = models.CharField(max_length=60)
    price= models.IntegerField(default=0)
    category= models.ForeignKey(Category,on_delete=models.CASCADE,default=1 )

Как мне теперь добавить подкатегорию в эту иерархию

Вы можете ссылаться на саму модель, используя 'self', например:

class Foo(models.Model):
    parent = models.ForeignKey('self', related_name='children', ...)

В зависимости от ваших потребностей, если ваши подкатегории могут иметь только одного родителя, то вы можете сделать:

class Category(models.Model):
    name = models.CharField(max_length=50)
    parent_category = models.ForeignKey(
        'self', related_name='sub_categories',
         on_delete=models.SET_NULL, null=True
    )

Но если ваши подкатегории могут иметь более одной родительской категории, то вы можете ссылаться на них, используя отношение "многие ко многим":

class Category(models.Model):
    name = models.CharField(max_length=50)
    sub_categories = models.ManyToManyField(
        'self', related_name='parent_categories',
    )

Предполагается, что одна категория может иметь более одной подкатегории, а одна подкатегория может быть связана с более чем одной категорией.

Одним из способов () является создание трех таблиц (надеюсь, вы видите взаимосвязь),

   class Category(models.Model):
        name= models.CharField(max_length=50)
    
        @staticmethod
        def get_all_categories():
            return Category.objects.all()
    
        def __str__(self):
            return self.name

    class SubCategory(models.Model):
        name = models.TextField(max_length=50)
        categories = models.ManyToManyField(Category)
    
    
    class Products(models.Model):
        name = models.CharField(max_length=60)
        price = models.IntegerField(default=0)
        category= models.ManyToManyField(SubCategory)

Другой способ ( удобный) относится к самому себе, ManyToManyField.symmetrical Это лучший способ.

class Category(models.Model):
    name = models.CharField(max_length=50)
    sub_categories = models.ManyToManyField("self")

    @staticmethod
    def get_all_categories():
        return Category.objects.all()

    def __str__(self):
        return self.name

Если вы не указываете имя related_name, Django автоматически создает его, используя имя вашей модели с суффиксом _set. В Документации есть более подробная информация

Использование модели mptt является лучшим вариантом для работы над этим сценарием использования

class Category(MPTTModel):
    name = models.CharField(max_length=128)
    slug = models.SlugField(max_length=128)
    description = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)
    parent = models.ForeignKey(
        "self", null=True, blank=True, related_name="children", on_delete=models.CASCADE
    )
    background_image = VersatileImageField(
        upload_to="category-backgrounds", blank=True, null=True
    )
    background_image_alt = models.CharField(max_length=128, blank=True)
    category_icon = models.FileField(upload_to="category-icons", max_length=200, blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = TreeManager()

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)


class Product(models.Model):
    name = models.CharField(max_length=200)
    weight = models.FloatField(default=0)
    length = models.FloatField(default=0)
    width = models.FloatField(default=0)
    height = models.FloatField(default=0)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    discounted_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    category = models.ForeignKey(
        Category, related_name="products", on_delete=models.CASCADE
    )
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name
    @property
    def get_featured_image(self):
        image = self.images.filter(is_featured=True).first()
        return image.image.url if image else None

    @property
    def get_price(self):
        if self.discounted_price:
            return self.discounted_price
        return self.price

django mptt docs

Для справки

Вернуться на верх