python 在Django中-模型继承-它是否允许您覆盖父模型的属性?




django-inheritance (8)

我希望这样做:

class Place(models.Model):
   name = models.CharField(max_length=20)
   rating = models.DecimalField()

class LongNamedRestaurant(Place):  # Subclassing `Place`.
   name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
   food_type = models.CharField(max_length=25)

这是我想要使用的版本(虽然我对任何建议持开放态度): http://docs.djangoproject.com/en/dev/topics/db/models/#id7http://docs.djangoproject.com/en/dev/topics/db/models/#id7

Django支持这个吗? 如果没有,有没有办法取得类似的结果?


将您的代码粘贴到一个全新的应用程序中,将应用程序添加到INSTALLED_APPS并运行syncdb:

django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'

看起来Django不支持这一点。


从Django 1.10开始, 它是可能的 ! 你只需做你要求的事情:

class Place(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class LongNamedRestaurant(Place):  # Subclassing `Place`.
    name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
    food_type = models.CharField(max_length=25)

除非抽象,否则这是不可能的,这就是为什么: LongNamedRestaurant也是一个Place ,不仅作为一个类,而且还在数据库中。 place-table包含每个纯Place和每个LongNamedRestaurantLongNamedRestaurant只是创建一个额外的表,其中food_type和对place表的引用。

如果你做了Place.objects.all() ,你也得到了一个LongNamedRestaurant地方,它将是Place一个实例(没有food_type )。 因此Place.nameLongNamedRestaurant.name共享相同的数据库列,因此必须属于同一类型。

我认为这对于普通模型来说是有道理的:每个餐馆都是一个地方,至少应该拥有一切。 也许这种一致性也是1.10之前的抽象模型不可能的原因,尽管它不会给那里的数据库问题。 正如@lampslave所言,它在1.10中成为可能。 我个人建议小心:如果Sub.x覆盖了Super.x,请确保Sub.x是Super.x的子类,否则Sub不能代替Super使用。

解决方法 :如果您只需要更改电子邮件字段,则可以创建自定义用户模型( AUTH_USER_MODEL ),该模型涉及相当多的代码重复。 或者,您可以按原样保留电子邮件,并确保所有表格都需要。 如果其他应用程序使用它,则不保证数据库完整性,并且不能以相反的方式工作(如果您不想要用户名)。


请参阅https://.com/a/6379556/15690 :

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

class Message(BaseMessage):
    # some fields...
Message._meta.get_field('is_public').default = True

这个超酷的代码片段允许您“覆盖”抽象父类中的字段。

def AbstractClassWithoutFieldsNamed(cls, *excl):
    """
    Removes unwanted fields from abstract base classes.

    Usage::
    >>> from oscar.apps.address.abstract_models import AbstractBillingAddress

    >>> from koe.meta import AbstractClassWithoutFieldsNamed as without
    >>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
    ...     pass
    """
    if cls._meta.abstract:
        remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
        for f in remove_fields:
            cls._meta.local_fields.remove(f)
        return cls
    else:
        raise Exception("Not an abstract model")

从抽象父类中删除字段后,您可以根据需要自由重新定义它们。

这不是我自己的工作。 来自这里的原始代码: https://gist.github.com/specialunderwear/9d917ddacf3547b646bahttps://gist.github.com/specialunderwear/9d917ddacf3547b646ba


不, 它不是

字段名称“隐藏”是不允许的

在普通的Python类继承中,子类允许覆盖父类的任何属性。 在Django中,不允许使用Field实例的属性(至少目前不是这样)。 如果基类具有名为author的字段,则无法在从该基类继承的任何类中创建另一个名为author模型字段。


我知道这是一个老问题,但我有一个类似的问题,并找到了一个解决方法:

我有以下课程:

class CommonInfo(models.Model):
    image = models.ImageField(blank=True, null=True, default="")

    class Meta:
        abstract = True

class Year(CommonInfo):
    year = models.IntegerField() 

但我希望在保持超类的图像字段可空的同时,需要Year的继承图像字段。 最后,我使用ModelForms在验证阶段强制执行图像:

class YearForm(ModelForm):
    class Meta:
        model = Year

    def clean(self):
        if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
            raise ValidationError("Please provide an image.")

        return self.cleaned_data

admin.py:

class YearAdmin(admin.ModelAdmin):
    form = YearForm

这似乎只适用于某些情况(当然,您需要在子类字段上强制执行更严格的规则)。

或者,您可以使用clean_<fieldname>()方法而不是clean() ,例如,如果需要填写字段town

def clean_town(self):
    town = self.cleaned_data["town"]
    if not town or len(town) == 0:
        raise forms.ValidationError("Please enter a town")
    return town

您不能覆盖模型字段,但可以通过覆盖/指定clean()方法轻松实现。 我遇到了电子邮件领域的问题,并希望在模型级别上使其独一无二,并且这样做:

def clean(self):
    """
    Make sure that email field is unique
    """
    if MyUser.objects.filter(email=self.email):
        raise ValidationError({'email': _('This email is already in use')})

然后,表单字段捕获错误消息,名称为“email”







django-inheritance