Как добавить пользовательский менеджер в модель стороннего приложения

Я добавил поле is_active во все свои модели для добавления функций мягкого удаления и добавил пользовательский менеджер для извлечения активных объектов.

class SoftDeleteManager(models.Manager):
    def get_queryset(self):
        return super(SoftDeleteManager, self).get_queryset().filter(is_active=True)


class Student(models.Model):
    is_active = models.BooleanField(default=True)
    active_objects = SoftDeleteManager()
    objects = models.Manager()

Кроме того, я хочу добавить тот же менеджер active_objects и к модели auth.User, но, поскольку эта модель не определена в моей кодовой базе, я не знаю, что делать дальше.


person SHIVAM JINDAL    schedule 09.02.2020    source источник
comment
Вы должны использовать настраиваемую пользовательскую модель, даже если вы не меняете никакие поля модели. См. здесь, как это сделать. Надеемся, что это не работающее приложение, и вы все еще можете начать все заново с пользовательской моделью пользователя (изменить модель пользователя в середине проекта сложно). Для других сторонних приложений вы всегда можете создать подкласс модели, чтобы добавить в модель поле is_active и изменить менеджера по умолчанию или добавить дополнительного менеджера.   -  person dirkgroten    schedule 09.02.2020
comment
Обратите внимание, что вы должны быть немного осторожны, чтобы повторно использовать поле is_active модели User для обозначения мягкого удаления. В некоторых случаях is_active равно False, пока пользователь не подтвердит свой адрес электронной почты. Или пользователи могут быть заблокированы из-за подозрительной активности, где is_active также может быть установлено на False. Поэтому интерпретация этого как удаленного может быть не тем, что вы хотите.   -  person dirkgroten    schedule 09.02.2020
comment
@dirkgroten Спасибо, но я ищу универсальное решение для добавления пользовательского менеджера в любое стороннее приложение. Вышеприведенный вариант использования является просто примером.   -  person SHIVAM JINDAL    schedule 09.02.2020
comment
Что ж, User — это особый случай, поэтому это не лучший пример стороннего приложения. Для любой другой модели достаточно подкласса если в модели нет поля is_active (поскольку вам нужно добавить таблицу с этим полем) или если в модели уже есть поле is_active, используйте прокси-модель.   -  person dirkgroten    schedule 09.02.2020


Ответы (1)


Предположение: вы хотите мягко удалить любую стороннюю модель, в которой нет поля is_active. И это не должна быть модель auth.User, потому что это особый случай.

Я бы сначала создал класс абстрактной модели для реализации мягкого удаления:

class SoftDeleteModel(models.Model):
    is_active = models.BooleanField(default=True)

    active_objects = SoftDeleteManager()
    objects = models.Manager()

    class Meta:
        abstract = True

Теперь предположим, что у вас есть стороннее приложение с моделью Message, тогда внутри вашего приложения вы можете создать его подкласс:

from 3rdparty.models import Message as ExtMessage
from .models import SoftDeleteModel

class Message(SoftDeleteModel, ExtMessage):
    pass

Теперь у вашего собственного класса Message есть дополнительное поле и менеджер active_objects. Вам потребуется makemigrations и migrate, потому что для Message будет создана новая таблица.

person dirkgroten    schedule 09.02.2020
comment
Ваше решение выглядит хорошо, но я ищу решение, которое добавляет пользовательский менеджер в стороннее приложение без добавления нового поля в существующую модель. - person SHIVAM JINDAL; 13.02.2020
comment
Ну тогда создай прокси модель и поставь кастом менеджер на прокси модель. - person dirkgroten; 13.02.2020
comment
но как изменится поведение кода стороннего приложения? - person SHIVAM JINDAL; 13.02.2020
comment
Что вы имеете в виду под «изменить поведение кода стороннего приложения»? В своем вопросе вы сказали, что все, что вам нужно, это дополнительный менеджер, который фильтрует is_active. Если вы создаете экземпляр объекта из прокси, он будет вести себя как сторонняя модель, но будет иметь дополнительный менеджер. Вам просто нужно убедиться, что вы всегда используете прокси в своем собственном коде. - person dirkgroten; 13.02.2020
comment
допустим, стороннее приложение использует метод менеджера по умолчанию, и вы переопределили его в своем пользовательском менеджере. и когда стороннее приложение вызывает field._default_manager.some_function, оно будет вызывать функцию по умолчанию, а не вашу. Это уже сделано в коде десериализации Django, поэтому я и спрашиваю. - person SHIVAM JINDAL; 13.02.2020
comment
В своем вопросе вы прямо написали, что не хотите заменить менеджера по умолчанию, а хотите добавить нового менеджера. Но если вы хотите, вы также можете заменить менеджера по умолчанию в вашей модели прокси. Затем, если стороннее приложение вызовет MyModel._meta.default_manager()...., оно будет использовать тот, который вы определили. Я не знаю, что вы подразумеваете под кодом десериализации Django. - person dirkgroten; 13.02.2020
comment
Было бы полезно, если бы ваш вопрос был более конкретным. Сообщите нам, какое именно стороннее приложение и какую модель вы хотите изменить в поведении. Сообщите нам, какой метод/поведение стороннего приложения следует переопределить. Тогда мы сможем лучше понять, почему наследование модели не работает/работает. - person dirkgroten; 13.02.2020
comment
Мне нужно попробовать какой-то код для указания всего здесь. Но ваш последний комментарий действительно помог. - person SHIVAM JINDAL; 13.02.2020