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',
)
Assuming that one category can have more than one subcategory and one subcategory can be related to more than one category.
Один из способов (беспорядочный) - создать три таблицы (надеюсь, вы видите взаимосвязь),
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)
Другой способ (удобный) связан с самим собой, Манитоманифиелд.симметричный Это лучший способ.
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
class Products(models.Model):
name = models.CharField(max_length=60)
price = models.IntegerField(default=0)
category= models.ManyToManyField(Category)
Если вы не укажете related_name, Django автоматически создаст его, используя имя вашей модели с суффиксом _set
. Документация содержит более подробную информацию
Каков наилучший способ? 3 стола или просто. 1 стол?
На самом деле там две таблицы. Для данного контекста подход recursive relationship
является лучшим способом (при условии, что объект подкатегории не имеет дополнительных атрибутов). Categories
и SubCategories
одно и то же
Я использовал грязный способ, 3 таблицы
Процесс запроса станет утомительным
Использование модели 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
Для справки
Я бы также предложил это, так как это уменьшает беспорядок и может масштабироваться дальше.