Cómo personalizar el perfil de usuario al usar django-allauth

107

Tengo un proyecto de django con la aplicación django-allauth. Necesito recopilar datos adicionales del usuario al registrarme. Me encontré con una pregunta similar aquí, pero desafortunadamente, nadie respondió la parte de personalización del perfil.

Según la documentación proporcionada paradjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

Una cadena que apunta a una clase de formulario personalizado (p ‘myapp.forms.SignupForm’. Ej. ) Que se utiliza durante el registro para pedirle al usuario información adicional (p. Ej., Suscripción al boletín informativo, fecha de nacimiento). Esta clase debería implementar un ‘save’método, aceptando al usuario recién registrado como su único parámetro.

Soy nuevo en django y estoy luchando con esto. ¿Alguien puede proporcionar un ejemplo de una clase de formulario personalizada? ¿Necesito agregar una clase de modelo también con un enlace al objeto de usuario como este ?

Shreyas
fuente

Respuestas:

170

Suponga que desea preguntarle al usuario su nombre / apellido durante el registro. Deberá poner estos campos en su propio formulario, así:

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

Luego, en su configuración, apunte a este formulario:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

Eso es todo.

pennersr
fuente
10
Gracias. Siempre es bueno saber del autor original :). ¿Necesito crear una clase adicional para almacenar esta información o todo se encarga de eso automáticamente?
Shreyas
12
De hecho, eso depende de la información que solicite. En cualquier caso, todo esto está más allá de todo el alcance. Si solicita el nombre / apellido como en el ejemplo anterior, entonces no necesita un modelo adicional y puede colocar cosas directamente en el modelo de Usuario. Si solicita la fecha de nacimiento del usuario, su color favorito o lo que sea, entonces necesita configurar su propio modelo de perfil para esto. Por favor, eche un vistazo aquí para saber
pennersr
6
Eso es exactamente lo que estaba buscando: un campo adicional como el color favorito. En caso de que esté interesado, por ejemplo, en el color favorito, creo que debería crear una nueva clase UserProfile y luego usar el User como un campo uno a uno y el color favorito como campo adicional. En ese caso, ¿puedo seguir usando un tipo de SignUpForm que haya declarado (con el color favorito) arriba y conectar el ACCOUNT_SIGNUP_FORM_CLASS o necesito crear el formulario y manejar el almacenamiento de datos en mi propio código?
Shreyas
4
Seguro, el mecanismo ACCOUNT_SIGNUP_FORM_CLASS todavía se puede utilizar. Solo debe asegurarse de que el método save () esté implementado correctamente de manera que el color favorito se almacene en el modelo que desee.
pennersr
5
@pennersr: ¿Cómo podría agregar esto ACCOUNT_SIGNUP_FORM_CLASSdespués del primer inicio de sesión social para recopilar y guardar los campos del modelo de usuario personalizado? Además, el uso del modelo de usuario personalizado por AUTH_USER_MODELcambios de git: github.com/pennersr/django-allauth no se carga en pypi.
Babu
23

Usando la solución sugerida por pennersr, estaba obteniendo una DeprecationWarning:

DeprecationWarning: The custom signup form must offer a def signup(self, request, user) method DeprecationWarning)

Esto se debe a que a partir de la versión 0.15, el método de guardado ha quedado obsoleto en favor de un método de registro def (solicitud, usuario).

Entonces, para resolver esto, el código del ejemplo debería ser así:

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()
ferrangb
fuente
2
La respuesta de @ pennsesr ahora se ha editado para usar en signuplugar de save.
Flimm
18

Esto es lo que funcionó para mí combinando algunas de las otras respuestas (ninguna de ellas está 100% completa y SECA).

En yourapp/forms.py:

from django.contrib.auth import get_user_model
from django import forms

class SignupForm(forms.ModelForm):
    class Meta:
        model = get_user_model()
        fields = ['first_name', 'last_name']

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

Y en settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

De esta manera usa los formularios del modelo para que esté SECO, y usa el nuevo def signup. Intenté poner 'myproject.myapp.forms.SignupForm'pero eso resultó en un error de alguna manera.

Howardwlo
fuente
usar 'yourapp.forms.SignupForm' en lugar de 'myproject.myapp.forms.SignupForm' también funcionó para mí
alpalalpal
6

@Shreyas: Es posible que la siguiente solución no sea la más limpia, pero funciona. Por favor, avíseme si tiene alguna sugerencia para limpiarlo más.

Para agregar información que no pertenece al perfil de usuario predeterminado, primero cree un modelo en yourapp / models.py. Lea la documentación general de django para obtener más información al respecto, pero básicamente:

from django.db import models

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile')
    organisation = models.CharField(organisation, max_length=100, blank=True)

Luego crea un formulario en tu aplicación / formularios.py:

from django import forms

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')
    organisation = forms.CharField(max_length=20, label='organisation')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        # Replace 'profile' below with the related_name on the OneToOneField linking back to the User model
        up = user.profile
        up.organisation = self.cleaned_data['organisation']
        user.save()
        up.save()
marca
fuente
Esto es exactamente lo que terminé usando para una aplicación Django 2.0 que ejecuta Wagtail CMS. ¿Funcionó para el registro regular, pero parece que menos con Social Auth?
Kalob Taulien
¿Cómo agregaría este campo adicional a la página de administración para el usuario en Wagtail?
Joshua
5

En tu users/forms.pypones:

from django.contrib.auth import get_user_model
class SignupForm(forms.ModelForm):
    class Meta:
        model = get_user_model()
        fields = ['first_name', 'last_name']
    def save(self, user):
        user.save()

En settings.py pones:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

De esta forma no se rompe el principio DRY por multiplicidad Definición de campos de modelos de usuario.

Adam Dobrawy
fuente
4

He probado muchos tutoriales diferentes y a todos les falta algo, repiten código innecesario o hacen cosas raras, a continuación sigue mi solución que une todas las opciones que encontré, está funcionando, ya lo puse en producción PERO todavía no me convence porque esperaría recibir first_name y last_name dentro de las funciones que adjunté a Users create para evitar crear un perfil dentro del formulario, pero no pude, por cierto, creo que te ayudará.

Models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    nationality = models.CharField(max_length=2, choices=COUNTRIES)
    gender = models.CharField(max_length=1, choices=GENDERS)

def __str__(self):
    return self.user.first_name


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

Forms.py

class SignupForm(forms.ModelForm):
    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)

    class Meta:
        model = Profile
        fields = ('first_name', 'last_name', 'nationality', 'gender')

    def signup(self, request, user):
        # Save your user
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

        user.profile.nationality = self.cleaned_data['nationality']
        user.profile.gender = self.cleaned_data['gender']
        user.profile.save()

Settings.py

ACCOUNT_SIGNUP_FORM_CLASS = 'apps.profile.forms.SignupForm'
Gregory
fuente
Irónicamente, a esto también le faltan algunas cosas. Los campos de perfil no tienen valores predeterminados, ni permiten nulos, por lo que su create_user_profileseñal falla por diseño. En segundo lugar, puede reducir esto a una señal, en función de created, especialmente cuando se habla en SECO. Y tercero, efectúa un guardado de perfil llamando a user.save () en su vista, luego guardando el perfil nuevamente con los datos reales.
Melvyn
@Melvyn ¿No debería ser fields = [...]con llaves en lugar de fields = (...) paréntesis?
Ahtisham
Puede, pero no tiene que ser así. Solo se usa como solo lectura para verificar si el campo en el modelo debe ser parte del formulario. Por tanto, puede ser una lista, tupla o conjunto o cualquier derivado de los mismos. Dado que las tuplas no son mutables, tiene más sentido usar tuplas y prevenir mutaciones accidentales. Desde una perspectiva de desempeño, estas colecciones son en la práctica demasiado pequeñas para tener algún impacto. Una vez que la colección sea demasiado larga, puede tener sentido cambiar a exclude.
Melvyn
0
#models.py

from django.conf import settings

class UserProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    image = models.ImageField(default='users/default.png', upload_to='users')
    fields = models.ForeignKey('Field' ,null=True ,on_delete=models.SET_NULL)
    category = models.ForeignKey('Category' ,null=True ,on_delete=models.SET_NULL)
    description = models.TextField()
    interests = models.ManyToManyField('Interests')

    ...

   def save(self, *args, **kwargs):
       super().save(*args, **kwargs)

...

def userprofile_receiver(sender, instance, created, *args, **kwargs):
    if created:
        userprofile = UserProfile.objects.create(user=instance)
    else:
        instance.userprofile.save()

post_save.connect(userprofile_receiver, sender=settings.AUTH_USER_MODEL)



 #forms.py

 class SignupForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SignupForm, self).__init__(*args, **kwargs)
        self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter first name'})
        self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter last name'})

    first_name = forms.CharField(max_length=100)
    last_name = forms.CharField(max_length=100)

    interests  = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, help_text="Choose your interests", queryset=Interests.objects.all())

    image = forms.ImageField(help_text="Upload profile image ")
    fields = forms.ChoiceField(help_text="Choose your fields ")
    category = forms.ChoiceField(help_text="Choose your category")

    class Meta:
        model = UserProfile
        fields = ('first_name', 'last_name',  'name', 'image', 'fields', 'category', 'description', 'phone', 'facebook', 'twitter', 'skype', 'site', 'address', 'interests' ,'biography')
        widgets = {
             ...
            'description': forms.TextInput(attrs={'placeholder': 'Your description'}),
            'address': forms.TextInput(attrs={'placeholder': 'Enter address'}),
            'biography': forms.TextInput(attrs={'placeholder': 'Enter biography'}),
            ....
    }
    def signup(self, request, user):
        # Save your user
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

        user.userprofile.image = self.cleaned_data.get('image')
        user.userprofile.fields = self.cleaned_data['fields']
        user.userprofile.category = self.cleaned_data['category']
        user.userprofile.description = self.cleaned_data['description']
        interests = self.cleaned_data['interests']
        user.userprofile.interests.set(interests)
        user.userprofile.save()


# settings.py or base.py

ACCOUNT_SIGNUP_FORM_CLASS = 'nameApp.forms.SignupForm'

Eso es. (:

Milovan Tomašević
fuente
-10

Cree un modelo de perfil con el usuario como OneToOneField

class Profile(models.Model):
    user = models.OneToOneField(User, verbose_name=_('user'), related_name='profiles')
    first_name=models.CharField(_("First Name"), max_length=150)
    last_name=models.CharField(_("Last Name"), max_length=150)
    mugshot = ImageField(_('mugshot'), upload_to = upload_to, blank=True)
    phone= models.CharField(_("Phone Number"), max_length=100)
    security_question = models.ForeignKey(SecurityQuestion, related_name='security_question')
    answer=models.CharField(_("Answer"), max_length=200)
    recovery_number= models.CharField(_("Recovery Mobile Number"), max_length=100)
    city=models.ForeignKey(City,related_name='city', blank=True, null=True, help_text=_('Select your City'))
    location=models.ForeignKey(Country,related_name='location', blank=True, null=True, help_text=_('Select your Location'))
Jubin Thomas
fuente
3
Gracias, pero no creo que esto sea todo lo que se necesita. Mi pregunta se refiere específicamente a la aplicación allauth.
Shreyas