Anzeige im Adminzugang als Tabelle

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

Bei den "Benutzern" sehe ich audf der Adminseite die Felder als tabelle und kann nach diesen Einträgen sortieren. Dort sehe ich aber nicht "joined_day" und "Last_login" und mir ist es auch nicht gelungen, sie dort anzuzeigen.
Ich habe eine OnetoOne Verknüpfung von "benutzer" zu "Profil" und kann mir dort das Startdatum und letzte Nutzung anzeigen lassen. So:

Code: Alles auswählen

    def __str__(self):
        return f"{self.vorname} {self.nachname}, {self.klasse}, {self.user.date_joined.date()}, {self.user.last_login.date()}"
Das ergibt aber keine Tabelle und ist nicht wirklich übersichtlich und kann auch nicht sortiert werden.
Bild
Wie mache ich das, dass ich die User nach Start bzw. letztem Login sortieren kann?
Benutzeravatar
grubenfox
User
Beiträge: 430
Registriert: Freitag 2. Dezember 2022, 15:49

wenn ich das Problem richtig verstehe, dann mit einer (neuen) von ModelAdmin abgeleiteten Klasse BenutzerAdmin,

Code: Alles auswählen

class BenutzerAdmin(admin.ModelAdmin):
    pass
die Klasse passend konfigurieren (u.a vielleicht mit fields)

Code: Alles auswählen

class BenutzerAdmin(admin.ModelAdmin):
    fields = ('vorname', 'nachname', 'klasse', 'startdatum', 'last_login') # je nach dem wie die Elemente (Felder bzw. Methoden) in der Klasse Benutzer heißen
und zusammen mit der Klasse Benutzer im Admin-Interface registrieren

Code: Alles auswählen

admin.site.register(Benutzer, BenutzerAdmin)
oder mit dem register Decorator.

Die Doku zur admin-site und den Möglichkeiten die admin-site umzubauen/anzupassen ist ja lang und ausführlich (mehr Möglichkeiten als ich mir immer merken kann).
Da ich zur Zeit kein laufendes Django zur Hand habe und daher nichts testen kann, sind die obigen Code-Teile nur aus der Doku kopiert und auf Verdacht angepasst. Also keine Garantie auf Funktion!
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

grubenfox hat geschrieben: Donnerstag 9. Februar 2023, 14:48 wenn ich das Problem richtig verstehe, dann mit einer (neuen) von ModelAdmin abgeleiteten Klasse BenutzerAdmin,
Die Doku zur admin-site und den Möglichkeiten die admin-site umzubauen/anzupassen ist ja lang und ausführlich (mehr Möglichkeiten als ich mir immer merken kann).
Da ich zur Zeit kein laufendes Django zur Hand habe und daher nichts testen kann, sind die obigen Code-Teile nur aus der Doku kopiert und auf Verdacht angepasst. Also keine Garantie auf Funktion!
Auweh, ich bin einfach zu blöd bzw. habe Probleme diese fachspezifische Ausdrucksweise der Dokumentation zu verstehen (wahrscheinlich liegt es auch daran, dass ich immer noch Probleme habe, die Geheimnisse der objektorientierten Programmierung zu verstehen) - ich habe aber (vor meinem Post schon versucht) diese Beschreibung in der Dokumentation zu lesen.
Ich möchte ja aber nicht eine neue Klasse erzeugen (wenn ich nicht muss), die gesuchten Felder sind im model "auth_user" ja schon drin, sie werden nur nicht angezeigt. Und in meinem model "Profile" kann ich sie ja wiederum anzeigen lassen, aber halt nicht in Spalten.
Benutzeravatar
grubenfox
User
Beiträge: 430
Registriert: Freitag 2. Dezember 2022, 15:49

Pitwheazle hat geschrieben: Donnerstag 9. Februar 2023, 15:27 habe Probleme diese fachspezifische Ausdrucksweise der Dokumentation zu verstehen
da sagst du was... und mein Dauerproblem mit vielen Dokumentationen: was Funktion A macht, was Methode B macht, ... was ich bei Konfiguration Z eintragen kann... das ist alles schön dokumentiert. Im Zweifel schön alphabetisch sortiert. Aber welche 3-5 Elemente von den vorhandenen 26 Stück für die üblichen Probleme X oder Y sinnvoll nutzbar sind, das findet man dann z.b. erst in externen Büchern in denen dass ganze Produkt von der "anderen" Seite aus betrachtet wird. So muss man sich erst mal durch alle 26 Optionen durch arbeiten bis man dann feststellen kann dass 21 davon gar nicht zum aktuellen Problem passen.
Pitwheazle hat geschrieben: Donnerstag 9. Februar 2023, 15:27 Ich möchte ja aber nicht eine neue Klasse erzeugen (wenn ich nicht muss), die gesuchten Felder sind im model "auth_user" ja schon drin, sie werden nur nicht angezeigt.
Musst du aber wohl. Irgendwie und irgendwo muss ja festgelegt werden z.b. welche Felder angezeigt werden sollen. Das was defaultmäßig angezeigt wird ist ja offenbar nicht das gewünschte...
Und weil hier das Problem "nicht angezeigte Felder" ist, ist meine starke Vermutung dass es mit dem anlegen von

Code: Alles auswählen

fields
(Details im Posting zwei höher) mit Beschreibung der gewünschten Felder getan ist. Und vermutlich greift dabei dann auch gleich die Sortierbarkeit usw...

Um in der Admin-Maske z.b.noch die Möglichkeit zum filtern der angezeigten Daten zu haben, brauchts dann noch ein paar weitere Daten an der richtigen Stelle.
Benutzeravatar
grubenfox
User
Beiträge: 430
Registriert: Freitag 2. Dezember 2022, 15:49

P.S. es geht hier ja nur um eine Klasse in der du festlegt wie die von dir gewünschte Admin-Ansicht aufgebaut sein soll bzw. aussehen soll.
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

grubenfox hat geschrieben: Donnerstag 9. Februar 2023, 14:48

Code: Alles auswählen

class BenutzerAdmin(admin.ModelAdmin):
    pass
die Klasse passend konfigurieren (u.a vielleicht mit fields)

Code: Alles auswählen

class BenutzerAdmin(admin.ModelAdmin):
    fields = ('vorname', 'nachname', 'klasse', 'startdatum', 'last_login') # je nach dem wie die Elemente (Felder bzw. Methoden) in der Klasse Benutzer heißen
und zusammen mit der Klasse Benutzer im Admin-Interface registrieren

Code: Alles auswählen

admin.site.register(Benutzer, BenutzerAdmin)
Das habe ich ja probiert, aber irgendwie geht das so nicht.
Der erste Code.

Code: Alles auswählen

[code]class BenutzerAdmin(admin.ModelAdmin):
    pass
bewirkt ja doch erstmal nicht - oder?
und der zweite:

Code: Alles auswählen

class BenutzerAdmin(admin.ModelAdmin):
    fields = ('vorname', 'nachname', 'klasse', 'startdatum', 'last_login') 
geht so nicht, denn 'vorname', 'nachname und, 'klasse' befinden sich in meiner Tabelle"Profil" und 'startdatum' und 'last_login' in der Tabelle auth_user'.
Möglicherweise stehe ich aber auch wieder auf dem Schlauch.
Ich bin aber ganz stolz, ich habe einen Teil selbst hinbekommen. Ich habe in meinem model "Profil" folgendes eingefügt:

Code: Alles auswählen

class Profil(models.Model):
    user = models.OneToOneField(User, related_name='profil', on_delete=models.CASCADE )
    nachname = models.CharField(max_length=30)
    vorname = models.CharField(max_length=30)
    
    klasse = models.CharField(max_length=10)

    def Start(self):        
        return self.user.date_joined.date()

    def Letzt(self):        
        return self.user.last_login
und in admin.py:

Code: Alles auswählen

class ProfilAdmin(admin.ModelAdmin):
    list_filter=("gruppe", )
    list_display = ('vorname', 'nachname', 'klasse', 'Start', 'Letzt') 
Jetzt bekomme ich die gewünschte Anzeige in Spalten - mit Anmeldetag und letztem Login. Ich kann auch nach Vorname, Nachname und Klasse sortieren, allerdings nicht nach Anmeldetag und letztem Login - die Daten sind ja nun mal auch in der anderen Tabelle.
Ich müsste also doch nur in "auth_user" dieses "list_display" um "date_joined" und "last_login" ergänzen können (und vielleicht auch dort eine Funktion ergänzen, die mir Vorname, Nachname und Klasse aus der "Profil"-Tabelle liefert).

Ach übrigens: Der Filter nach "gruppe" funktioniert.
Benutzeravatar
grubenfox
User
Beiträge: 430
Registriert: Freitag 2. Dezember 2022, 15:49

Pitwheazle hat geschrieben: Donnerstag 9. Februar 2023, 16:49
Ich bin aber ganz stolz, ich habe einen Teil selbst hinbekommen. Ich habe in meinem model "Profil" folgendes eingefügt:

Code: Alles auswählen

class Profil(models.Model):
    user = models.OneToOneField(User, related_name='profil', on_delete=models.CASCADE )
    nachname = models.CharField(max_length=30)
    vorname = models.CharField(max_length=30)
    
    klasse = models.CharField(max_length=10)

    def Start(self):        
        return self.user.date_joined.date()

    def Letzt(self):        
        return self.user.last_login
und in admin.py:

Code: Alles auswählen

class ProfilAdmin(admin.ModelAdmin):
    list_filter=("gruppe", )
    list_display = ('vorname', 'nachname', 'klasse', 'Start', 'Letzt') 
Jetzt bekomme ich die gewünschte Anzeige in Spalten - mit Anmeldetag und letztem Login. Ich kann auch nach Vorname, Nachname und Klasse sortieren, allerdings nicht nach Anmeldetag und letztem Login - die Daten sind ja nun mal auch in der anderen Tabelle.
Ich müsste also doch nur in "auth_user" dieses "list_display" um "date_joined" und "last_login" ergänzen können (und vielleicht auch dort eine Funktion ergänzen, die mir Vorname, Nachname und Klasse aus der "Profil"-Tabelle liefert).

Ach übrigens: Der Filter nach "gruppe" funktioniert.
Da haben wir es ja... mehr oder weniger. Gut ich war von einer Klasse 'Benutzer' ausgegangen, aber Namen sind Schall und Rauch. Dann also 'Profil'. Profil liefert alle gewünschten Daten (Nachname, Vorname und Klasse speichert es in der eigenen Tabelle, der Rest kommt von der Klasse/Tabelle User) und in der Admin-Seite wird alles schön angezeigt. Offenbar hatte mich die Django-Doku mit dem 'fields' auch auf die falsche Fährte gelockt und 'list_display' ist das benötigte Element für die Anzeige. Ich glaube bei meiner Django-Anwendung bin ich seinerzeit auch schon genau hierüber gestolpert.
Erwähnte ich schon dass man da so seine Probleme hat aus den vielen in der Doku beschriebenen Elementen die richtigen raus zu suchen... :wink:

Das sich die angezeigte Tabelle nicht nach 'Start' bzw. 'Letzt' sortieren lässt, irritiert mich jetzt. Da hatte ich andere Hoffnungen...
Ich glaube des Rätsels Lösung versteckt sich irgendwie in der Beschreibung von ModelAdmin.sortable_by
Aber ich habe jetzt nicht die Ruhe und Zeit um mich darin zu vertiefen...
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

grubenfox hat geschrieben: Donnerstag 9. Februar 2023, 17:26 Erwähnte ich schon dass man da so seine Probleme hat aus den vielen in der Doku beschriebenen Elementen die richtigen raus zu suchen... :wink:
Habe ich schon erwähnt, wie froh ich bin, dass ich da nicht der einzige bin. Auf meine Fragen bekomme ich oft den Hinweis "Das ist doch alles hier beschrieben ..." - und ich staune immer wieder, wo das hergezaubert wird.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Bei umfangreicher Doku gibt es oft Tutorials/Howtos. Da sollte man reinschauen,wenn man sich erschlagen fühlt. Ist bei Python selbst ja auch nicht anders.
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

Klar, mache ich, ansonsten hätte ich sicher schon das Handtuch geworfen.
Benutzeravatar
grubenfox
User
Beiträge: 430
Registriert: Freitag 2. Dezember 2022, 15:49

Pitwheazle hat geschrieben: Donnerstag 9. Februar 2023, 18:15 Klar, mache ich, ansonsten hätte ich sicher schon das Handtuch geworfen.
Hier ebenso...
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@Pitwheazle: Wenn ich mich richtig erinnere: Du kannst für die Liste einfach auf Relationen zugreifen. So wie man das auch bei Queries macht. Das Anlegen von Properties (denn es sollten eigentlich Properties sein) ist also unnötig.

Code: Alles auswählen

class ProfilAdmin(admin.ModelAdmin):
    list_filter=("gruppe", )
    list_display = ('vorname', 'nachname', 'klasse', 'user__date_joined', 'user__last_login') 
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Wenn du die Admin-Ansicht von UserAdmin verändern möchtest, kannst du von der original Klasse erben und anpassen.
Durch das Erben erhälst du die speziellen Sachen wie die Passwort-Verschlüsselung, etc.

In deiner admin.py

Code: Alles auswählen

from django.contrib.auth.admin import UserAdmin

class PitwheazleUserAdmin(UserAdmni):
    list_display = ()

admin.site.unregistger(User)
admin.site.register(User,  PitwheazleUserAdmin)
Die Verantwortung der Namensgebung übergebe ich vertrauensvoll an dich.
Benutzeravatar
grubenfox
User
Beiträge: 430
Registriert: Freitag 2. Dezember 2022, 15:49

Stimmt... wie sparrow oben schreibt: die Relationen (mit zwei Unterstrichen im Namen) gibt es auch. Vielleicht ist es das.

@sparrow: zum "Wenn du die Admin-Ansicht von UserAdmin verändern möchtest, kannst du von der original Klasse erben und anpassen." mache ich mal ein neues Thema auf. Da hätte ich auch mal eine Frage...
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

sparrow hat geschrieben: Donnerstag 9. Februar 2023, 20:27 @Pitwheazle: Wenn ich mich richtig erinnere: Du kannst für die Liste einfach auf Relationen zugreifen. So wie man das auch bei Queries macht. Das Anlegen von Properties (denn es sollten eigentlich Properties sein) ist also unnötig.

Code: Alles auswählen

class ProfilAdmin(admin.ModelAdmin):
    list_filter=("gruppe", )
    list_display = ('vorname', 'nachname', 'klasse', 'user__date_joined', 'user__last_login') 
Nö, das geht nicht:
ERRORS:
<class 'accounts.admin.ProfilAdmin'>: (admin.E108) The value of 'list_display[3]' refers to 'user__date_joined', which is not a callable, an attribute of 'ProfilAdmin', or an attribute or method on 'accounts.Profil'.
<class 'accounts.admin.ProfilAdmin'>: (admin.E108) The value of 'list_display[4]' refers to 'user__last_login', which is not a callable, an attribute of 'ProfilAdmin', or an attribute or method on 'accounts.Profil'.
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

sparrow hat geschrieben: Donnerstag 9. Februar 2023, 20:37 Wenn du die Admin-Ansicht von UserAdmin verändern möchtest, kannst du von der original Klasse erben und anpassen.
Durch das Erben erhälst du die speziellen Sachen wie die Passwort-Verschlüsselung, etc.

In deiner admin.py

Code: Alles auswählen

from django.contrib.auth.admin import UserAdmin

class PitwheazleUserAdmin(UserAdmni):
    list_display = ()

admin.site.unregistger(User)
admin.site.register(User,  PitwheazleUserAdmin)
Die Verantwortung der Namensgebung übergebe ich vertrauensvoll an dich.
Das bekomme ich auf Anhieb auch nicht hin. "User" wird in meinem admin.py nicht registriert und kann daher auch nicht unregistriert werden ("User" ist in meiner IDE unterstrichen). Bearbeiten kann ich User aber schon.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@Pitzwheazle: Du musst das Model User schon importieren. Man registriert ja die entsprechenden Modelle.

Also wenn ich das richtig erinnere:

Code: Alles auswählen

from django.contrib.auth.models import User
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

Prima! Entschuldige, ich habe zwar schon viele Zeilen geschrieben (und die funktionieren dank eurer Hilfe auch (meistens)), aber bei einigen Fragen stehe ich "wie der Ochs' vor'm Scheunentor". Eigentlich ist es groß genug, man muss es aber auch als Scheunentor erkennen.
Ich fürchte, den entgültigen Durchblick werde ich nicht mehr erreichen, aber ein bißchen lerne ich dazu. Jetzt habe ich gelernt, dass man das "django.contrib.auth" wie meine Apps "accounts" und "core" benutzen kann und mit ".model" die models von da importieren kann!
Was aber genau passiert, wenn ich mit:

Code: Alles auswählen

admin.site.unregister(User)
admin.site.register(User,  BenutzerAdmin)  
user zunächst unregistriere und dann wieder registriere erschließt sich mir noch nicht -

Code: Alles auswählen

class BenutzerAdmin(UserAdmin):
    list_display = ('username', 'date_joined', 'last_login')
funktioniert aber!
Jetzt hätte ich noch gerne den zugehörigen Nachname und Vorname aus "Profil" und die Zeitangaben noch formatiert.
Sehe ich das richtig, dass ich dazu eine Property anlegen muss?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Was das macht:
Du verwerndest doch bereits eigene Admin-Klassen. Und das tut natürlich django.contrib.auth auch. Dort steht in einer admin.pyd die Klasse UserAdmin.
Und so wie du deine Admin-Klassen mit Model-Klassen per register vernüpfst, tut das auch django.contrib.auth. Und deshalh gehst du her, unregistrierst die bereits registrierte Klasse und registrierst deine eigene.

Ich würde mir überlegen, ob du wirkllich die zusätzlichen Methoden in das Model schreiben willst, um an die Daten zu kommen. Das könnte Vorteile bein den Datenbank-Hits bei der Anzeige haben - aber ich finde es seltsam, Methoden in einem Model anzulegen, nur um sie im Admin anzuzeigen.

Warum hat dein Profil Vornamen und Nachnamen? Das sind doch eigentlich Daten, die in django.contrib.auth.models.User schon stehen.

Ich würde es stattdessen in die Admin-Klasse aufnehmen.

Beispielhaft:

Code: Alles auswählen

class BenutzerAdmin(UserAdmin):
    list_display = ('username', 'profil_firstname', 'date_joined', 'last_login')

    def profil_firstname(self, obj):
        return obj.profil.vorname

    profil_firstname.short_description = "Profil Vorname"
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

sparrow hat geschrieben: Freitag 10. Februar 2023, 17:21 Was das macht:
Du verwerndest doch bereits eigene Admin-Klassen. Und das tut natürlich django.contrib.auth auch. Dort steht in einer admin.pyd die Klasse UserAdmin.
Und so wie du deine Admin-Klassen mit Model-Klassen per register vernüpfst, tut das auch django.contrib.auth. Und deshalh gehst du her, unregistrierst die bereits registrierte Klasse und registrierst deine eigene.
Ich weiß nicht, ob du das "Lustige Angelspiel" noch kennst. Das haben wir immer auf Kindergeburtstagen gespielt. In der Mitte steht eine Kiste, oben offen. Da kommen Pappefiguren von Fischen und anderen Teilen rein und an diesen Figuren war ein Metallplättchen. Die Kinder sitzen um die Kiste und können nicht reingucken. Jedes Kind hat eine Angel an der ein Magnet befestigt ist. Wenn man einen Fisch angelt war es prima, manchmal hing aber nur ein kaputter Stiefel dran. So geht es mir mit diesem django.contrib.auth - ich kann nicht in die Kiste reinschauen und staune immer wieder was ich rausangele.
Als ich mein Projekt angefangen habe, war das noch schlimmer. Mein Tutorium hat gesagt, um meine User-Anmelde-Daten zu ergänzen kann ich auch ein zweites Model mit einer OneToOne Beziehung verknüpfen - und das habe ich dann auch gemacht. In der User-Tabelle stehen die geheimnisvollen Django-Dinger drin und da brauche ich nur den Benutzername und das Passwort und eventuell die Emailadresse (viele Schülerinnen und Schüler haben keine) - an der Integration dieser Emailadresse arbeite ich ja noch in einer anderen Baustelle - Ja und alle anderen Daten, die ich so brauche sind in meiner Tabelle "Profil", das sind einige, darunter auch Vor- und Nachname. Die Tabelle habe ich selbst erstellt, ich kann sie sehen und mit ihr (größtenteils) umgehen.
sparrow hat geschrieben: Freitag 10. Februar 2023, 17:21 Warum hat dein Profil Vornamen und Nachnamen? Das sind doch eigentlich Daten, die in django.contrib.auth.models.User schon stehen.
Zwischenzeitlich ist mein Projekt ja schon online und da werde ich das eher nicht mehr ändern (können?). Und weil ich jetzt zwar weiß, wo diese "User" model liegt, es aber nicht sehen kann, weiß ich jetzt wieder nicht, wie ich meine Vor- und Nachnamen da reinbekomme und meine Datumsanzeigen formatieren kann - auweh!
Antworten