【问题标题】:Django sharing one model between two othersDjango 在另外两个模型之间共享一个模型
【发布时间】:2016-06-15 23:35:37
【问题描述】:

我建立了一个系统来审查葡萄酒和食品。我很快发现自己在重复模型和模板,但差异很小。

从根本上说,我似乎希望评论与食物或葡萄酒相关。而且每种食物或葡萄酒都可以有很多评论。

我对两者都有一个 FK(当前方式),只留了一个空白,但鉴于它们非常相似,我认为这是不明智的。

然后我使用抽象模型来至少泛化字段(新方法),但由于我无法链接到通用项目模型,因此我有一个更优雅的代码库来解决同样的问题。

对此进行研究,我想知道从食物和葡萄酒到评论的通用关系是否是可行的方法,或者可能是内容类型,但我不太了解它们是如何工作的,或者它们是否是我正在寻找的为。

目前的方式 - 葡萄酒有品牌,食品有商店,评论有食品和葡萄酒

class Brand(models.Model):
    brand_name = models.CharField(max_length=30)
    location = models.ForeignKey(Location, null=True,blank=True)

import datetime
YEAR_CHOICES = []
for r in range(2005, (datetime.datetime.now().year+1)):
    YEAR_CHOICES.append((r,r))
YEAR_CHOICES = list(reversed(YEAR_CHOICES))

class Wine(models.Model):
    wine_name = models.CharField(max_length=30)
    wine_type = models.ForeignKey(WineType)
    wine_year = models.IntegerField( choices=YEAR_CHOICES, default=datetime.datetime.now().year)

    brand = models.ForeignKey(Brand)

class Store(models.Model):
    store_name = models.CharField(max_length=30)

    def __str__(self): 
        return self.store_name

class Food(models.Model):
    food_name = models.CharField(max_length=30)
    food_desc = models.CharField(blank=True,max_length=100)

    store = models.ForeignKey(Store)

    def __str__(self): 
        return self.store.store_name +' - '+self.food_name

class Review(models.Model):
    rating = models.CharField(max_length=30)
    value = models.CharField(max_length=30)
    date = models.DateField(auto_now_add=True)
    person = models.ForeignKey(Person)
    comment = models.CharField(blank=True,max_length=100)
    food = models.ForeignKey(Food, blank=True,default=None,null=True)
    wine = models.ForeignKey(Wine, blank=True,default=None,null=True)

    class Meta():
        ordering = ['-date']

新方式 - 葡萄酒和食品是商品,商店和品牌是来源,但评论仍然需要葡萄酒和食品

class Source(models.Model):
    name =  models.CharField(max_length=30)
    desc = models.CharField(blank=True,max_length=100)
    class Meta:
        abstract = True

class Item(models.Model):
    name =  models.CharField(max_length=30)
    desc = models.CharField(blank=True,max_length=100)

    class Meta:
        abstract = True



class WineSource(Source):
    location = models.ForeignKey(Location, null=True,blank=True)
    class Meta():
        ordering = ['location', 'name']

class FoodSource(Source):
    def __str__(self): 
        return self.name

import datetime
YEAR_CHOICES = []
for r in range(2005, (datetime.datetime.now().year+1)):
    YEAR_CHOICES.append((r,r))
YEAR_CHOICES = list(reversed(YEAR_CHOICES))

class Wine(Item):

    wine_type = models.ForeignKey(WineType)
    wine_year = models.IntegerField( choices=YEAR_CHOICES, default=datetime.datetime.now().year)

    source = models.ForeignKey(WineSource)

    def __str__(self): 
        return self.source.name +' '+self.name+ ' ' + str(self.wine_type)+ ' '+ str(self.wine_year)

class Food(Item):


    source = models.ForeignKey(FoodSource)

    def __str__(self): 
        return self.source.name +' - '+self.name

class Review(models.Model):
    rating = models.CharField(max_length=30)
    value = models.CharField(max_length=30)
    date = models.DateField(auto_now_add=True)
    person = models.ForeignKey(Person)

    food = models.ForeignKey(Food, blank=True,default=None,null=True)
    wine = models.ForeignKey(Wine, blank=True,default=None,null=True)
    #Doesn't work as it's abstract- item = models.ForeignKey(Item,null=True)

    class Meta():
        ordering = ['-date']

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    您也可以使用Multi-table inheritance 代替AbstractClass 来代替Item。然后您可以在Review 中将ForeignKey 直接设置为Item。 您也可以将其与InheritanceManager 结合使用:

    from model_utils.managers import InheritanceManager
    
    class Item(models.Model):
        name =  models.CharField(max_length=30)
        desc = models.CharField(blank=True,max_length=100)    
    
        objects = InheritanceManager()    
    
    class Wine(Item):
    
        wine_type = models.ForeignKey(WineType)
        wine_year = models.IntegerField( choices=YEAR_CHOICES, default=datetime.datetime.now().year)
    
        source = models.ForeignKey(WineSource)
    
        def __str__(self): 
            return self.source.name +' '+self.name+ ' ' + str(self.wine_type)+ ' '+ str(self.wine_year)
    
    
    class Food(Item):
    
        source = models.ForeignKey(FoodSource)
    
        def __str__(self): 
            return self.source.name +' - '+self.name
    
    class Review(models.Model):
        rating = models.CharField(max_length=30)
        value = models.CharField(max_length=30)
        date = models.DateField(auto_now_add=True)
        person = models.ForeignKey(Person)
    
        item = models.ForeignKey(Item,null=True)
    
        class Meta():
            ordering = ['-date']
    

    然后你可以像这样过滤:

    wine_reviews = Review.objects.exclude(item__wine__isnull=True)
    
    food_reviews = Review.objects.exclude(item__food__isnull=True)
    
    # and all item (directly sub-classed as wine or food:
    items = Item.objects.select_subclasses().all()
    

    【讨论】:

    • 我认为这是一种非常酷的技术,我可以看到它在我想要更多“OO”的地方工作,但通过抽象类的简单继承足以让我的代码重用
    【解决方案2】:

    我认为Generic Foreign Key 是答案。比如:

    from django.db import models
    from django.contrib.contenttypes.fields import GenericForeignKey
    from django.contrib.contenttypes.models import ContentType
    
    class Product(models.Model):
         ...
    
    class Food(Product):
         ...
    
    class Wine(Product):
         ...
    
    class Review(models.Model):
        ...
    
        content_type = models.ForeignKey(ContentType)
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')
    

    这使我们能够将评论与我们项目中任何模型的任何单个记录相关联。 content_type 字段跟踪您尝试关联的模型(在这种情况下为FoodWine)。 object_id 字段跟踪记录在我们试图跟踪的WineFood 表中。 content_object 是一个方便属性,允许我们直接访问对象(一旦保存评论)。

    创建新评论时,您只需将WineFood 分配给content_object 字段:

    wine = Wine.objects.get(...)
    review = Review(..., content_object=wine)
    review.save()
    

    【讨论】:

    • 环顾四周后,我可以不做以下事情(取自stackoverflow.com/questions/20895429/…):reviews= generic.GenericRelation('Review') 至于可用性。我还查找给定葡萄酒的评论(使用某种我不太了解的助手)......这行得通吗?即在模板中 {% for review in wine.review_set.all %}
    • {% for review in wine.reviews.all %} 和我能想到的每一个变化都不再起作用:/
    猜你喜欢
    • 1970-01-01
    • 2012-09-19
    • 1970-01-01
    • 2022-01-06
    • 2015-09-30
    • 2010-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多