看起来我已经找到了基于抽象类的类方法的解决方案,它通过contribute_to_class
方法创建适当的字段,并由class_prepared
信号启动。
它的灵感来自于以下文章:Django Model Field Injection,在某些情况下,Django application import and missed class_prepared signals文章可能有用,但在我的特殊情况下,它并不相关。
因此,解决方案是定义在抽象类中的方法,将创建正确的领域:
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
信号来实现。根据documentation,放置它的最佳位置在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测试,并且在运行时的服务器,该模型的测试,并与制备迁移时的行为等同于通常定义的硬编码的场3210。