我希望我的一些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 Clashes
看起来这个“owner”属性需要在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 clashes:
'''
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
时,我将获得一个owner
属性,其相关名称为*class_name*_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'
我做错了什么?
答案 0 :(得分:2)
实际上,django解决完全你的问题(将一个带有related_name的外键带到一个抽象类)!请查看文档@ https://docs.djangoproject.com/en/1.8/topics/db/models/#be-careful-with-related-name
从那里复制以获得答案完整性:
如果在ForeignKey或ManyToManyField上使用related_name属性,则必须始终为该字段指定唯一的反向名称。这通常会导致抽象基类出现问题,因为此类中的字段包含在每个子类中,每次都具有完全相同的属性值(包括related_name)。
要解决此问题,当您在抽象基类(仅)中使用related_name时,名称的一部分应包含
'%(app_label)s'
和'%(class)s'
。
'%(class)s'
将替换为使用该字段的子类的低级名称。'%(app_label)s'
将替换为子类包含在其中的应用程序的低级名称。每个安装的应用程序名称必须是唯一的,并且每个应用程序中的模型类名称也必须是唯一的,因此结果名称最终会有所不同。
因此,例如在您的情况下,只需将Owned
更改为:
class Owned(models.Model): owner = models.ForeignKey(User, related_name='%(app_label)s_%(class)s_owner') class Meta: abstract = True