Usando Django auth UserAdmin para un modelo de usuario personalizado

82

De los documentos de Django.Contrib.Auth :

Ampliación del usuario predeterminado de Django Si está completamente satisfecho con el modelo de usuario de Django y solo desea agregar información de perfil adicional, simplemente puede django.contrib.auth.models.AbstractUsercrear una subclase y agregar sus campos de perfil personalizados. Esta clase proporciona la implementación completa del usuario predeterminado como modelo abstracto.

Dicho y hecho. Creé un nuevo modelo como el siguiente:

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

Esto se muestra en admin casi como el estándar de Django User. Sin embargo, la diferencia más importante en admin es que el campo de (re) establecer contraseña no está presente, sino que se muestra un CharField normal en su lugar. ¿Realmente tengo que anular cosas en la configuración de administración para que esto funcione? Si es así, ¿cómo puedo hacer eso de una manera algo SECA (es decir, sin copiar cosas de la fuente de Django ... eww ...)?

nip3o
fuente

Respuestas:

135

Después de investigar el código fuente de Django por un tiempo, encontré un alma funcional. No estoy totalmente satisfecho con esta solución, pero parece funcionar. ¡No dude en sugerir mejores soluciones!


Django usa UserAdminpara renderizar el bonito Usermodelo de apariencia de administrador . Con solo usar esto en nuestro admin.py-file, podemos obtener el mismo aspecto para nuestro modelo.

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

Sin embargo, esto por sí solo probablemente no sea una buena solución, ya que Django Admin no mostrará ninguno de sus campos especiales. Hay dos razones para esto:

  • UserAdminutiliza UserChangeFormcomo formulario a utilizar al modificar el objeto, que a su vez utiliza Usercomo modelo.
  • UserAdmindefine una formsetspropiedad -propiedad, utilizada posteriormente por UserChangeForm, que no incluye sus campos especiales.

Entonces, creé un formulario de cambio especial que sobrecarga la clase interna Meta para que el formulario de cambio use el modelo correcto. También tuve que sobrecargarme UserAdminpara agregar mis campos especiales al conjunto de campos, que es la parte de esta solución que no me gusta un poco, ya que se ve un poco fea. ¡No dude en sugerir mejoras!

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
fuente
¡Excelente! Eso es exactamente lo que estaba buscando, estoy haciendo lo mismo con mi modelo de usuario personalizado, usando AbstractUser. Esta solución funciona muy bien, algo de personalización (ocultar campos, agregar su 'some_extra_data' en la 'Información personal') también sería bueno, pero por ahora me alegraste el día. Gracias @nico
Dachmt
¿La sobrecarga de los conjuntos de campos de UserAdmin es fea? Funciona. Gracias.
mayúsculas
6
También tenga en cuenta que debe anular el formulario de creación de usuarios, según la respuesta de @ kdh454.
Thane Brimhall
Esto es perfecto, no es complicado en absoluto. La documentación de django apunta a este artículo: docs.djangoproject.com/en/2.0/topics/auth/customizing/… ... pero no pude hacerlo funcionar con eso, se quejaba de algunas restricciones de clave externa de usuario. Puede estar relacionado solo con mi proyecto, ¡sin embargo, su solución funciona!
Vladimir Marton
57

La respuesta de nico ha sido extremadamente útil, pero descubrí que Django todavía hace referencia al modelo de usuario al crear un nuevo usuario.

El ticket # 19353 hace referencia a este problema.

Para solucionarlo, tuve que hacer algunas adiciones más a 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
fuente
Tu solución funcionó perfectamente para mí. ¡Muchas gracias!
Guinunez
3
Tenga en cuenta que a partir de 1.6, la llamada a forms.ValidationErrortiene un segundo argumento code='duplicate_username':: github.com/django/django/blob/1.6/django/contrib/auth/… Además, probé esto en 1.6.5 y, por alguna razón, no necesitaba anular UserChangeFormen absoluto y el formulario de cambio funcionó bien con mi campo personalizado. No puedo explicar por qué. Parece que no debería haber funcionado debido a que la UserChangeFormclase tiene model = Userpresente: github.com/django/django/blob/1.6.5/django/contrib/auth/…
Nick
2
FYI ticket # 19353 ahora se ha corregido, lo que esencialmente significa que su solución puede ser redundante. Si usamos la solución de @ nip3o deberíamos estar bien.
kstratis
50

Una solución más 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 hará referencia correctamente al modelo MyUser para su creación y modificación. Estoy usando Django 1.6.2.

cesc
fuente
No sé por qué, pero seguía obteniendo un "TypeError: tipos de operandos no admitidos para +: 'NoneType' y 'tuple'" al intentar esto.
Chase Roberts
Le sugiero que inspeccione UserAdmin.fieldsets y vea si es None
Rômulo Collopy
1
esta es la respuesta más actualizada y debería ser la aceptada
brillante
1
En su lugar None, déjelo , puede ingresar una cadena y se usará como el título de la sección en el formulario de administración
Guillaume Lebreton
De acuerdo con @brillout, descubrí que esta es la mejor respuesta. Para agregar, es posible que obtenga este error, que tiene sentido (Python 3.8, Django 3):ERRORS: <class 'users.admin.CustomerUserAdmin'>: (admin.E005) Both 'fieldsets' and 'fields' are specified.
nicorellius
9

La respuesta de cesc no me funcionaba cuando intenté agregar un campo personalizado al formulario de creación. ¿Quizás ha cambiado desde 1.6.2? De cualquier manera, encontré que agregar el campo a ambos conjuntos de campos y add_fieldsets hizo el truco.

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
fuente
2

Otra solución similar ( Tomó de aquí ):

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
fuente
-1

Solo haz lo siguiente

from django.contrib import admin
from .models import MyUser

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

Eso es. Se mostrarán todos los campos del modelo MyUser. La lista de campos de UserAdmin para mostrar está codificada (consulte su código fuente), pero ModelAdmin no. Es flexible

Sturm Tiger
fuente
No recomendado. Esto usará el predeterminado ModelAdmin, no el personalizado UserAdmin. El resultado es un desastre.
dqd