Модели Django: динамическое наследование от классов, определенных в отдельном приложении
Я использую 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 и т.д.)
Это означает, что такое наследование также требует создания скрипта миграции (для добавления необходимых полей в базу данных). Таким образом, сделать это во время выполнения, когда приложение уже готово, может быть даже невозможно, так как для миграций должна быть определена модель.
Однако, даже если есть способ сделать такое "динамическое" наследование "на лету" - я бы настоятельно рекомендовал не идти этим путем, потому что это действительно не прозрачно, когда некоторые особенности моделей определяются таким скрытым способом.
Итак, хорошее решение для вашего запроса простое - просто не делайте это динамически. Т.е.:
myproject/userprofile/models.py
from social.models import Likeable
class UserAccount(Likeable):
# the definition of the user account class
И поэтому другие модели, которым требуются Likeable функции, будут просто наследоваться от него напрямую.
На ваш вопрос о
Как я могу динамически получить свою модель
userprofile.models.UserAccount
Это можно сделать через:
from django.apps import apps
app_models = apps.get_app_config('userprofile').get_models()
В качестве продолжения о динамическом наследовании вы можете обратиться сюда Как динамически менять базовый класс экземпляров во время выполнения?
Чтобы динамически унаследовать другой класс, мы обычно делаем:
def make_class(cls, cls_to_inherit):
return type(
cls.__name__,
(cls, cls_to_inherit),
{},
)
Чтобы динамически наследовать другую модель Django, которая имеет metaclass=ModelBase, мы делаем:
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, но это, вероятно, вызовет больше проблем, чем решит.
# 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. Модели в вашем models.py уже созданы к тому моменту, когда Django вызывает метод ready. Метакласс модели Django, который выполняет важные операции для корректного заполнения полей формы, работает только во время создания класса; вы не можете редактировать модель после ее создания.