Модели 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, который выполняет важные операции для корректного заполнения полей формы, работает только во время создания класса; вы не можете редактировать модель после ее создания.

Вернуться на верх