2014-01-05 47 views
1

我有一個計劃模型,可以有2個獎勵分配給它只有一個爲該計劃的成員和另一個爲他們的朋友。Django模型設計使用GenericForeignKey

以下是我如何爲此設計模型,但現在我開始質疑設計,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

我會建議保持它很簡單 - 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的詳細程度,但它是如此簡單明瞭的維護。

我也意識到,你可能最終與具有改變行爲,爲不同類型的例如爲:

if self.type = CUSTOM: 
    pass 

,但這又是保持非常簡單的獎勵類的方法。如果代碼開始出現分歧,您可以使用Proxy Models

也許有人會說,這不是「Python化」但我們不會在這裏處理純Python類,除了Zen of Python國家作爲其第三個原則:

簡單比複雜好。

+0

感謝DaveB,你的評論非常有趣,現在閱讀了Django的兩個Scoops。 – Prometheus

+0

這個方法我唯一的問題是不同的獎勵有擴展的屬性,不必與每個相關。這就是爲什麼我有一個基地的抽象模型。我喜歡使用選擇器的想法。 – Prometheus

+0

代理模型,這很有趣我不知道你甚至可以做到這一點,很好。唯一的問題是這個「必須從一個非抽象模型類繼承」 – Prometheus

1

你可以讓你AbstractReward不那麼抽象(重命名爲BaseReward),然後ForeignKey來,並得到實際的獎勵類型和對象的某種方法。您需要提出額外要求,但我認爲這與GenericForeignKey的要求相同。