Django 模型:从独立应用程序中定义的 类 动态继承
Django models: dynamic inheritance from classes defined in standalone app
我正在使用 Django 3.2
我写了一个独立的应用程序 social
,它的模型定义如下:
from abc import abstractmethod
class ActionableModel:
# This MUST be implemented by ALL CONCRETE sub classes
@abstractmethod
def get_actionable_object(self):
pass
# This MUST be implemented by ALL CONCRETE sub classes
@abstractmethod
def get_owner(self):
pass
def get_content_info(self):
actionable_object = self.get_actionable_object()
ct = ContentType.objects.get_for_model(actionable_object)
object_id = actionable_object.id
return (ct, object_id)
# ...
class Likeable(models.Model, ActionableModel):
likes = GenericRelation(Like)
likes_count = models.PositiveIntegerField(default=0, db_index=True)
last_liked = models.DateTimeField(editable=False, blank=True, null=True, default=None)
# ...
在我的项目(使用独立应用程序)中,我有以下代码:
myproject/userprofile/apps.py
class UserprofileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'userprofile'
verbose_name = _('User Profile')
def ready(self):
# Check if social app is installed
from django.apps import apps
if apps.is_installed("social"):
# check some condition
# inherit from social.Likeable
# ... ?
如何动态地让我的模型 userprofile.models.UserAccount
从 Likeable 派生?
如果我对要求的理解正确 - 您需要应用程序 userprofile
中的模型来实现 Likeable
功能。
因此这种情况下的继承不仅允许使用已实现的方法,而且还使其在数据库中具有相同的公共字段(likes
、likes_count
等)
这意味着这样的继承还需要生成迁移脚本(以在数据库中添加必要的字段)。因此,甚至可能无法在应用程序准备就绪时在运行时执行此操作,因为您必须为迁移定义模型。
然而,即使有一种方法可以实现这种“动态”继承 on-the-fly - 我强烈建议不要采用这种方式,因为当模型的某些功能以这种方式定义时,它确实不透明一个隐藏的方式。
因此,针对您的请求的好的解决方案很简单 - 只是不要动态执行。即:
myproject/userprofile/models.py
from social.models import Likeable
class UserAccount(Likeable):
# the definition of the user account class
因此其他需要 Likeable
功能的模型将直接继承它。
关于
的问题
How can I dynamically get my model userprofile.models.UserAccount
可以通过以下方式完成:
from django.apps import apps
app_models = apps.get_app_config('userprofile').get_models()
作为动态继承的 follow-up 你可以参考这里 How to dynamically change base class of instances at runtime?
UPD - 解决问题的方法有点不同
而不是使用 类 的继承,这在这种情况下甚至可能是不可能的 - 可以通过与数据库中的 Likeable
实体的关系来扩展目标模型。
即而不是携带 Likeable
继承 - 它可以作为 one-to-one 关系提供,可以在运行时构建如下:
class UserprofileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'userprofile'
verbose_name = _('User Profile')
def ready(self):
# Check if social app is installed
from django.apps import apps
from django.db import models
if apps.is_installed("social"):
for obj in apps.get_app_config(self.name).get_models():
# add relationship
obj.add_to_class(
'likeable',
models.OneToOneField(Likeable, on_delete=models.SET_NULL, null=True
)
然后,可以通过 likeable
字段在 userprofile
的模型上访问 likeable 的方法。即:
u = UserAccount.objects.last()
u.likeable.get_content_info()
要动态继承另一个class,我们通常这样做:
def make_class(cls, cls_to_inherit):
return type(
cls.__name__,
(cls, cls_to_inherit),
{},
)
要动态继承另一个具有 metaclass=ModelBase
的 Django 模型,我们这样做:
def make_model(model, model_to_inherit):
model._meta.abstract = True
if model._meta.pk.auto_created:
model._meta.local_fields.remove(model._meta.pk)
return type(
model.__name__,
(model, model_to_inherit),
{'__module__': model.__module__},
)
用法:
class UserprofileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'userprofile'
verbose_name = _('User Profile')
def ready(self):
# Check if social app is installed
from django.apps import apps
if apps.is_installed("social"):
# check some condition
# inherit from social.models.Likeable
from social.models import Likeable
from . import models
models.UserAccount = make_model(models.UserAccount, Likeable)
您可以在 models.py
的 class 创建时间执行此操作,但它可能导致的问题多于解决的问题。
# myproject/userprofile/models.py
from django.db import models
from django.apps import apps
if apps.is_installed('someotherapp'):
from someotherapp.models import Likeable
BaseKlass = Likeable
else:
BaseKlass = models.Model
class UserAccount(BaseKlass):
username = models.CharField(max_length=64)
您无法控制已经从您的 AppConfig.ready
创建的模型 class 的继承。 models.py
中的模型已经在 Django 调用 ready
方法的时间点创建。 Django 的模型 metaclass,它执行基本操作以正确形成字段,仅在 class 创建时有效;模型创建后无法编辑。
我正在使用 Django 3.2
我写了一个独立的应用程序 social
,它的模型定义如下:
from abc import abstractmethod
class ActionableModel:
# This MUST be implemented by ALL CONCRETE sub classes
@abstractmethod
def get_actionable_object(self):
pass
# This MUST be implemented by ALL CONCRETE sub classes
@abstractmethod
def get_owner(self):
pass
def get_content_info(self):
actionable_object = self.get_actionable_object()
ct = ContentType.objects.get_for_model(actionable_object)
object_id = actionable_object.id
return (ct, object_id)
# ...
class Likeable(models.Model, ActionableModel):
likes = GenericRelation(Like)
likes_count = models.PositiveIntegerField(default=0, db_index=True)
last_liked = models.DateTimeField(editable=False, blank=True, null=True, default=None)
# ...
在我的项目(使用独立应用程序)中,我有以下代码:
myproject/userprofile/apps.py
class UserprofileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'userprofile'
verbose_name = _('User Profile')
def ready(self):
# Check if social app is installed
from django.apps import apps
if apps.is_installed("social"):
# check some condition
# inherit from social.Likeable
# ... ?
如何动态地让我的模型 userprofile.models.UserAccount
从 Likeable 派生?
如果我对要求的理解正确 - 您需要应用程序 userprofile
中的模型来实现 Likeable
功能。
因此这种情况下的继承不仅允许使用已实现的方法,而且还使其在数据库中具有相同的公共字段(likes
、likes_count
等)
这意味着这样的继承还需要生成迁移脚本(以在数据库中添加必要的字段)。因此,甚至可能无法在应用程序准备就绪时在运行时执行此操作,因为您必须为迁移定义模型。
然而,即使有一种方法可以实现这种“动态”继承 on-the-fly - 我强烈建议不要采用这种方式,因为当模型的某些功能以这种方式定义时,它确实不透明一个隐藏的方式。
因此,针对您的请求的好的解决方案很简单 - 只是不要动态执行。即:
myproject/userprofile/models.py
from social.models import Likeable
class UserAccount(Likeable):
# the definition of the user account class
因此其他需要 Likeable
功能的模型将直接继承它。
关于
的问题How can I dynamically get my model
userprofile.models.UserAccount
可以通过以下方式完成:
from django.apps import apps
app_models = apps.get_app_config('userprofile').get_models()
作为动态继承的 follow-up 你可以参考这里 How to dynamically change base class of instances at runtime?
UPD - 解决问题的方法有点不同
而不是使用 类 的继承,这在这种情况下甚至可能是不可能的 - 可以通过与数据库中的 Likeable
实体的关系来扩展目标模型。
即而不是携带 Likeable
继承 - 它可以作为 one-to-one 关系提供,可以在运行时构建如下:
class UserprofileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'userprofile'
verbose_name = _('User Profile')
def ready(self):
# Check if social app is installed
from django.apps import apps
from django.db import models
if apps.is_installed("social"):
for obj in apps.get_app_config(self.name).get_models():
# add relationship
obj.add_to_class(
'likeable',
models.OneToOneField(Likeable, on_delete=models.SET_NULL, null=True
)
然后,可以通过 likeable
字段在 userprofile
的模型上访问 likeable 的方法。即:
u = UserAccount.objects.last()
u.likeable.get_content_info()
要动态继承另一个class,我们通常这样做:
def make_class(cls, cls_to_inherit):
return type(
cls.__name__,
(cls, cls_to_inherit),
{},
)
要动态继承另一个具有 metaclass=ModelBase
的 Django 模型,我们这样做:
def make_model(model, model_to_inherit):
model._meta.abstract = True
if model._meta.pk.auto_created:
model._meta.local_fields.remove(model._meta.pk)
return type(
model.__name__,
(model, model_to_inherit),
{'__module__': model.__module__},
)
用法:
class UserprofileConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'userprofile'
verbose_name = _('User Profile')
def ready(self):
# Check if social app is installed
from django.apps import apps
if apps.is_installed("social"):
# check some condition
# inherit from social.models.Likeable
from social.models import Likeable
from . import models
models.UserAccount = make_model(models.UserAccount, Likeable)
您可以在 models.py
的 class 创建时间执行此操作,但它可能导致的问题多于解决的问题。
# myproject/userprofile/models.py
from django.db import models
from django.apps import apps
if apps.is_installed('someotherapp'):
from someotherapp.models import Likeable
BaseKlass = Likeable
else:
BaseKlass = models.Model
class UserAccount(BaseKlass):
username = models.CharField(max_length=64)
您无法控制已经从您的 AppConfig.ready
创建的模型 class 的继承。 models.py
中的模型已经在 Django 调用 ready
方法的时间点创建。 Django 的模型 metaclass,它执行基本操作以正确形成字段,仅在 class 创建时有效;模型创建后无法编辑。