我建立了一个审查葡萄酒和食物的系统。我很快发现自己重复模型和模板的微小差异。
从根本上说,我似乎想要一份与食物或葡萄酒相关的评论。每种食物或葡萄酒都可以有很多评论。
我对两者都有一个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']
答案 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
字段会跟踪您尝试与之关联的模型(在这种情况下为Food
或Wine
)。 object_id
字段跟踪我们正在尝试跟踪的Wine
或Food
表中的哪条记录。 content_object
是一个便利属性,允许我们直接访问该对象(一旦保存了评论)。
创建新评价时,您只需将Wine
或Food
分配到content_object
字段:
wine = Wine.objects.get(...)
review = Review(..., content_object=wine)
review.save()
答案 1 :(得分:1)
您还可以使用Multi-table inheritance代替AbstractClass
Item
。然后,您可以在ForeignKey
中设置直接Item
到Review
。
您也可以将其与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()