在 Django - 模型继承 - 是否允许您覆盖父模型的属性?
我希望这样做:
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/#id7
Django 支持吗?如果没有,有没有办法达到类似的结果?
I'm looking to do this:
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)
This is the version I would like to use (although I'm open to any suggestion):
http://docs.djangoproject.com/en/dev/topics/db/models/#id7
Is this supported in Django? If not, is there a way to achieve similar results?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
更新的答案:正如人们在评论中指出的那样,原始答案没有正确回答问题。事实上,数据库中只创建了
LongNamedRestaurant
模型,而没有创建Place
。解决方案是创建一个代表“Place”的抽象模型,例如。
AbstractPlace
,并继承它:另请阅读@Mark answer,他给出了很好的解释为什么不能更改从非抽象类继承的属性。
(请注意,这仅在 Django 1.10 后才可能:在 Django 1.10 之前,无法修改从抽象类继承的属性。)
Updated answer: as people noted in comments, the original answer wasn't properly answering the question. Indeed, only the
LongNamedRestaurant
model was created in database,Place
was not.A solution is to create an abstract model representing a "Place", eg.
AbstractPlace
, and inherit from it:Please also read @Mark answer, he gives a great explanation why you can't change attributes inherited from a non-abstract class.
(Note this is only possible since Django 1.10: before Django 1.10, modifying an attribute inherited from an abstract class wasn't possible.)
不,不是< /a>:
No, it is not:
除非是抽象的,否则这是不可能的,原因如下:
LongNamedRestaurant
也是一个Place
,不仅作为一个类,而且在数据库中。地点表包含每个纯Place
和每个LongNamedRestaurant
的条目。LongNamedRestaurant
只是创建一个带有food_type
的额外表和对 place 表的引用。如果您执行
Place.objects.all()
,您还会获得作为LongNamedRestaurant
的每个地点,并且它将是Place
的实例(没有food_type
)。因此,Place.name
和LongNamedRestaurant.name
共享相同的数据库列,因此必须具有相同的类型。我认为这对于正常模型来说是有意义的:每个餐厅都是一个地方,并且至少应该拥有该地方所拥有的一切。也许这种一致性也是为什么 1.10 之前的抽象模型不可能的原因,尽管它不会出现数据库问题。正如@lampslave 所说,它在 1.10 中成为可能。我个人建议小心:如果 Sub.x 覆盖 Super.x,请确保 Sub.x 是 Super.x 的子类,否则 Sub 不能用来代替 Super。
解决方法:您可以创建一个自定义用户模型 (
AUTH_USER_MODEL
),如果您只需要更改电子邮件字段,该模型会涉及大量代码重复。或者,您可以保留电子邮件原样,并确保所有表格都需要它。如果其他应用程序使用它,这并不能保证数据库的完整性,并且反之亦然(如果您想让用户名不需要)。That is not possible unless abstract, and here is why:
LongNamedRestaurant
is also aPlace
, not only as a class but also in the database. The place-table contains an entry for every purePlace
and for everyLongNamedRestaurant
.LongNamedRestaurant
just creates an extra table with thefood_type
and a reference to the place table.If you do
Place.objects.all()
, you also get every place that is aLongNamedRestaurant
, and it will be an instance ofPlace
(without thefood_type
). SoPlace.name
andLongNamedRestaurant.name
share the same database column, and must therefore be of the same type.I think this makes sense for normal models: every restaurant is a place, and should have at least everything that place has. Maybe this consistency is also why it was not possible for abstract models before 1.10, although it would not give database problems there. As @lampslave remarks, it was made possible in 1.10. I would personally recommend care: if Sub.x overrides Super.x, make sure Sub.x is a subclass of Super.x, otherwise Sub cannot be used in place of Super.
Workarounds: You can create a custom user model (
AUTH_USER_MODEL
) which involves quite a bit of code duplication if you only need to change the email field. Alternatively you can leave email as it is and make sure it's required in all forms. This doesn't guarantee database integrity if other applications use it, and doesn't work the other way around (if you want to make username not required).请参阅https://stackoverflow.com/a/6379556/15690:
See https://stackoverflow.com/a/6379556/15690:
我的解决方案与下一个
monkey patching
一样简单,请注意我如何更改LongNamedRestaurant
模型中name
字段的max_length
属性:My solution is as simple as next
monkey patching
, notice how I changedmax_length
attribute ofname
field inLongNamedRestaurant
model:将您的代码粘贴到一个新的应用程序中,将应用程序添加到 INSTALLED_APPS 并运行syncdb:
看起来 Django 不支持这一点。
Pasted your code into a fresh app, added app to INSTALLED_APPS and ran syncdb:
Looks like Django does not support that.
这段超酷的代码允许您“覆盖”抽象父类中的字段。
当这些字段从抽象父类中删除后,您可以根据需要自由地重新定义它们。
这不是我自己的作品。来自此处的原始代码: https://gist.github.com/specialunderwear/9d917ddacf3547b646ba
This supercool piece of code allows you to 'override' fields in abstract parent classes.
When the fields have been removed from the abstract parent class you are free to redefine them as you need.
This is not my own work. Original code from here: https://gist.github.com/specialunderwear/9d917ddacf3547b646ba
也许你可以处理contribute_to_class:
Syncdb工作正常。我没有尝试这个例子,在我的例子中,我只是覆盖一个约束参数,所以......等待&看 !
Maybe you could deal with contribute_to_class :
Syncdb works fine. I dont tried this example, in my case I just override a constraint parameter so ... wait & see !
我知道这是一个老问题,但我遇到了类似的问题并找到了解决方法:
我有以下类:
但我希望需要 Year 的继承图像字段,同时保持超类的图像字段可为空。最后,我使用 ModelForms 在验证阶段强制执行图像:
admin.py:
看来这仅适用于某些情况(当然,您需要在子类字段上强制执行更严格的规则)。
或者,您可以使用
clean_()
方法代替clean()
,例如,如果需要填充字段town
在:I know it's an old question, but i had a similar problem and found a workaround:
I had the following classes:
But I wanted Year's inherited image-field to be required while keeping the image field of the superclass nullable. In the end I used ModelForms to enforce the image at the validation stage:
admin.py:
It seems this is only applicable for some situations (certainly where you need to enforce stricter rules on the subclass field).
Alternatively you can use the
clean_<fieldname>()
method instead ofclean()
, e.g. if a fieldtown
would be required to be filled in:您不能覆盖模型字段,但可以通过覆盖/指定 clean() 方法轻松实现。我遇到了电子邮件字段的问题,希望使其在模型级别上唯一,并这样做:
然后,错误消息由名称为“email”的表单字段捕获
You can not override Model fields, but its easily achieved by overriding/specifying clean() method. I had the issue with email field and wanted to make it unique on Model level and did it like this:
The error message is then captured by Form field with name "email"