在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
和每个LongNamedRestaurant
。 LongNamedRestaurant
只是创build一个额外的表与food_type
和地方表的引用。
如果你做了Place.objects.all()
,你也会得到每一个LongNamedRestaurant
地方,它将成为Place
一个实例(没有food_type
)。 所以Place.name
和LongNamedRestaurant.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”的表单字段捕获