使用GenericForeignKey的Django模型设计

时间:2014-01-05 23:26:39

标签: python django django-models

我有一个Scheme模型,只能分配2个奖励,一个用于方案成员,另一个用于他们的朋友。

以下是我为此设计模型的方法,但现在我开始质疑设计,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'

2 个答案:

答案 0 :(得分:2)

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

鉴于3种类型奖励的数据定义相似,我完全失去了继承权,只是给它一个类型选择:

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)

Two Scoops of Django - 这是如何在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

但这又是非常简单的维护。如果代码开始真正分歧,你可以使用Proxy Models

有些人可能认为这不是'Pythonic',但我们不是在这里处理纯Python类,除了Zen of Python状态作为其第三原则:

  

简单比复杂更好。

答案 1 :(得分:1)

您可以使AbstractReward不那么抽象(并将其重命名为BaseReward),然后将ForeignKey重命名为它,并以某种方式获取实际的奖励类型和对象。您需要提出额外的请求,但我认为它与GenericForeignKey相同。