web-dev-qa-db-fra.com

Utiliser Django auth UserAdmin pour un modèle utilisateur personnalisé

À partir des documents Django.Contrib.Auth :

Extension de l'utilisateur par défaut de Django Si vous êtes entièrement satisfait du modèle utilisateur de Django et que vous souhaitez simplement ajouter des informations de profil supplémentaires, vous pouvez simplement sous-classe Django.contrib.auth.models.AbstractUser et ajouter vos champs de profil personnalisés. Cette classe fournit l'implémentation complète de l'utilisateur par défaut en tant que modèle abstrait.

Dit et fait. J'ai créé un nouveau modèle comme ci-dessous:

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

Cela se voit dans l’administrateur presque comme le User standard de Django. Cependant, la différence la plus importante dans l’administration réside dans le fait que le champ password (re) set n’est pas présent, mais qu’un CharField normal s’affiche. Dois-je vraiment écraser les éléments dans admin-config pour que cela fonctionne? Si tel est le cas, comment puis-je le faire de manière quelque peu DRY (c'est-à-dire sans copier d'éléments de la source Django ... eww ...)?

65
nip3o

Après avoir fouillé dans le code source de Django pendant un moment, j'ai trouvé une âme qui fonctionne. Je ne suis pas totalement satisfait de cette solution, mais elle semble fonctionner. N'hésitez pas à suggérer de meilleures solutions!


Django utilise UserAdmin pour rendre à l'administrateur Nice le modèle User. En utilisant simplement ceci dans notre fichier admin.py-, nous pouvons obtenir la même apparence pour notre modèle.

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

Cependant, cela seul n'est probablement pas une bonne solution, puisque Django Admin n'affichera aucun de vos champs spéciaux. Il y a deux raisons à cela:

  • UserAdmin utilise UserChangeForm comme formulaire à utiliser pour modifier l'objet, qui à son tour utilise User comme modèle.
  • UserAdmin définit une propriété formsets-, utilisée ultérieurement par UserChangeForm, qui n'inclut pas vos champs spéciaux.

J'ai donc créé un formulaire de modification spécial qui surcharge la classe interne Meta de sorte que le formulaire de modification utilise le modèle correct. J'ai également dû surcharger UserAdmin pour ajouter mes champs spéciaux à la zone de champs, ce qui est la partie de cette solution qui me déplaît un peu, car elle a l'air un peu moche. N'hésitez pas à suggérer des améliorations!

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)
103
nip3o

la réponse de nico a été extrêmement utile, mais j'ai trouvé que Django faisait toujours référence au modèle User lors de la création d'un nouvel utilisateur.

Ticket # 19353 fait référence à ce problème.

Afin de résoudre ce problème, j'ai dû faire quelques ajouts supplémentaires à 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)
50
kdh454

Une solution plus simple, 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 référencera correctement le modèle MyUser pour la création et la modification ..__ J'utilise Django 1.6.2.

38
cesc

la réponse de cesc ne fonctionnait pas pour moi lorsque j'ai tenté d'ajouter un champ personnalisé au formulaire de création. Peut-être que ça a changé depuis la 1.6.2? Quoi qu'il en soit, j'ai trouvé que l'ajout du champ aux deux champs et add_fieldsets ont fait l'affaire. 

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)
3
aidnani8

Une autre solution similaire ( A pris d'ici ):

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)
0
Federico Comesaña