Korzystanie z Django auth UserAdmin dla niestandardowego modelu użytkownika

82

Z dokumentacji Django.Contrib.Auth :

Rozszerzanie domyślnego użytkownika Django Jeśli jesteś całkowicie zadowolony z modelu użytkownika Django i chcesz tylko dodać dodatkowe informacje o profilu, możesz po prostu utworzyć podklasę django.contrib.auth.models.AbstractUseri dodać własne pola profilu. Ta klasa zapewnia pełną implementację domyślnego User jako modelu abstrakcyjnego.

Powiedziane i zrobione. Stworzyłem nowy model jak poniżej:

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

To pojawia się w panelu administracyjnym prawie tak, jak w standardzie Django User. Jednak najważniejszą różnicą w ustawieniach admin jest to, że nie ma pola ustawiania (ponownego) hasła, ale zamiast tego wyświetlany jest normalny CharField. Czy naprawdę muszę przesłonić rzeczy w admin-config, aby to zadziałało? Jeśli tak, jak mogę to zrobić w nieco SUCHY sposób (tj. Bez kopiowania rzeczy ze źródła Django… eww…)?

nip3o
źródło

Odpowiedzi:

135

Po pewnym czasie przekopania się w kodzie źródłowym Django, znalazłem działającą duszę. Nie jestem do końca zadowolony z tego rozwiązania, ale wydaje się, że działa. Zapraszam do proponowania lepszych rozwiązań!


Django używa UserAdmindo renderowania ładnego administracyjnego wyglądu Usermodelu. Używając tego w naszym admin.py-file, możemy uzyskać ten sam wygląd naszego modelu.

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

Jednak samo to prawdopodobnie nie jest dobrym rozwiązaniem, ponieważ Django Admin nie wyświetli żadnego z twoich specjalnych pól. Są ku temu dwa powody:

  • UserAdminużywa UserChangeFormjako formy używanej podczas modyfikowania obiektu, który z kolei używa Userjako swojego modelu.
  • UserAdmindefiniuje formsets-właściwość, używaną później przez UserChangeForm, która nie obejmuje twoich specjalnych pól.

Dlatego stworzyłem specjalny formularz zmiany, który przeciąża wewnętrzną klasę Meta, tak że formularz zmiany używa poprawnego modelu. Musiałem też przeładować, UserAdminaby dodać moje pola specjalne do zestawu pól, co jest częścią tego rozwiązania, które trochę nie lubię, ponieważ wygląda trochę brzydko. Zapraszam do sugerowania ulepszeń!

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
źródło
Świetny! Dokładnie tego szukałem, robię to samo z moim niestandardowym modelem użytkownika, używając AbstractUser. To rozwiązanie działa świetnie, trochę dostosowań (ukrywanie pól, dodanie swoich 'some_extra_data' 'w `` Personal info' ') też byłoby fajne, ale na razie zrobiłeś mój dzień. Dzięki @nico
Dachmt
Przeciążenie zestawów pól UserAdmin jest brzydkie? To działa. Dzięki.
allcaps
6
Zauważ również, że musisz nadpisać formularz tworzenia użytkownika, zgodnie z odpowiedzią @ kdh454.
Thane Brimhall
To jest idealne, wcale nie jest brudne. Dokumentacja django wskazuje na ten artykuł: docs.djangoproject.com/en/2.0/topics/auth/customizing/ ... ... ale nie byłem w stanie tego zrobić, narzekał na pewne ograniczenia użytkownika z kluczem obcym. Może dotyczyć tylko mojego projektu, ale Twoje rozwiązanie działa!
Vladimir Marton
57

Odpowiedź nico była niezwykle pomocna, ale zauważyłem, że Django nadal odwołuje się do modelu użytkownika podczas tworzenia nowego użytkownika.

Bilet nr 19353 odnosi się do tego problemu.

Aby to naprawić, musiałem zrobić kilka dodatkowych dodatków 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
źródło
Twoje rozwiązanie zadziałało dla mnie bez zarzutu. Wielkie dzięki!
guinunez
3
Zauważ, że zaczynając od 1.6, wywołanie do forms.ValidationErrorma drugi argument code='duplicate_username':: github.com/django/django/blob/1.6/django/contrib/auth/ ... Poza tym próbowałem tego w 1.6.5 iz jakiegoś powodu w ogóle nie trzeba było nadpisywać, UserChangeForma formularz zmiany działał dobrze z moim polem niestandardowym. Nie potrafię wyjaśnić dlaczego. Wygląda na to, że nie powinno to zadziałać, ponieważ UserChangeFormklasa ma model = Userobecną: github.com/django/django/blob/1.6.5/django/contrib/auth/ ...
Nick
2
Numer FYI nr 19353 został już naprawiony, co zasadniczo oznacza, że ​​Twoje rozwiązanie może być zbędne. Jeśli użyjemy tylko rozwiązania @ nip3o, powinno być dobrze.
kstratis
50

Prostsze rozwiązanie, 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 będzie poprawnie odwoływać się do modelu MyUser podczas tworzenia i modyfikacji. Używam Django 1.6.2.

cesc
źródło
Nie wiem dlaczego, ale ciągle otrzymywałem "TypeError: nieobsługiwane typy operandów dla +: 'NoneType' i 'tuple'" podczas próby.
Chase Roberts
Proponuję zapoznać się z ustawieniami UserAdmin.fieldsets i sprawdzić, czy jest to Brak
Rômulo Collopy
1
to jest najbardziej aktualna odpowiedź i powinna być akceptowana
brillout
1
Zamiast tego pozwól None, możesz wpisać ciąg znaków, który będzie używany jako tytuł sekcji w formularzu administratora
Guillaume Lebreton
Zgadzam się z @brillout, stwierdziliśmy, że to najlepsza odpowiedź. Aby dodać, możesz otrzymać ten błąd, który ma sens (Python 3.8, Django 3):ERRORS: <class 'users.admin.CustomerUserAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.
nicorellius
9

Odpowiedź cesc nie działała dla mnie, gdy próbowałem dodać niestandardowe pole do formularza tworzenia. Może to się zmieniło od wersji 1.6.2? Tak czy inaczej, stwierdziłem, że dodanie pola do obu zestawów pól i add_fieldsets załatwiło sprawę.

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
źródło
2

Inne podobne rozwiązanie ( wzięte stąd ):

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
źródło
-1

Po prostu wykonaj następujące czynności

from django.contrib import admin
from .models import MyUser

admin.site.register(MyUser, admin.ModelAdmin)

Otóż ​​to. Zostaną wyświetlone wszystkie pola modelu MyUser. Lista pól UserAdmin do wyświetlenia jest zakodowana na stałe (zobacz kod źródłowy), ale ModelAdmin nie. Jest elastyczny.

Sturm Tiger
źródło
Niepolecane. Spowoduje to użycie wartości domyślnej ModelAdmin, a nie dostosowanej UserAdmin. Rezultatem jest bałagan.
dqd