Замінити набір запитів за замовчуванням у адміністратора Django


79

Одна з моїх моделей має видалений прапор, який використовується для глобального приховування об’єктів:

class NondeletedManager(models.Manager):
    """Returns only objects which haven't been deleted"""

    def get_query_set(self):
        return super(NondeletedManager, self).get_query_set().exclude(deleted=True)

class Conversation(BaseModel):
    ...
    deleted = models.BooleanField(default=False)
    objects = NondeletedManager()
    all_conversations = models.Manager() # includes deleted conversations

Як я можу замінити набір запитів за замовчуванням, який використовується адміністративним модулем Django для включення видалених розмов?


Вам справді потрібні власні менеджери для цих простих запитів?
Сезар,

3
Так, видалені об’єкти слід ігнорувати універсально (за винятком сторінок адміністратора), тому має сенс встановити за замовчуванням.
Натан Єллін

Відповіді:


144

Ви можете замінити get_queryset метод у своєму класі адміністратора моделі.

class MyModelAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super(MyModelAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

Зверніть увагу, що в Django <= 1,5 метод був названий просто queryset.


3
Як би це працювало у цьому випадку? Чи можу я змінити набір запитів, створений, ModelAdmin.querysetдля включення видалених об'єктів? Я не хочу самостійно створювати набір запитів, замість того, щоб звертатися до суперкласу.
Натан Єллін,

Подивіться на мою відповідь, щоб зрозуміти, що я маю на увазі. Чи існує альтернатива повній повторній реалізації функції?
Натан Єллін,

4
Це допомагає поставити відповідь у своїй відповіді, а не просто посилатись. Зараз це посилання мертве, тому я оновлю, щоб дати пояснення.
Dan


1
Я не зміг отримати цю роботу. Я отримую повідомлення про помилку із заявою, що .filter немає в об'єкті qs. По-друге, я також не можу використовувати набір запитів ні для чого, крім запиту. stackoverflow.com/questions/54472649/… Будь-яка допомога?
Gary

9

Конрад правильний, але це складніше, ніж приклад, наведений у документації.

Видалені бесіди не можуть бути включені до набору запитів, який уже їх виключає. Отже, я не бачу іншого варіанту, ніж повторна реалізація admin.ModelAdmin.queryset.

class ConversationAdmin (admin.ModelAdmin):

    def queryset (self, request):
        qs = Conversation.all_conversations
        ordering = self.get_ordering(request)
        if ordering:
            qs = qs.order_by(*ordering)
        return qs

1
Я не думаю, що в цьому є щось погане. Використання двох менеджерів - це шлях. Однак правда, що адміністратор Django міг забезпечити гачок, так що вам не доведеться повторно реалізовувати частину замовлення.
Thomas Orozco,

3

Що може бути не так із наступним:

class Conversation(BaseModel):
    ...
    deleted = models.BooleanField(default=False)
    objects = models.Manager() # includes deleted conversations
    nondeleted_conversations = NondeletedManager()

Тож у своїх власних додатках / проектах ви використовуєте Conversation.nondeleted_conversations()і дозволяєте вбудованому додатку адміністратора робити це.


1
Я ігнорую видалені об'єкти скрізь, крім сторінок адміністратора, тому вважаю, що це має бути за замовчуванням. Крім того, таким чином мені не потрібно оновлювати застарілий код, додаючи можливість видалення розмов.
Натан Єллін

2

Прийняте рішення чудово працює для мене, але мені потрібна була трохи більша гнучкість, тому в підсумку я розширив подання списку змін, щоб додати власний параметр набору запитів. Тепер я можу налаштувати свій набір запитів / фільтр за замовчуванням як такий, і його все ще можна змінити, використовуючи інший фільтр (отримати параметри):

def changelist_view(self, request, extra_context=None):
    if len(request.GET) == 0 :
        q = request.GET.copy()
        q['status__gt'] = 4
        request.GET = q
        request.META['QUERY_STRING'] = request.GET.urlencode()

    return super(WorksheetAdmin,self).changelist_view(request, extra_context=extra_context)

2

Це можна зробити за допомогою проксі-моделі Django .

# models.py
class UnfilteredConversation(Conversation):
    class Meta:
        proxy = True

    # this will be the 'default manager' used in the Admin, and elsewhere
    objects = models.Manager() 

# admin.py
@admin.register(UnfilteredConversation)
class UnfilteredConversationAdmin(Conversation):
    # regular ModelAdmin stuff here
    ...

Або, якщо у вас є існуючий клас ModelAdmin, який ви хочете використати повторно:

admin.site.register(UnfilteredConversation, ConversationAdmin)

Цей підхід дозволяє уникнути проблем, які можуть виникнути при перевизначенні менеджера за замовчуванням в оригінальній моделі розмови - оскільки менеджер за замовчуванням також використовується у відносинах ManyToMany та зворотних відносинах ForeignKey.


1

Натан Єллін правильний, але ви можете змінити порядок менеджерів, і перший буде типовим, тоді він використовується адміністратором:

class Conversation(BaseModel):
    ...
    deleted = models.BooleanField(default=False)

    all_conversations = models.Manager() # includes deleted conversations
    objects = NondeletedManager()

Реалізація адмін get_queryset()використання ._default_managerзамість .objects, як показано в наступному

qs = self.model._default_manager.get_queryset()

ref Реалізація Django github BaseModelAdmin

Це гарантує лише те, що кожного разу, коли ви використовуєте YourModel.objects, ви не включатимете видалені об'єкти, а загальні подання та інші використовує також ._default_manager. Тоді, якщо ви не перевизначите get_queryset, це не рішення. Я щойно перевірив ListView та адміністратора.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.