摘要在摘要Django模型中定义的外国钥匙参考模型



我有一个类似的抽象模型:

class Like(models.Model):
    TARGET_MODEL = 'TargetModel'
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    target = models.ForeignKey(TARGET_MODEL)
    class Meta:
        abstract = True

我想让每个子类中的TARGET_MODEL不同。例如,从blog应用程序引用Post模型的模型LikeForPost

class LikeForPost(Like):
    TARGET_MODEL = 'blog.Post'

它似乎不起作用,因为TARGET_MODEL不是从子类实例化的。实现这一目标的正确方法是什么?

我知道我可以重新定义LikeForPost类中的整个target字段,但我希望有一个更优雅的解决方案,可以仅覆盖模型名称。

看起来我已经找到了基于抽象类的类方法的解决方案,该类方法通过contribute_to_class方法创建了适当的字段,并且由class_prepared信号启动。

它的灵感来自以下文章:Django Model Field注入,在某些情况下,Django应用程序导入和错过的class_prebared Signals Artices文章可能很有用,但是在我的特别情况下,它并不重要。

因此,解决方案是在抽象类中定义一种将创建适当字段的方法:

class Like(models.Model):
    TARGET_MODEL = None  # will be overridden in subclass
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    @classmethod
    def on_class_prepared(cls):
        target_field = models.ForeignKey(cls.TARGET_MODEL)
        target_field.contribute_to_class(cls, 'target')
    class Meta:
        abstract = True

可以将子类不受欢迎:

class LikeForPost(Like):
    TARGET_MODEL = 'blog.Post'

为了使其正常运行,必须在创建LikeForPost类之后调用on_class_prepared()函数,这可以通过将其连接到Django的class_prepared信号来实现。根据文档,最好的位置是在AppConfig.__init__()方法中。因此,我们选择一个应该负责此设置的应用程序,在我的情况下是blog,然后将以下代码添加到blog/apps.py

from django.apps import AppConfig
from django.db.models import signals
def call_on_class_prepared(sender, **kwargs):
    """Calls the function only if it is defined in the class being prepared"""
    try:
        sender.on_class_prepared()
    except AttributeError:
        pass

class BlogConfig(AppConfig):
    name = 'blog'
    def __init__(self, app_name, app_module):
        super(BlogConfig, self).__init__(app_name, app_module)
        # Connect programmatic class adjustment function to the signal
        signals.class_prepared.connect(call_on_class_prepared)

并默认使此配置处于活动状态,请在blog/__init__.py中进行配置:

default_app_config = 'blog.apps.BlogConfig'

该解决方案在Django 1.10中进行了测试,并且在运行服务器时与正常定义的硬编码字段相同,在使用manage.py makemigrations进行迁移时。

Stephen McDonald的本文描述了@bartonaz的答案方法,称为 django模型字段注入,并在上提供了更多上下文其他征用缺点

一种方法是实现尽可能多的模型类,就像抽象基类一样,以便用户可以用自己的模型将这些类别分类。对于某些类型的自定义,这种方法是有意义的,例如,这就是我对Django-Forms-Builder所做的。但是,这种方法存在一些警告。首先,无法在抽象模型上定义关系字段,因此需要在同一应用程序中的具体模型或实现自己的子类中实现这些字段。其次,任何引用您的模型的功能,例如视图或中间件,都需要具有可配置的设置以选择要使用的模型,或者完全由用户重新进化以使用其自定义字段。

另一种方法是简单地建议用户使用多桌继承提供该应用程序提供的模型。不幸的是,这将引入不必要的开销,并在访问子类实例时需要额外的数据库查询。最好的情况是,在处理单个实例的视图中,这相当于一个或两个查询。最坏的情况是,当模板中的QuerySet与QuerySet一起使用时,每个返回的实例都会执行额外的查询 - 经典的N 1查询问题。

并精确此 django模型场注入的确切目标

该方法归结为三个概念:

  • 动态添加字段到模型类
  • 确保Django的模型系统尊重新领域
  • 使上面的负载订购正确以使上述工作

最新更新