在 Django 模型中存储项目的类别和子类别的最佳方法

Posted

技术标签:

【中文标题】在 Django 模型中存储项目的类别和子类别的最佳方法【英文标题】:Best way to store category and subcategories of an item in Django models 【发布时间】:2015-08-02 10:57:46 【问题描述】:

我想存储像苹果这样的东西,它将被存储为“树果 - 苹果 - 富士 - 有机”,而一些商品的类别更多,例如小麦,即“谷物 - 小麦 - 软白冬 - 斯蒂芬斯” - 有机”。

class Item(models.Model):

    user = models.ForeignKey(User)
    type = models.CharField(max_length=100, default=None ,blank=True) #Cereal Grain
    commodity = models.CharField(max_length=100, default=None ,blank=True) #Wheat
    cl = models.CharField(max_length=100, default=None ,blank=True) #Soft White Winter
    variety = models.CharField(max_length=100, default=None ,blank=True) #Stephens
    market = models.CharField(max_length=100, default=None ,blank=True) #Organic

    def __str__(self):
        return u"%s" % (self.user)



class Shorten_Item(models.Model):

    user = models.ForeignKey(User)
    type = models.CharField(max_length=100, default=None ,blank=True) #Tree Fruit
    commodity = models.CharField(max_length=100, default=None ,blank=True) #Apple
    variety = models.CharField(max_length=100, default=None ,blank=True) #Fuji
    market = models.CharField(max_length=100, default=None ,blank=True) #Organic


    def __str__(self):
        return u"%s" % (self.user)

将有与这些表相关联的成本项目。

class User_Variable_Items(models.Model):

    #variety = models.ForeignKey(User_Variety)
    category = models.CharField(max_length=100,default=None ,blank=True)
    sub_category = models.CharField(max_length=100,default=None ,blank=True)
    item = models.CharField(max_length=100,default=None ,blank=True)
    price = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)
    unit = models.CharField(max_length=100,default=None ,blank=True)
    quantity = models.FloatField(default=0,blank=True)
    total = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)

    class Meta:
        verbose_name_plural = _("User_Variable_Items")

class User_Fixed_Items(models.Model):

    #variety = models.ForeignKey(User_Variety)
    category = models.CharField(max_length=100,default=None ,blank=True)
    price = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)
    unit = models.CharField(max_length=100,default=None ,blank=True)
    quantity = models.FloatField(default=0,blank=True)
    total = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)

    class Meta:
        verbose_name_plural = _("User_Fixed_Items")

User_Variable_Cost_ItemsUser_Fixed_Cost_items 引用上表之一(不是两者)的最佳方式是什么?

谢谢

【问题讨论】:

【参考方案1】:

我认为你想要做的是:

拥有某种类型的项目,来自某个类别,可以在继承层次结构上。 Itens 可以有不同的类型并制作不同的东西,这听起来像是一个继承结构(例如 Item > PriceFixedItem 和 Item > VariablePriceItem)。

通过这种方式,您可以尝试组合数据结构来存储和恢复数据库中的数据。你可以尝试做这样的事情吗:

创建一个引用自己的模型,创建一个类别树,可以是:

class Category(models.Model):
     parent = models.ForeignKey('self') # Here category will refer to it self
     name = models.CharField()

现在您的商品只能引用一个类别,您可以将这里的东西变得更加动态:

 class Item(models.Model):
     category = models.ForeignKey(Category)
     name = models.CharField()
     price = models.FloatField()

我们现在将拥有不同类型的物品:

class FixedPriceItem(Item):
    pass

class VariablePriceItem(Item):
    price_tax = models.FloatField()

目前最好的是我们如何拥有这个结构,Django可以解决你的问题,但是如何?很简单,看这个:

class UserItem(models.Model):
    item = models.ForeignKey(Item)

从现在开始,您将拥有一种单一的对象类型来管理用户项目,这非常好维护。我可以在这里看到的最重要的事情是您正在尝试解决数据库结构上的这个问题,但是当您在像 Django 上这样的 ORM 框架上进行编程时,您不必太在意数据库结构,您需要解决您在编程语言上的问题,Python 在这种情况下,所以使用 OO 概念解决这个问题很简单,就像这个一样。另一个重要的事情是你一生都必须牢记的概念:拥有数据的对象,就是关心数据的对象。如果你在错误的地方实现这个你的控制逻辑,你将不得不写更多,哭更多,在这个例子中,你必须让 Item 对象像他们将呈现、计算或存储他的数据一样关心。不要尝试在User的类上实现控件,当你有另一种类型的物品时这将非常有价值,你需要多放一个if来验证是否像这样或那样使用这个物品。

【讨论】:

要让模型具有引用自身的外键,请使用models.ForeignKey('self')...请参阅docs 和此answer 刚刚更新,谢谢。我知道'self',只是缺乏。【参考方案2】:

如果你可以重新组织你的模型,你可以利用模型继承 (https://docs.djangoproject.com/en/1.8/topics/db/models/#model-inheritance)

您的Item 模型可能是Shorten_Item 的子类,因为它只包含一个额外字段cl = CharField()

您可以保持Shorten_Item 不变,然后让Item 继承该模型:

class Item(Shorten_Item):
    cl = models.CharField(max_length=100, default=None ,blank=True)

现在在你的另一个班级,

class User_Variable_Items(models.Model):

    ...
    # Add a foreign key to the base Item class
    # Which by inheritance can contain both keys from Shorten_Item and Item
    item = ForeignKey('Shorten_Item')

当您检索 Shorten_Item 的 ID 时,使用您的业务逻辑来确定是要将其用作 Shorten_Item 还是 Item(在需要时将其向下转换为 Item

如果您无法重新组织模型以从继承中受益,您唯一的解决方案可能是使用 item_id = IntegerField() 作为能够指向任何表的“外键”,并且您必须编写更多内容自己查询

【讨论】:

以上是关于在 Django 模型中存储项目的类别和子类别的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 模型中存储与外键关联的数据

SELECT 子类别和没有子类别的父类别

选择一个包含子类别的类别并将它们放入 DataGridView 中,没有类别重复

如何在 Django 模型上存储字典?

获取总类别树(所有类别及其子类别的列表)与根据需要获取每个分支?

获取所有属于子类别的文章 多对多 Laravel