Como personalizar o perfil do usuário ao usar django-allauth

107

Eu tenho um projeto django com o aplicativo django-allauth. Preciso coletar dados adicionais do usuário no momento da inscrição. Eu me deparei com uma pergunta semelhante aqui, mas, infelizmente, ninguém respondeu à parte de personalização do perfil.

De acordo com a documentação fornecida paradjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

Uma string apontando para uma classe de formulário personalizado (por exemplo ‘myapp.forms.SignupForm’) que é usada durante a inscrição para solicitar ao usuário uma entrada adicional (por exemplo, inscrição no boletim informativo, data de nascimento). Essa classe deve implementar um ‘save’método, aceitando o usuário recém-inscrito como seu único parâmetro.

Eu sou novo no django e estou lutando com isso. Alguém pode fornecer um exemplo de uma classe de formulário personalizado? Eu preciso adicionar uma classe de modelo também com um link para o objeto de usuário como este ?

Shreyas
fonte

Respostas:

170

Suponha que você queira perguntar ao usuário seu nome / sobrenome durante a inscrição. Você precisará colocar esses campos em seu próprio formulário, assim:

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

Então, em suas configurações aponte para este formulário:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

Isso é tudo.

Pennersr
fonte
10
Obrigado. É sempre bom ouvir o autor original :). Eu preciso criar uma classe adicional para armazenar essas informações ou o allauth cuida disso automaticamente?
Shreyas
12
Na verdade, isso depende das informações que você está pedindo. Em qualquer caso, tudo isso está além do escopo do allauth. Se você solicitar o nome / sobrenome como no exemplo acima, não precisará de um modelo extra e poderá colocar as coisas diretamente no modelo do usuário. Se você gostaria de perguntar a data de nascimento do usuário, sua cor favorita ou qualquer outra coisa, então você precisa configurar seu próprio modelo de perfil para isso. Por favor, dê uma olhada aqui como fazer isso: docs.djangoproject.com/en/dev/topics/auth/…
pennersr
6
Isso é exatamente o que eu estava procurando - um campo adicional como cor favorita. Caso eu esteja interessado em dizer, a cor favorita, acredito que devo criar uma nova classe UserProfile e então usar o usuário como um campo um para um e a cor favorita como o campo adicional. Nesse caso, ainda posso usar um tipo de SignUpForm que você declarou (com a cor favorita) acima e conectar o ACCOUNT_SIGNUP_FORM_CLASS a ele ou preciso criar o formulário e gerenciar o salvamento dos dados em meu próprio código?
Shreyas
4
Claro, o mecanismo ACCOUNT_SIGNUP_FORM_CLASS ainda pode ser usado. Você só precisa ter certeza de que o método save () está implementado corretamente de forma que a cor favorita seja armazenada em qualquer modelo que você quiser.
pennersr
5
@pennersr - Como poderia fazer isso ACCOUNT_SIGNUP_FORM_CLASSapós o primeiro login social para coletar e salvar os campos do modelo de usuário personalizado? Além disso, o uso do modelo de usuário personalizado por AUTH_USER_MODELalterações de git: github.com/pennersr/django-allauth não são carregados em pypi.
Babu
23

Usando a solução sugerida por pennersr, recebi um aviso de suspensão de uso:

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

Isso ocorre porque a partir da versão 0.15, o método save foi substituído por um método def signup (solicitação, usuário).

Então, para resolver isso, o código do exemplo deve ser assim:

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
fonte
2
A resposta de @pennsesr agora foi editada para usar em signupvez de save.
Flimm 01 de
18

Aqui está o que funcionou para mim combinando algumas das outras respostas (nenhuma delas está 100% completa e SECA).

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

E em settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

Desta forma ele usa os formulários do modelo para que fique SECO, e usa o novo def signup. Tentei colocar, 'myproject.myapp.forms.SignupForm'mas de alguma forma resultou em um erro.

Howardwlo
fonte
usar 'yourapp.forms.SignupForm' em vez de 'myproject.myapp.forms.SignupForm' funcionou para mim também
alpalalpal
6

@Shreyas: A solução abaixo pode não ser a mais limpa, mas funciona. Por favor, deixe-me saber se você tem alguma sugestão para limpá-lo ainda mais.

Para adicionar informações que não pertencem ao perfil de usuário padrão, primeiro crie um modelo em yourapp / models.py. Leia a documentação geral do django para aprender mais sobre isso, mas basicamente:

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)

Em seguida, crie um formulário em 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()
marca
fonte
Isso é exatamente o que acabei usando para um aplicativo Django 2.0 que está executando o Wagtail CMS. Funcionou para inscrição regular, mas não tanto com o Social Auth, pelo que parece?
Kalob Taulien
Como eu adicionaria esse campo extra à página de administração do usuário no Wagtail?
Joshua
5

Em seu users/forms.pyvocê coloca:

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

Em settings.py você coloca:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

Desta forma, você não quebra o princípio DRY por multiplicidade Definição dos campos dos modelos de usuário.

Adam Dobrawy
fonte
4

Já tentei vários tutoriais diferentes e em todos falta algo, repetindo código desnecessário ou fazendo coisas esquisitas, segue abaixo minha solução que junta todas as opções que encontrei, está funcionando, já coloquei em produção MAS está ainda não me convenceu, pois esperava receber first_name e last_name dentro das funções que anexei a Users create para evitar a criação de um perfil dentro do formulário mas não consegui, pelo menos acho que vai te ajudar.

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
fonte
Ironicamente, faltam algumas coisas também. Os campos de Perfil não têm padrões, nem permitem null, portanto, seu create_user_profilesinal falha por design. Em segundo lugar, você pode reduzir isso para um sinal, com base em created, especialmente quando se fala em DRY. E, terceiro, você efetua um salvamento de Perfil chamando user.save () em sua visualização e salvando o perfil novamente com os dados reais.
Melvyn
@Melvyn que não deveria estar fields = [...]com colchetes em vez de fields = (...) parênteses?
Ahtisham
Pode, mas não tem que ser. É usado somente para leitura para verificar se o campo no modelo deve fazer parte do formulário. Portanto, pode ser uma lista, tupla ou conjunto ou qualquer derivado disso. Como as tuplas não são mutáveis, faz mais sentido usar tuplas e evitar mutações acidentais. Do ponto de vista do desempenho, essas coleções são, na prática, muito pequenas para ter qualquer impacto. Quando a coleção ficar muito longa, pode fazer sentido alternar para 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'

É isso. (:

Milovan Tomašević
fonte
-10

Crie um modelo de perfil com o usuário 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
fonte
3
Obrigado, mas não acho que isso seja tudo de que precisamos. Minha pergunta se refere especificamente ao aplicativo allauth.
Shreyas