如何干燥我的模型
Posted
技术标签:
【中文标题】如何干燥我的模型【英文标题】:How to DRY up my models 【发布时间】:2014-05-08 05:50:43 【问题描述】:我是一个制作问答网站。目前我有这样的模型
class Question(models.Model):
title = models.CharField(max_length=150)
detail = models.TextField()
submitter = models.ForeignKey(User)
date_added = models.DateTimeField(auto_now_add = True)
...# some additional fields such as tags
class Answer(models.Model):
detail = models.TextField()
submitter = models.ForeignKey(User)
date_added = models.DateTimeField(auto_now_add = True)
...
class QuestionVote(models.Model):
voter = models.ForeignKey(User)
question = models.ForeignKey(Question)
#replicating what I did for QuestionVote
class AnswerVote(models.Model):
voter = models.ForeignKey(User)
question = models.ForeignKey(Question)
除了标题和标签外,问答模型相同。要向 Answers 添加投票功能,我必须将 QuestionVote 模型复制为 AnswerVote,并重复我为视图中的问题投票所做的一切。我看了一点模型继承,但如果我声明一个抽象基类并从中继承问答模型,那么我不能使用外键。那么避免这种重复的最佳方法是什么?
【问题讨论】:
@谁指出,我已经纠正了复制粘贴错误 【参考方案1】:您可以反过来使用一对一关系:
class Vote(models.Model):
voter = models.ForeignKey(User)
...# some additional fields
class Question(models.Model):
title = models.CharField(max_length=150)
detail = models.TextField()
submitter = models.ForeignKey(User)
date_added = models.DateTimeField(auto_now_add = True)
vote = models.OneToOneField(Vote)
...# some additional fields such as tags
class Answer(models.Model):
detail = models.TextField()
submitter = models.ForeignKey(User)
date_added = models.DateTimeField(auto_now_add = True)
vote = models.OneToOneField(Vote)
...
【讨论】:
目前为止我最喜欢这种方法。这样我也可以使用继承,外键将进入继承的模型。我认为哪个应该有效。【参考方案2】:我个人觉得 DRY 很重要,所以我愿意牺牲一些优雅来实现它。例如,在这种情况下,我可能会这样做:
class QuestionOrAnswer(models.Model):
is_question = models.BooleanField()
title = models.CharField(max_length=150)
detail = models.TextField()
submitter = models.ForeignKey(User)
date_added = models.DateTimeField(auto_now_add = True)
question_specific_field = ...
def clean(self):
# Make sure that question_specific_field is set only if is_question is true.
class Vote(models.Model):
target = models.ForeignKey(QuestionOrAnswer)
...
【讨论】:
如果 Answer 具有 Question 的外键(它实际上应该)会发生什么? 您可以使用自引用外键:question = models.ForeignKey('self')。见***.com/questions/15285626/…【参考方案3】:看看abstract base classes
您可以将公共字段/功能放入其中(submitter
、detail
等),然后在您的 Question
和 Answer
模型中继承它
编辑:
基于其他答案和我对您的问题的更多了解,我正在更改我的答案。您目前在“干”和“良好的可读性/可维护性”之间徘徊,并且不同的开发人员会在不同的网站上出现,所以您可能只需要选择您最喜欢的一个。
关于那张纸条,我是这样表述的:
##############################
# Question and Answer Models #
##############################
class QuestionAnswerBase(models.Model): # Choose a better name :)
detail = models.TextField()
submitter = models.ForeignKey(User)
date_added = models.DateTimeField(auto_now_add = True)
... # More common fields here
class Meta:
abstract = True
class Question(QuestionAnswerBase):
title = models.CharField(max_length=150)
...# some additional fields such as tags etc
class Answer(QuestionAnswerBase):
... # No extra fields needed but you'll prob want a custom __unicode__ method etc
###############
# Vote Models #
###############
class VoteBase(models.Model):
voter = models.ForeignKey(User)
... # Other shared fields etc
class Meta:
abstract = True
class AnswerVote(VoteBase):
answer = models.ForeignKey(Answer)
class QuestionVote(VoteBase):
question = models.ForeignKey(Question)
(在这种情况下,注释块是为了便于阅读。)
“但现在我有更多的模型!”
是的,你有,但它们中没有重复的字段,并且抽象意味着你有能力添加不同的行为——例如——QuestionVote
和AnswerVote
(假设你想要这个能力如果出现更好的Answer
,则撤回AnswerVote
s,但不希望能够撤回QuestionVote
s)而不需要“这是X是Y还是Z?如果是Z就行在你编写的每个模型方法中都有 this else do this" 子句。
“看起来很丑!”
它既可读又显式,这意味着它是beautiful :)
“我想换一种方式!”
没关系,已经有其他很好的答案可以说明如何做到这一点。这只是我作为一个非常热衷于最佳实践和可读代码的人的建议。
【讨论】:
但是你不能将外键指向一个抽象类,因为抽象类没有与之相关的表。 @Selcuk 你是对的,我误读了要求。虽然默认情况下无法完成,但有一些很好的 django 包允许很多 DRYer 模型继承 + 外键关系,例如django-polymorphic。不过在这种情况下可能有点矫枉过正。以上是关于如何干燥我的模型的主要内容,如果未能解决你的问题,请参考以下文章