Django在另外两个人之间共享一个模型

时间:2016-03-02 21:43:42

标签: python django django-models

我建立了一个审查葡萄酒和食物的系统。我很快发现自己重复模型和模板的微小差异。

从根本上说,我似乎想要一份与食物或葡萄酒相关的评论。每种食物或葡萄酒都可以有很多评论。

我对两者都有一个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']

2 个答案:

答案 0 :(得分:1)

我认为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()

答案 1 :(得分:1)

您还可以使用Multi-table inheritance代替AbstractClass Item。然后,您可以在ForeignKey中设置直接ItemReview。 您也可以将其与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()