Comment personnaliser le profil utilisateur lors de l'utilisation de django-allauth

107

J'ai un projet django avec l'application django-allauth. J'ai besoin de collecter des données supplémentaires auprès de l'utilisateur lors de l'inscription. Je suis tombé sur une question similaire ici mais malheureusement, personne n'a répondu à la partie personnalisation du profil.

Selon la documentation fournie pourdjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

Une chaîne pointant vers une classe de formulaire personnalisée (par exemple ‘myapp.forms.SignupForm’) qui est utilisée lors de l'inscription pour demander à l'utilisateur une entrée supplémentaire (par exemple, inscription à la newsletter, date de naissance). Cette classe doit implémenter une ‘save’méthode, en acceptant l'utilisateur nouvellement inscrit comme seul paramètre.

Je suis nouveau sur Django et j'ai du mal avec ça. Quelqu'un peut-il fournir un exemple d'une telle classe de formulaire personnalisée? Dois-je également ajouter une classe de modèle avec un lien vers l'objet utilisateur comme celui-ci ?

Shreyas
la source

Réponses:

170

Supposons que vous souhaitiez demander à l'utilisateur son prénom / nom lors de l'inscription. Vous devrez mettre ces champs dans votre propre formulaire, comme ceci:

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()

Ensuite, dans vos paramètres, pointez sur ce formulaire:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

C'est tout.

Pennersr
la source
10
Merci. Il est toujours bon d'entendre l'auteur original :). Dois-je créer une classe supplémentaire pour stocker ces informations ou allauth s'en charge-t-il automatiquement?
Shreyas
12
Cela dépend en fait des informations que vous demandez. Dans tous les cas, tout cela est au-delà de toute portée. Si vous demandez le prénom / nom comme dans l'exemple ci-dessus, vous n'avez pas besoin d'un modèle supplémentaire et pouvez placer les choses directement dans le modèle utilisateur. Si vous demandez la date de naissance de l'utilisateur, sa couleur préférée ou autre, vous devez configurer votre propre modèle de profil pour cela. Veuillez jeter un œil ici pour savoir comment procéder: docs.djangoproject.com/en/dev/topics/auth/…
pennersr
6
C'est exactement ce que je recherchais - un champ supplémentaire comme la couleur préférée. Au cas où je serais intéressé par la couleur préférée, par exemple, je pense que je devrais créer une nouvelle classe UserProfile, puis utiliser l'utilisateur comme un champ un à un et la couleur préférée comme champ supplémentaire. Dans ce cas, puis-je toujours utiliser un type de SignUpForm que vous avez déclaré (avec la couleur préférée) ci-dessus et y connecter ACCOUNT_SIGNUP_FORM_CLASS ou dois-je créer le formulaire et gérer l'enregistrement des données dans mon propre code?
Shreyas
4
Bien sûr, le mécanisme ACCOUNT_SIGNUP_FORM_CLASS peut toujours être utilisé. Vous devez simplement vous assurer que la méthode save () est correctement implémentée afin que la couleur préférée soit stockée dans le modèle de votre choix.
pennersr
5
@pennersr - Comment pourrais-je ajouter cela ACCOUNT_SIGNUP_FORM_CLASSaprès la toute première connexion sociale pour collecter et enregistrer les champs de modèle utilisateur personnalisé? De plus, l'utilisation du modèle utilisateur personnalisé par les AUTH_USER_MODELmodifications de git: github.com/pennersr/django-allauth ne sont pas téléchargées dans pypi.
Babu
23

En utilisant la solution suggérée par pennersr, je recevais un avertissement de dépréciation:

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

En effet, à partir de la version 0.15, la méthode de sauvegarde a été déconseillée au profit d'une méthode d'inscription def (requête, utilisateur).

Donc, pour résoudre cela, le code de l'exemple devrait être comme ceci:

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
la source
2
La réponse de @ pennsesr a maintenant été modifiée pour être utilisée à la signupplace de save.
Flimm
18

Voici ce qui a fonctionné pour moi en combinant quelques-unes des autres réponses (aucune d'entre elles n'est complète à 100% et sèche).

Dans 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()

Et dans settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

De cette façon, il utilise les formulaires modèles pour qu'il soit SEC, et utilise le nouveau def signup. J'ai essayé de mettre 'myproject.myapp.forms.SignupForm'mais cela a entraîné une erreur en quelque sorte.

Howardwlo
la source
utiliser 'yourapp.forms.SignupForm' au lieu de 'myproject.myapp.forms.SignupForm' a également fonctionné pour moi
alpalalpal
6

@Shreyas: La solution ci-dessous n'est peut-être pas la plus propre, mais elle fonctionne. S'il vous plaît laissez-moi savoir si vous avez des suggestions pour le nettoyer davantage.

Pour ajouter des informations qui n'appartiennent pas au profil utilisateur par défaut, créez d'abord un modèle dans yourapp / models.py. Lisez la documentation générale de django pour en savoir plus, mais en gros:

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)

Créez ensuite un formulaire dans yourapp / forms.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()
marque
la source
C'est exactement ce que j'ai fini par utiliser pour une application Django 2.0 exécutant le CMS Wagtail. Avez-vous travaillé pour une inscription régulière, mais moins avec Social Auth, semble-t-il?
Kalob Taulien
Comment ajouter ce champ supplémentaire à la page d'administration de l'utilisateur dans Wagtail?
Joshua
5

Dans votre users/forms.pyvous mettez:

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()

Dans settings.py vous mettez:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

De cette façon, vous ne cassez pas le principe DRY par la multiplicité de la définition des champs des modèles utilisateur.

Adam Dobrawy
la source
4

J'ai essayé de nombreux tutoriels différents et il leur manque quelque chose, répéter du code inutile ou faire des choses bizarres, ci-dessous suit ma solution qui rejoint toutes les options que j'ai trouvées, ça marche, je l'ai déjà mis en production MAIS ça toujours pas me convaincre parce que je m'attendrais à recevoir le prénom et le nom dans les fonctions que j'ai attachées aux utilisateurs créent pour éviter de créer un profil à l'intérieur du formulaire, mais je ne pourrais pas, par ailleurs, je pense que cela vous aidera.

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'
Grégory
la source
Ironiquement, il manque également quelques éléments. Les champs Profil n'ont pas de valeurs par défaut, et n'autorisent pas la valeur NULL, donc votre create_user_profilesignal échoue par conception. Deuxièmement, vous pouvez réduire cela à un seul signal, basé sur created, en particulier lorsque vous parlez DRY. Et troisièmement, vous effectuez une sauvegarde de profil en appelant user.save () dans votre vue, puis en enregistrant à nouveau le profil avec les données réelles.
Melvyn
@Melvyn Ne devrait-il pas être fields = [...]avec des accolades au lieu de fields = (...) parenthèses?
Ahtisham
Cela peut, mais ne doit pas l'être. Il n'est utilisé qu'en lecture seule pour vérifier si le champ du modèle doit faire partie du formulaire. Il peut donc s'agir d'une liste, d'un tuple ou d'un ensemble ou de tout dérivé de celui-ci. Étant donné que les tuples ne sont pas mutables, il est plus logique d'utiliser des tuples et d'éviter les mutations accidentelles. Du point de vue de la performance, ces collections sont en pratique trop petites pour avoir un impact. Une fois que la collection devient trop longue, il peut être judicieux de passer à la excludeplace.
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'

C'est ça. (:

Milovan Tomašević
la source
-10

Créer un modèle de profil avec l'utilisateur comme 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
la source
3
Merci, mais je ne pense pas que ce soit tout ce dont vous avez besoin. Ma question concerne spécifiquement l'application allauth.
Shreyas