使用GenericForeignKey设计Django模型



我有一个Scheme模型,它只能分配两个奖励,一个给Scheme的成员,另一个给他们的朋友。

以下是我如何为此设计模型的,但现在我开始质疑设计,"计划和奖励"的链接不正确吗?我是否应该在抽象奖励上采取相反的关系?

方案:

class Scheme(models.Model):
name = models.CharField(max_length=60)
participant_reward_content_type = models.ForeignKey(ContentType,
editable=False,
related_name='%(app_label)s_%(class)s_as_participant',
null=True, blank=True
)
participant_reward_object_id = models.PositiveIntegerField(null=True, blank=True)
participant_reward = generic.GenericForeignKey('participant_reward_content_type', 'participant_reward_object_id')
friend_reward_content_type = models.ForeignKey(ContentType,
editable=False,
related_name='%(app_label)s_%(class)s_as_friends',
null=True, blank=True
)
friend_reward_object_id = models.PositiveIntegerField(null=True, blank=True)
friend_reward = generic.GenericForeignKey('friend_reward_content_type', 'friend_reward_object_id')

奖励:

class AbstractReward(models.Model):
"""
Abstract reward common information shared for all rewards.
"""
description = models.CharField(max_length="150")
active = models.BooleanField(default=True)
#scheme = models.ForeignKey(Scheme, null=True,)
class Meta:
abstract = True

class SingleVoucherReward(AbstractReward):
"""
Single-use coupons are coupon codes that can only be used once
"""
pass
class Meta:
app_label = 'schemes'

class MultiVoucherReward(AbstractReward):
"""
A multi-use coupon code is a coupon code that can be used unlimited times.
"""
code = models.CharField(max_length=200)
expiry = models.DateTimeField(null=True)
class Meta:
app_label = 'schemes'

class CustomReward(AbstractReward):
"""
A reward class used when it can't be handled or they would like to
handle reward fulfillment themselves.
"""
pass
class Meta:
app_label = 'schemes'

我建议保持简单-http://en.wikipedia.org/wiki/KISS_principle

考虑到三种类型的奖励在数据定义上的相似性,我会完全失去继承,只给它一个类型选择:

class Reward(models.Model):
SINGLE = 'Single'
MULTI = 'Multi'
CUSTOM = 'Custom'
TYPE_CHOICES = (
(SINGLE, 'Single'),
(MULTI,  'Multi'),
(CUSTOM, 'Custom'),
)
description = models.CharField(max_length="150")
active = models.BooleanField(default=True)
type = models.CharField(max_length=10, choices=TYPE_CHOICES, default=SINGLE)
code = models.CharField(max_length=200, blank=True)
expiry = models.DateTimeField(null=True)

Django的两个Scoops——这是如何在Django中处理事情的一个很好的参考——也推荐了这种方法。

这也意味着你不需要GenericForeignKey,可以使用简单的外键,从而再次大幅降低复杂性:

class Scheme(models.Model):
name = models.CharField(max_length=60)
participant_reward = models.ForeignKey('Reward', null=True, blank=True)
friend_reward = models.ForeignKey('Rewards', null=True, blank=True)

像Django管理员和ModelForms这样的内置工具将通过这种方法开箱即用。

有些人可能不喜欢TYPE_CHOICES的冗长,但它维护起来非常简单明了。

我还意识到,你可能会在Reward类上使用一些方法,这些方法必须修改不同类型的行为,例如:

if self.type = CUSTOM:
pass

但同样,维护起来非常简单。如果代码开始出现分歧,可以使用代理模型。

有些人可能会争辩说这不是"python",但我们在这里没有处理纯python类,而且除了将python的Zen状态作为其第三个原则之外:

简单胜于复杂。

您可以使您的AbstractReward不那么抽象(并将其重命名为BaseReward),然后将ForeignKey转换为它,并通过某种方法获得实际的奖励类型和对象。您需要提出额外的请求,但我认为GenericForeignKey也是如此。

最新更新