子类 Django ModelBase(Django 模型的元类)
Posted
技术标签:
【中文标题】子类 Django ModelBase(Django 模型的元类)【英文标题】:Subclass Django ModelBase (metaclass for Django models) 【发布时间】:2016-02-21 01:41:48 【问题描述】:我希望我的一些 Django 模型具有“所有者”属性。稍后我可能需要更改或扩充逻辑,并且该逻辑在许多类中被重用。所以我想从一个Owned
类继承,让我存储创建该类的用户。我还没有尝试填充该字段,我只是需要它存在。
首先我尝试了这个:
from django.db import models
from django.contrib.auth.models import User
class Owned(models.Model):
owner = models.ForeignKey(User, related_name='owner')
class Meta:
abstract = True
但是当我在几个子类中从 Owned
继承时,我得到了一个 Django 反向访问器错误:Django Reverse Accessor ***es
看起来这个“所有者”属性需要在 Owned
类的子类中具有不同的“related_name”。
所以我尝试了这个:
from django.db import models
from django.db.models.base import ModelBase
from django.contrib.auth.models import User
class _OwnedMeta(ModelBase):
'''
Should makes "Owned" class below work.
Gets around problem with reverse accessor ***es:
'''
def __init__(cls, name, bases, dct):
related_name = '_owner'.format(name)
dct['owner'] = models.ForeignKey(User, related_name=related_name)
super(_OwnedMeta, cls).__init__(name, bases, dct)
class Owned(models.Model):
'''
Instances get an "owner" attribute
that is a foreign key to '<class_name>_owner'
'''
__metaclass__ = _OwnedMeta
owner = models.ForeignKey(User, related_name='owner')
class Meta:
abstract = True
这个想法是,当我将Owned
子类化时,我将获得一个具有相关名称*class_name*_owner
的owner
属性。
像这样:
Class Subclass(Owned):
pass
instance = Subclass()
现在,如果这可行,instance.subclassed 将是 Django User
模型的外键,related_name
将是“Subclass_owner”。
但它不起作用。这是错误消息的摘录:
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/base.py", line 297, in add_to_class
value.contribute_to_class(cls, name)
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 1588, in contribute_to_class
super(ForeignObject, self).contribute_to_class(cls, name, virtual_only=virtual_only)
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 272, in contribute_to_class
add_lazy_relation(cls, self, other, resolve_related_class)
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 84, in add_lazy_relation
operation(field, model, cls)
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 271, in resolve_related_class
field.do_related_class(model, cls)
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 307, in do_related_class
self.set_attributes_from_rel()
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 304, in set_attributes_from_rel
self.rel.set_field_name()
File "/Users/maxwellheiber/dev/dc/lib/python2.7/site-packages/django/db/models/fields/related.py", line 1259, in set_field_name
self.field_name = self.field_name or self.to._meta.pk.name
AttributeError: 'NoneType' object has no attribute 'name'
我做错了什么?
【问题讨论】:
【参考方案1】:实际上,django 完全解决了您的问题(具有一个外键和一个抽象类的相关名称)!请查看文档@@https://docs.djangoproject.com/en/1.8/topics/db/models/#be-careful-with-related-name
从那里复制以确保答案的完整性:
如果您在 ForeignKey 或 ManyToManyField 上使用 related_name 属性,则必须始终为字段指定唯一的反向名称。这通常会在抽象基类中引起问题,因为此类上的字段包含在每个子类中,并且每次的属性值(包括相关名称)完全相同。
要解决此问题,当您在抽象基类中使用related_name 时(仅),部分名称应包含
'%(app_label)s'
和'%(class)s'
。
'%(class)s'
替换为使用该字段的子类的小写名称。'%(app_label)s'
替换为包含子类的应用程序的小写名称。每个安装的应用程序名称必须是唯一的,并且每个应用程序中的模型类名称也必须是唯一的,因此生成的名称最终会有所不同。
因此,例如在您的情况下,只需将 Owned
更改为:
【讨论】:
以上是关于子类 Django ModelBase(Django 模型的元类)的主要内容,如果未能解决你的问题,请参考以下文章