在Django中 – 模型inheritance – 它允许你重写父模型的属性吗?

我正在寻找这样做:

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) 

这是我想要使用的版本(虽然我打开任何build议): http : //docs.djangoproject.com/en/dev/topics/db/models/#id7

Django支持这个吗? 如果没有,是否有办法达到类似的结果?

不, 它不是 :

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

在普通的Python类inheritance中,允许子类重写父类的任何属性。 在Django中,这是不允许的Field实例属性(至less,目前不)。 如果基类有一个名为author的字段,则不能在任何inheritance自该基类的类中创build另一个称为author模型字段。

由于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) 

请参阅https://stackoverflow.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 

这是不可能的,这是为什么: LongNamedRestaurant也是一个Place ,不仅作为一个类,而且在数据库中。 地方表包含每个纯Place和每个LongNamedRestaurantLongNamedRestaurant只是创build一个额外的表与food_type和地方表的引用。

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

我认为这对于普通的模式是有意义的:每个餐厅都是一个地方,至less应该有那个地方的一切。 也许这种一致性也是抽象模型不可能的原因,尽pipe它不会给数据库带来问题。 如果可能的话,它将允许您将AbstractUser扩展为自定义用户模型,只更改电子邮件字段并且不存在任何身份问题。

解决方法 :如果您只需要更改电子邮件字段,则可以创build自定义用户模型( AUTH_USER_MODEL ),其中包含相当多的代码重复。 或者,您可以保留电子邮件,并确保它是所有forms的必需。 这不能保证数据库的完整性,如果其他应用程序使用它,而不是反过来(如果你想使用户名不需要)。

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

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

看起来像Django不支持。

也许你可以处理contribution_to_class:

 class LongNamedRestaurant(Place): food_type = models.CharField(max_length=25) def __init__(self, *args, **kwargs): super(LongNamedRestaurant, self).__init__(*args, **kwargs) name = models.CharField(max_length=255) name.contribute_to_class(self, 'name') 

Syncdb工作正常。 我没有试过这个例子,在我的情况下,我只是重写一个约束参数,所以…等等,看!

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

 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/9d917ddacf3547b646ba

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

我有以下类:

 class CommonInfo(models.Model): image = models.ImageField(blank=True, null=True, default="") class Meta: abstract = True class Year(CommonInfo): year = models.IntegerField() 

但是我希望Year的inheritance的image-field是必需的,同时保持超类的图像域为空。 最后,我使用了ModelForms在validation阶段执行图像:

 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”的表单字段捕获