2014-01-05 2 views
1

У меня есть модель схемы, которая может иметь только 2 награды, один для члена схемы, а другой для своего друга.Дизайн модели Django с использованием GenericForeignKey

Ниже приведен пример того, как у меня есть модель для этого, но теперь я начинаю сомневаться в дизайне. Неправильно ли ссылка на Схему и награды? Должен ли я иметь отношения наоборот на абстрактной награде?

Схема:

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) 

Встроенный в таких вещах, как администратор и ModelForms Django будет просто работать из с этим подходом.

Некоторым может не понравиться многословие TYPE_CHOICES, но это так просто и понятно для поддержания.

Я также понимаю, что вы можете в конечном итоге с методами на классе Reward, которые должны изменить поведение для различных типов т.д .:

if self.type = CUSTOM: 
    pass 

, но опять же, это очень прост в обслуживании. Вы можете использовать Proxy Models, если код начинает действительно расходиться.

Некоторые могут возразить, что это не «Pythonic», но мы не обрабатывает чистые классы питона здесь, и кроме Zen of Python государств, как его третий принцип:

Простой лучше, чем сложнее.

+0

Спасибо DaveB, вы очень интересные комментарии, читая два совок Django прямо сейчас. – Prometheus

+0

Моя единственная проблема с этим подходом заключается в том, что разные награды имеют расширенные свойства, которые не обязательно относятся к каждому. Вот почему у меня есть абстрактная модель для базы. Мне нравится идея использования селекторов tho. – Prometheus

+0

Прокси-модели, это интересно, я не знал, что вы даже можете это сделать, хорошо. Единственный улов - это «наследовать от одного не абстрактного модельного класса», – Prometheus

1

Вы можете сделать свой AbstractReward не так абстрактно (и переименовать его в BaseReward), затем ForeignKey к нему и получить фактическое вознаграждение тип и объект в некотором методе. Вам нужно будет сделать дополнительный запрос, но я думаю, что это будет то же самое с GenericForeignKey.

Смежные вопросы