Überschreiben Sie das Standard-Abfrageset in Django admin

79

Eines meiner Modelle verfügt über ein gelöschtes Flag, mit dem Objekte global ausgeblendet werden:

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

Wie kann ich das vom Django-Verwaltungsmodul verwendete Standardabfrageset überschreiben, um gelöschte Konversationen einzuschließen?

Natan Yellin
quelle
Benötigen Sie wirklich benutzerdefinierte Manager für diese einfachen Abfragen?
César
3
Ja, gelöschte Objekte sollten allgemein ignoriert werden (außer auf Administrationsseiten), daher ist es sinnvoll, einen Standard festzulegen.
Natan Yellin

Antworten:

142

Sie können die get_queryset Methode in Ihrer Modelladministrationsklasse überschreiben .

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)

Beachten Sie, dass in Django <= 1.5 die Methode nur benannt wurde queryset.

Konrad Hałas
quelle
3
Wie würde das in diesem Fall funktionieren? Kann ich das von erstellte Abfrageset so ändern ModelAdmin.queryset, dass es gelöschte Objekte enthält? Ich möchte das Abfrageset nicht selbst erstellen, anstatt die Oberklasse aufzurufen.
Natan Yellin
Schauen Sie sich meine Antwort an, um zu sehen, was ich meine. Gibt es eine Alternative zur vollständigen Neuimplementierung der Funktion?
Natan Yellin
4
Es ist hilfreich, die Antwort tatsächlich in Ihre Antwort aufzunehmen, anstatt nur zu verknüpfen. Dieser Link ist jetzt tot, daher werde ich ihn aktualisieren, um eine Erklärung zu geben.
Dan
16
Auf Django 1.6, wurde diese Methode umbenannt zu get_queryset.
Fernando Macedo
1
Ich konnte das nicht zum Laufen bringen. Ich erhalte die Fehlermeldung, dass .filter im qs-Objekt nicht enthalten ist. Zweitens kann ich Queryset auch nur für Anfragen verwenden. stackoverflow.com/questions/54472649/… Hilfe?
Gary
8

Konrad ist richtig, aber dies ist schwieriger als das in der Dokumentation angegebene Beispiel.

Gelöschte Konversationen können nicht in ein Abfrageset aufgenommen werden, das sie bereits ausschließt. Daher sehe ich keine andere Option als die vollständige Neuimplementierung von 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
Natan Yellin
quelle
Ich glaube nicht, dass daran etwas falsch ist. Die Verwendung von zwei Managern ist der richtige Weg. Es ist jedoch wahr, dass der Django-Administrator einen Hook bereitstellen könnte, damit Sie den Bestellungsteil nicht erneut implementieren müssen.
Thomas Orozco
3

Was wäre mit folgendem so falsch:

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

In Ihren eigenen Apps / Projekten verwenden Conversation.nondeleted_conversations()Sie die integrierte Admin-App und lassen sie das tun.

Evan Porter
quelle
1
Ich ignoriere gelöschte Objekte überall außer auf den Administrationsseiten, daher denke ich, dass dies die Standardeinstellung sein sollte. Außerdem muss ich auf diese Weise keinen Legacy-Code aktualisieren, indem ich die Möglichkeit zum Löschen von Konversationen hinzufüge.
Natan Yellin
2

Die akzeptierte Lösung funktioniert gut für mich, aber ich brauchte etwas mehr Flexibilität, sodass ich die Änderungslistenansicht erweitert habe, um einen benutzerdefinierten Abfragesatzparameter hinzuzufügen. Ich kann jetzt meinen Standard-Abfragesatz / Filter als solchen konfigurieren und er kann weiterhin mithilfe eines anderen Filters geändert werden (Parameter abrufen):

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)
radtek
quelle
2

Sie können dies mit einem Django- Proxy-Modell tun .

# 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
    ...

Wenn Sie über eine vorhandene ModelAdmin-Klasse verfügen, möchten Sie diese erneut verwenden:

admin.site.register(UnfilteredConversation, ConversationAdmin)

Dieser Ansatz vermeidet Probleme, die beim Überschreiben des Standardmanagers im ursprünglichen Konversationsmodell auftreten können, da der Standardmanager auch in ManyToMany-Beziehungen und beim Umkehren von ForeignKey-Beziehungen verwendet wird.

zlovelady
quelle
1

Natan Yellin ist korrekt, aber Sie können die Reihenfolge der Manager ändern. Die erste ist die Standardeinstellung, dann wird sie vom Administrator verwendet:

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

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

Die Administrator-Implementierung der get_queryset()Verwendung ._default_managerstattdessen .objects, wie im Folgenden gezeigt

qs = self.model._default_manager.get_queryset()

ref Django Github BaseModelAdmin Implementierung

Dies stellt nur sicher, dass Sie bei jeder Verwendung von YourModel.objects keine gelöschten Objekte einschließen, sondern die generischen Ansichten und andere auch ._default_manager verwenden. Wenn Sie get_queryset nicht überschreiben, ist dies keine Lösung. Ich habe gerade eine ListView und einen Administrator überprüft.

AramirezMiori
quelle