Verwenden von Django auth UserAdmin für ein benutzerdefiniertes Benutzermodell

79

Aus den Django.Contrib.Auth-Dokumenten :

Erweitern des Standardbenutzers von Django Wenn Sie mit dem Benutzermodell von Django vollkommen zufrieden sind und nur einige zusätzliche Profilinformationen hinzufügen möchten, können Sie einfach django.contrib.auth.models.AbstractUserIhre benutzerdefinierten Profilfelder unterordnen und hinzufügen. Diese Klasse bietet die vollständige Implementierung des Standardbenutzers als abstraktes Modell.

Gesagt, getan. Ich habe ein neues Modell wie folgt erstellt:

class MyUser(AbstractUser):
  some_extra_data = models.CharField(max_length=100, blank=True)

Dies zeigt sich in admin fast wie Djangos Standard User. Der wichtigste Unterschied in admin besteht jedoch darin, dass das Feld zum (erneuten) Festlegen des Kennworts nicht vorhanden ist, sondern stattdessen ein normales CharField angezeigt wird. Muss ich wirklich Dinge in der Admin-Konfiguration überschreiben, damit dies funktioniert? Wenn ja, wie kann ich das auf etwas TROCKENE Weise tun (dh ohne Sachen aus der Django-Quelle zu kopieren ... eww ...)?

nip3o
quelle

Antworten:

132

Nachdem ich mich eine Weile im Django-Quellcode umgesehen hatte, fand ich eine funktionierende Lösung. Ich bin mit dieser Lösung nicht ganz zufrieden, aber sie scheint zu funktionieren. Fühlen Sie sich frei, bessere Lösungen vorzuschlagen!


Django verwendet UserAdmin, um den netten Admin-Look für das UserModell zu rendern . admin.pyWenn wir dies nur in unserer Datei verwenden, können wir das gleiche Aussehen für unser Modell erhalten.

from django.contrib.auth.admin import UserAdmin
admin.site.register(MyUser, UserAdmin)

Dies allein ist jedoch wahrscheinlich keine gute Lösung, da Django Admin keines Ihrer speziellen Felder anzeigt. Dafür gibt es zwei Gründe:

  • UserAdminwird UserChangeFormals Formular verwendet, das beim Ändern des Objekts verwendet werden soll, das wiederum Userals Modell verwendet wird.
  • UserAdminformsetsDefiniert eine Eigenschaft, die später von verwendet wird UserChangeFormund Ihre speziellen Felder nicht enthält.

Also habe ich ein spezielles Änderungsformular erstellt, das die Meta-Innenklasse überlastet, sodass das Änderungsformular das richtige Modell verwendet. Ich musste auch überladen UserAdmin, um meine speziellen Felder zum Feldsatz hinzuzufügen. Dies ist der Teil dieser Lösung, den ich ein bisschen nicht mag, da sie ein bisschen hässlich aussieht. Fühlen Sie sich frei, Verbesserungen vorzuschlagen!

from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm

class MyUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = MyUser

class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm

    fieldsets = UserAdmin.fieldsets + (
            (None, {'fields': ('some_extra_data',)}),
    )


admin.site.register(MyUser, MyUserAdmin)
nip3o
quelle
Toll! Genau das habe ich gesucht. Ich mache dasselbe mit meinem benutzerdefinierten Benutzermodell, indem ich den AbstractUser verwende. Diese Lösung funktioniert hervorragend, einige Anpassungen (Ausblenden von Feldern, Hinzufügen Ihrer 'some_extra_data' in den 'Persönlichen Informationen') wären auch nett, aber jetzt haben Sie meinen Tag gemacht. Danke @nico
Dachmt
Überladung von de UserAdmin-Feldsätzen hässlich? Es klappt. Vielen Dank.
Allcaps
6
Beachten Sie auch, dass Sie das Benutzererstellungsformular gemäß der Antwort von @ kdh454 überschreiben müssen.
Thane Brimhall
Das ist perfekt, es ist überhaupt nicht chaotisch. Die Django-Dokumentation verweist auf diesen Artikel: docs.djangoproject.com/de/2.0/topics/auth/customizing/… ... aber ich konnte es nicht zum Laufen bringen, sie beschwerte sich über einige Fremdschlüsseleinschränkungen des Benutzers. Könnte nur mit meinem Projekt zusammenhängen, trotzdem funktioniert Ihre Lösung!
Vladimir Marton
56

Die Antwort von nico war äußerst hilfreich, aber ich fand, dass Django beim Erstellen eines neuen Benutzers immer noch auf das Benutzermodell verweist.

Ticket Nr. 19353 verweist auf dieses Problem.

Um das Problem zu beheben, musste ich noch einige Ergänzungen vornehmen admin.py

admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from main.models import MyUser
from django import forms


class MyUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = MyUser


class MyUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = MyUser

    def clean_username(self):
        username = self.cleaned_data['username']
        try:
            MyUser.objects.get(username=username)
        except MyUser.DoesNotExist:
            return username
        raise forms.ValidationError(self.error_messages['duplicate_username'])


class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm
    add_form = MyUserCreationForm
    fieldsets = UserAdmin.fieldsets + (
        (None, {'fields': ('extra_field1', 'extra_field2',)}),
    )

admin.site.register(MyUser, MyUserAdmin)
kdh454
quelle
Ihre Lösung hat bei mir einwandfrei funktioniert. Danke vielmals!
Guinunez
3
Beachten Sie, dass in 1.6 starten, wird der Anruf auf forms.ValidationErrorein zweites Argument hat: code='duplicate_username': github.com/django/django/blob/1.6/django/contrib/auth/... Auch habe ich versucht , dies auf 1.6.5 und aus irgendeinem Grund, ich musste überhaupt nicht überschrieben werden UserChangeFormund das Änderungsformular funktionierte gut mit meinem benutzerdefinierten Feld. Ich kann nicht erklären warum. Es scheint, als hätte es nicht funktionieren sollen, weil die UserChangeFormKlasse model = Useranwesend war: github.com/django/django/blob/1.6.5/django/contrib/auth/…
Nick
2
Das FYI-Ticket Nr. 19353 wurde jetzt behoben, was im Wesentlichen bedeutet, dass Ihre Lösung möglicherweise redundant ist. Wenn wir nur die Lösung von @ nip3o verwenden, sollten wir gut sein.
Kstratis
49

Eine einfachere Lösung, admin.py:

from django.contrib.auth.admin import UserAdmin
from main.models import MyUser

class MyUserAdmin(UserAdmin):
    model = MyUser

    fieldsets = UserAdmin.fieldsets + (
            (None, {'fields': ('some_extra_data',)}),
    )

admin.site.register(MyUser, MyUserAdmin)

Django verweist beim Erstellen und Ändern korrekt auf das MyUser-Modell. Ich benutze Django 1.6.2.

cesc
quelle
Ich weiß nicht warum, aber ich habe immer wieder einen "TypeError: nicht unterstützte Operandentyp (e) für +: 'NoneType' und 'tuple'" erhalten, als ich dies versuchte.
Chase Roberts
Ich empfehle Ihnen, UserAdmin.fieldsets zu überprüfen und festzustellen, ob es keine ist
Rômulo Collopy
1
Dies ist die
aktuellste
1
Stattdessen Nonekönnen Sie eine Zeichenfolge eingeben, die als Titel des Abschnitts im Administrationsformular verwendet wird
Guillaume Lebreton,
Stimmen Sie @brillout zu und finden Sie, dass dies die beste Antwort ist. Zum Hinzufügen wird möglicherweise der folgende Fehler ERRORS: <class 'users.admin.CustomerUserAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.
angezeigt
9

Die Antwort von cesc funktionierte nicht für mich, als ich versuchte, dem Erstellungsformular ein benutzerdefiniertes Feld hinzuzufügen. Vielleicht hat es sich seit 1.6.2 geändert? In beiden Fällen habe ich festgestellt, dass das Hinzufügen des Felds zu beiden Feldsätzen und add_fieldsets den Trick ausgeführt haben.

ADDITIONAL_USER_FIELDS = (
    (None, {'fields': ('some_additional_field',)}),
)

class MyUserAdmin(UserAdmin):
    model = MyUser

    add_fieldsets = UserAdmin.add_fieldsets + ADDITIONAL_USER_FIELDS
    fieldsets = UserAdmin.fieldsets + ADDITIONAL_USER_FIELDS

admin.site.register(MyUser, MyUserAdmin)
aidnani8
quelle
2

Eine andere ähnliche Lösung ( von hier übernommen ):

from __future__ import unicode_literals

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _

from .models import User


class UserAdminWithExtraFields(UserAdmin):

    def __init__(self, *args, **kwargs):
        super(UserAdminWithExtraFields, self).__init__(*args, **kwargs)

        abstract_fields = [field.name for field in AbstractUser._meta.fields]
        user_fields = [field.name for field in self.model._meta.fields]

        self.fieldsets += (
            (_('Extra fields'), {
                'fields': [
                    f for f in user_fields if (
                        f not in abstract_fields and
                        f != self.model._meta.pk.name
                    )
                ],
            }),
        )


admin.site.register(User, UserAdminWithExtraFields)
Federico Comesaña
quelle