子类 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*_ownerowner 属性。

像这样:

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 更改为:

拥有的类(模型。模型): owner = models.ForeignKey(User, related_name='%(app_label)s_%(class)s_owner') 元类: 摘要 = 真

【讨论】:

以上是关于子类 Django ModelBase(Django 模型的元类)的主要内容,如果未能解决你的问题,请参考以下文章

无法在 django 中解压不可迭代的 ModelBase 对象

Djang DJANGO_SETTINGS_MODULE

Django框架-- Djang与Ajax

创建djang+vue项目

获取 django 模型的类名

Djang视图层