Im admin Tool list_filter ordnen

Django, Flask, Bottle, WSGI, CGI…
Antworten
Pitwheazle
User
Beiträge: 909
Registriert: Sonntag 19. September 2021, 09:40

Zwischenzeitlich habe ich ja schon viele Einträge in meiner Datenbank. Ich kann diese im admin Tool filtern und auch ordnen. Ich kann aber die Filter nicht ordnen. Wenn ich den Filter "user" aufrufe, kommen diese wild durcheinander (wahrscheinlich nach Datum der Anmeldung?). Ich habe keine Möglichkeit gefunden (oder habe diese Möglichkeit in der Dokumentation nicht verstanden) - oder gibt es diese womöglich gar nicht?

Code: Alles auswählen

class ZaehlerAdmin(admin.ModelAdmin):
    list_filter=("user","kategorie",)
    ordering = ["user__vorname", "kategorie__zeile"]
    
class ProtokollAdmin(admin.ModelAdmin):
    list_filter=("user","kategorie",)
    list_display = ('name', 'kategorie', 'start') 
    ordering = ["user__vorname", "kategorie__zeile"]
Benutzeravatar
grubenfox
User
Beiträge: 447
Registriert: Freitag 2. Dezember 2022, 15:49

ungetestet: in der angezeigten Tabelle auf den Spaltenkopf, nach dem sortiert werden soll, klicken?
Benutzeravatar
noisefloor
User
Beiträge: 3882
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

unter Vorbehalt: wenn du die gefilterten Werte anders sortiert geliefert bekommen willst (Standardsortierung ist nach dem Primärschlüssel aufsteigend, wenn ich mich nicht irre), dann musst du dir einen eigenen Filter schreiben und dort das Queryset entsprechend definieren. Klingt kompliziert, ist es aber nicht wirklich. Siehe: https://docs.djangoproject.com/en/4.2/r ... listfilter

Gruß, noisefloor
Pitwheazle
User
Beiträge: 909
Registriert: Sonntag 19. September 2021, 09:40

Auf der Seite war ich schon mal - ich hatte gehofft, das ginge vielleicht einfacher. Ansonsten gibt es doch bei Django für fast alles fertige Lösungen. Danke! ... mal sehen, ob ich das geregelt bekomme :)
Benutzeravatar
grubenfox
User
Beiträge: 447
Registriert: Freitag 2. Dezember 2022, 15:49

Was kann jetzt eigentlich im admin-Tool nun gefiltert und sortiert werden und was kann nicht sortiert werden?
und gibt die Lookup-Syntax bei ordering überhaupt irgendeinen Sinn?

Was passiert denn bei

Code: Alles auswählen

class ZaehlerAdmin(admin.ModelAdmin):
    list_filter=("user","kategorie",)
    ordering = ["user", "kategorie"]
Ich habe leider kein laufendes Django hier um selbst das Verhalten von Django zu testen....
Pitwheazle
User
Beiträge: 909
Registriert: Sonntag 19. September 2021, 09:40

Die Einträge werden wunderbar nach "user" und "kategorie" geordnet. Der Filter ist durcheinander:
Bild
Benutzeravatar
grubenfox
User
Beiträge: 447
Registriert: Freitag 2. Dezember 2022, 15:49

Ah, ich glaube so langsam wird mir das Problem klar... und mich hatte das auch immer genervt.
Ich weiß nur nicht mehr ob ich das bis zum abschalten der Django-Anwendung zwangsweise so hingenommen hatte oder ob ich die Liste doch noch sortiert bekommen hatte.

Bei mir in `admin.py` habe ich kein `ordering`, aber hast du in den Model-Metadaten ein `ordering` definiert?

https://django.readthedocs.io/en/stable ... ta-options

https://django.readthedocs.io/en/stable ... l#ordering

Die Model-Klasse zu `ZaehlerAdmin` heißt auch `Zaehler`?

Code: Alles auswählen

class Zaehler(models.Model):
    # keine Ahnung was hier für Einträge für diverse Felder und Methoden stehen, die Frage ist ob am Ende eben `class Meta` steht

    class Meta:
        ordering = ['user']
Die ordering-Zeile könnte auch

Code: Alles auswählen

        ordering = ['vorname']
lauten. Ich bin da nicht sicher was da wie bei dir benannt ist.
Benutzeravatar
noisefloor
User
Beiträge: 3882
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

hab's mal bei meinem aktuellen Projekt ausprobiert. Eigene Filter schreiben ist doch ziemlich einfach:

Code: Alles auswählen

class CasthouseSortedListFilter(admin.SimpleListFilter):
    title = "casthouses sorted alphabetically"

    parameter_name = "sorted"

    def lookups(self, request, model_admin):
        return [
            ("sorted", ("sorted")),
               ]
    def queryset(self, request, queryset):
        if self.value()=='sorted':
            return queryset.order_by('name')

class CasthouseAdmin(admin.ModelAdmin):
    list_filter = [CasthouseSortedListFilter]
    search_fields = ['country']
    search_help_text = 'Search by ISO 3166-1 alpha-2 country code (e.g. DE for Germany or AE for UAE)'
Hier kann man die Einträge in der DB für das "Casthouse" Modell in Adminbackend alphabetisch sortieren lassen, nach dem Feld "name" aufsteigend. Standardsortierung ist ansonsten nach der PK absteigend.

Gruß, noisefloor
Pitwheazle
User
Beiträge: 909
Registriert: Sonntag 19. September 2021, 09:40

Zwischenzeitlich hatte ich chatGPT mal mit dem Problem beschäftigt. Interessiert es auch, was (er/sie/es/non) dazu sagt?
Der letzte Vorschlag:
Es tut mir leid für das Missverständnis. Sie haben recht, die Reihenfolge, die Sie im ordering-Attribut festlegen, wirkt sich auf die Anzeige der Objekte aus, nicht auf die Anzeige der Filter. Das Sortieren der Filter selbst ist in Django standardmäßig nicht direkt unterstützt.

Wenn Sie die Reihenfolge der Filter im Dropdown-Menü ändern möchten, ist dies eine etwas komplexere Anpassung, die über die Standardfunktionen von Django hinausgeht. Sie könnten diese Anpassung erreichen, indem Sie das Template für den Admin-Bereich überschreiben und die Filter manuell sortieren. Hier ist eine allgemeine Anleitung, wie Sie vorgehen könnten:

Erstellen Sie eine Datei templates/admin/filter.html in Ihrem Projekt, wenn sie noch nicht vorhanden ist.

In dieser Datei können Sie die Filter manuell sortieren, indem Sie eine benutzerdefinierte Schleife verwenden:

html

{% load i18n admin_urls %}
{% for spec in filter_specs %}
<div class="grp-module">
<h2>{{ spec.title }}</h2>
<ul class="grp-module-content">
{% if spec.choices %}
{% for choice in spec.choices %}
<li>
<a href="{{ spec.get_query_string(choice) }}" class="{% if choice.selected %}selected{% endif %}">
{{ choice.display }}
</a>
</li>
{% endfor %}
{% endif %}
</ul>
</div>
{% endfor %}

Jetzt können Sie die Methode extra_context in Ihrem ZaehlerAdmin verwenden, um das von Ihnen erstellte Template einzubinden:

python

class ZaehlerAdmin(admin.ModelAdmin):
list_filter = ("user", "kategorie",)
ordering = ["user__vorname", "kategorie__zeile"]

def get_filters(self, request):
filters = super().get_filters(request)
sorted_filters = sorted(filters.items(), key=lambda f: f[0])
return dict(sorted_filters)

def changelist_view(self, request, extra_context=None):
if extra_context is None:
extra_context = {}
extra_context['title'] = "Filter"
extra_context['user_filter'] = self.get_filters(request)['user']
return super().changelist_view(request, extra_context=extra_context)

def get_template_list(self, request):
return ["admin/filter.html"] + super().get_template_list(request)

Dieser Ansatz fügt eine benutzerdefinierte Filteranzeige hinzu, in der die Filter manuell sortiert sind. Bitte beachten Sie, dass dies eine fortgeschrittene Anpassung ist, und Sie möglicherweise weitere Anpassungen und Tests durchführen müssen, um sie an Ihre spezifischen Anforderungen anzupassen.

Denken Sie daran, die Django-Dokumentation zu konsultieren und den Code entsprechend Ihrer Django-Version und Modellstruktur anzupassen.
Ich habe ihm/ihr dann mitgeteilt, dass ich eine Fehlermeldung "'super' object has no attribute 'get_filters'" erhalte.
Die Antwort:
Es tut mir leid für das Missverständnis. Der Fehler tritt auf, weil es tatsächlich keine Methode namens get_filters in der ModelAdmin-Klasse gibt. Ich habe einen Fehler gemacht, und ich entschuldige mich dafür. Das war mein Fehler.

Die Filter im Admin-Bereich zu sortieren, ist leider keine einfache Aufgabe, da die Admin-Schnittstelle von Django diese Funktionalität standardmäßig nicht unterstützt. Es erfordert erweitertes Wissen in Django und möglicherweise umfangreiche Anpassungen im Admin-Interface.

In vielen Fällen könnte die beste Vorgehensweise sein, mit den standardmäßigen Filteroptionen zu arbeiten und darauf zu achten, die Namen der Optionen so zu wählen, dass sie leicht zu erkennen und zu sortieren sind.

Wenn das Sortieren der Filter für Ihre Anwendung von entscheidender Bedeutung ist, könnte es sich lohnen, in Betracht zu ziehen, benutzerdefinierte Ansichten und Filter-Optionen außerhalb des Django-Admin-Interfaces zu erstellen, um die gewünschte Funktionalität zu erreichen. Dies würde mehr Aufwand erfordern, bietet Ihnen jedoch die Flexibilität, die Sie benötigen.

Nochmals Entschuldigung für die Verwirrung, und vielen Dank für Ihr Verständnis.
Jetzt probiere ich mal @noisefloor s Vorschlag aus.
Pitwheazle
User
Beiträge: 909
Registriert: Sonntag 19. September 2021, 09:40

@noisefloor: Das mit "search_fields = ..." habe ich übernommen, das reicht eigentlich für das was ich benötige. Aber das Sortieren der Namen im Filter bekomme ich nicht hin. Ich bekomme unter Filter die Anzeige "alle" und "sortiert" - aber es passiert nichts wenn ich draufklicke. Denke dran, dass ich nach wie vor nicht wirklich Ahnung habe, was ich da tue. Ich habe es folgendermaßen abgeändert:

Code: Alles auswählen

class UserSortedListFilter(admin.SimpleListFilter):
    title = "User sortiert"
    parameter_name = "sorted"

    def lookups(self, request, model_admin):
        return [
            ("sorted", ("sortiert")),
               ]
    def queryset(self, request, queryset):
        if self.value()=='sortiert':
            return queryset.order_by('user__vorname')

class ZaehlerAdmin(admin.ModelAdmin):
    list_filter = [UserSortedListFilter]
    search_fields = ['user__vorname', 'user__nachname']
    #list_filter=("user","kategorie",)
    ordering = ["user__vorname", "kategorie__zeile"]
"request" und "model_admin" ist jeweils ausgegraut - was fehlt da bei mir?
Benutzeravatar
noisefloor
User
Beiträge: 3882
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wie ist denn deine zugehörige Klasse in models.py aufgebaut? Du willst in der Klasse "Zähler" suchen und die Klasse eine Fremdschlüsselbeziehung zur Klasse "User"?

Gruß, noisefloor
Benutzeravatar
noisefloor
User
Beiträge: 3882
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

so, gerade mal getestet: das funktioniert schon.

models.py:

Code: Alles auswählen

class Casthouse(models.Model):
    name = models.CharField(max_length=50, null=False, blank=False)
    ...

class CastAlloy(models.Model):
    casthouse = models.ForeignKey('Casthouse', related_name='alloys',
                                  on_delete=models.CASCADE)
    ...
admin.py:

Code: Alles auswählen

class CastAlloySortedListFilter(admin.SimpleListFilter):
    title = "Results sorted alphabetically"

    parameter_name = "sorted"

    def lookups(self, request, model_admin):
        return [
            ("sorted", ("sorted")),
               ]
    def queryset(self, request, queryset):
        if self.value()=='sorted':
            return queryset.order_by('casthouse__name')

class CastAlloyAdmin(admin.ModelAdmin):
    list_filter = [CastAlloySortedListFilter]
Ohne Filter wird die Liste wie erwartet absteigend nach dem PK sortiert, im Filter alphabetisch aufsteigend nach dem Name.

Gruß, noisefloor
Pitwheazle
User
Beiträge: 909
Registriert: Sonntag 19. September 2021, 09:40

Sehe ich das richtig, dass für diese Sortierung ein eine eigene Tabelle

Code: Alles auswählen

class CastAlloy(models.Model):
    casthouse = models.ForeignKey('Casthouse', related_name='alloys',
                                  on_delete=models.CASCADE)
angelegt wird? Oder entspricht das meinen Tabellen "Zähler" und "user (profil)"?
Benutzeravatar
noisefloor
User
Beiträge: 3882
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

nee, CastAlloy enthält die Daten, welche Legierung(en) die Gießerei gießt. Die Modelle haben schon noch ein paar mehr Attribute. Die habe ich hier nur weggeschnitten, weil für das Problem eigentlich nicht relevant.

Die ganze Klasse sieht so aus:

Code: Alles auswählen

class CastAlloy(models.Model):
    casthouse = models.ForeignKey('Casthouse', related_name='alloys',
                                  on_delete=models.CASCADE)
    alloy = models.CharField(max_length=14, choices=ALLOYS)
    remark = models.CharField(max_length=200, default='(no remarks)')

    def __str__(self):
        return f'{self.alloy} is casted by {self.casthouse.name}'

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['casthouse', 'alloy'],
                                    name='unique_alloy'),]
Gruß, noisefloor
Antworten