如何覆盖抽象 class 的选择?

How can I override choices of abstract class?

我有 AbstractProfile 预定义模型 PRIVACY_CHOICES:

class AbstractProfile(models.Model):
    PRIVACY_CHOICES = (
        (1, _('all')),
        (2, _('no one')),
    )

    title = models.CharField(_('title'), max_length=30)
    info = models.TextField(_('information'), max_length=500, blank=True)
    info_privacy = models.IntegerField(_('show information to'), default=1, choices=PRIVACY_CHOICES)

    city = models.CharField(_('location'), max_length=30, blank=True)
    city_privacy = models.IntegerField(_('show location to'), default=1, choices=PRIVACY_CHOICES)
    address = models.CharField(_('address'), max_length=30, blank=True)
    address_privacy = models.IntegerField(_('show address to'), default=1, choices=PRIVACY_CHOICES)

    class Meta:
        abstract = True

class UserProfile(AbstractProfile):
    PRIVACY_CHOICES = (
        (1, _('all')),
        (2, _('friends')),
        (3, _('friends of friends')),
        (4, _('only me')),
    )

    title = None

    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    names_privacy = models.IntegerField(_('show names to'), default=1, choices=PRIVACY_CHOICES)

    birth_date = models.DateField(_('birth date'), null=True, blank=True)
    birth_date_privacy = models.IntegerField(_('show birth date to'), default=1, choices=PRIVACY_CHOICES)

    avatar = models.ImageField(upload_to='users/avatar', null=True, blank=True)

UserProfile 应该有来自 AbstractProfile 的字段,但有自己的 PRIVACY_CHOICES。在当前实现中,UserProfilePRIVACY_CHOICES 不会覆盖 AbstractProfilePRIVACY_CHOICES。怎么可能解决?以后可能会有其他型号,应该也有自己的PRIVACY_CHOICES

我使用 Django 1.10

找到解决方案。

models.py:

class AbstractProfile(models.Model):
    PRIVACY_CHOICES = (
        (1, _('all')),
        (2, _('no one')),
    )

    title = models.CharField(_('title'), max_length=30)
    info = models.TextField(_('information'), max_length=500, blank=True)
    info_privacy = models.IntegerField(_('show information to'), default=1, choices=PRIVACY_CHOICES)

    city = models.CharField(_('location'), max_length=30, blank=True)
    city_privacy = models.IntegerField(_('show location to'), default=1, choices=PRIVACY_CHOICES)
    address = models.CharField(_('address'), max_length=30, blank=True)
    address_privacy = models.IntegerField(_('show address to'), default=1, choices=PRIVACY_CHOICES)

    class Meta:
        abstract = True

class UserProfile(AbstractProfile):
    PRIVACY_CHOICES = (
        (1, _('all')),
        (2, _('friends')),
        (3, _('friends of friends')),
        (4, _('only me')),
    )

    # NEW PIECE OF CODE
    def __init__(self, *args, **kwargs):
        def get_class_attrs(cls):
            return re.findall(r'\w+(?=[,\)])', cls.__dict__['__doc__'])

        super(UserProfile, self).__init__(*args, **kwargs)

        all_fields = get_class_attrs(UserProfile)
        for each_field in all_fields:
            # all fields with '_privacy' in the name have 'choice' option
            if '_privacy' in each_field:
                self._meta.get_field(each_field).choices = self.PRIVACY_CHOICES

                default_privacy_choice = self.PRIVACY_CHOICES[0][0]
                if self._meta.get_field(each_field).default != default_privacy_choice:
                    self._meta.get_field(each_field).default = default_privacy_choice
    # END OF NEW PIECE OF CODE

    title = None

    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    names_privacy = models.IntegerField(_('show names to'), default=1, choices=PRIVACY_CHOICES)

    birth_date = models.DateField(_('birth date'), null=True, blank=True)
    birth_date_privacy = models.IntegerField(_('show birth date to'), default=1, choices=PRIVACY_CHOICES)

    avatar = models.ImageField(upload_to='users/avatar', null=True, blank=True)

class CompanyProfile(AbstractProfile):
    pass

class OneMoreClass(AbstractProfile):
    pass

还有必要修改forms.py:

class UserProfileForm(forms.ModelForm):
    class Meta:
        model = UserProfile()  # old_version was: model = UserProfile
        fields = ('title',
                  'first_name', 'last_name', 'names_privacy',
                  'birth_date', 'birth_date_privacy',
                  'info', 'info_privacy',
                  'city', 'city_privacy', 'address', 'address_privacy',
                  'avatar',)

未修改的形式采用摘要 class 中的选项。现在不需要在不同的 class 中重复相同的字段。如果所有 classes 都有自己的选择版本,那么可以将方法 def __init__ 复制到那些 classes 并进行适当的修改(至少要更改 class 名称),甚至可以作为一个单独的功能,但这是另一回事了。