Jak dostosować profil użytkownika podczas korzystania z django-allauth

107

Mam projekt django z aplikacją django-allauth. Podczas rejestracji muszę zebrać dodatkowe dane od użytkownika. Natknąłem się tutaj na podobne pytanie, ale niestety nikt nie odpowiedział na część dotyczącą dostosowywania profilu.

Zgodnie z dostarczoną dokumentacjądjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

Ciąg znaków wskazujący na niestandardową klasę formularza (np. ‘myapp.forms.SignupForm’), Który jest używany podczas rejestracji, aby poprosić użytkownika o dodatkowe informacje (np. Zapis do newslettera, data urodzenia). Ta klasa powinna implementować ‘save’metodę, akceptując nowo zarejestrowanego użytkownika jako jedyny parametr.

Jestem nowy w django i walczę z tym. Czy ktoś może podać przykład takiej niestandardowej klasy formularza? Czy muszę dodać również klasę modelu z odsyłaczem do obiektu użytkownika w ten sposób ?

Shreyas
źródło

Odpowiedzi:

170

Załóżmy, że chcesz zapytać użytkownika o jego imię / nazwisko podczas rejestracji. Musisz umieścić te pola we własnym formularzu, na przykład:

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

Następnie w ustawieniach wskaż ten formularz:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

To wszystko.

pennersr
źródło
10
Dzięki. Zawsze dobrze jest usłyszeć od autora oryginału :). Czy muszę utworzyć dodatkową klasę, aby przechowywać te informacje, czy też allauth zajmie się tym automatycznie?
Shreyas,
12
To właściwie zależy od informacji, o które prosisz. W każdym razie to wszystko wykracza poza zakres prawdy. Jeśli poprosisz o imię / nazwisko jak w powyższym przykładzie, nie potrzebujesz dodatkowego modelu i możesz umieścić rzeczy bezpośrednio w modelu użytkownika. Jeśli chcesz zapytać o datę urodzenia użytkownika, jego ulubiony kolor lub cokolwiek innego, musisz ustawić własny model profilu. Proszę spojrzeć tutaj, jak to zrobić: docs.djangoproject.com/en/dev/topics/auth/ ...
pennersr
6
Dokładnie tego szukałem - dodatkowego pola w postaci ulubionego koloru. Jeśli interesuje mnie, powiedzmy, ulubiony kolor, uważam, że powinienem utworzyć nową klasę UserProfile, a następnie użyć pola User jako pola jeden do jednego i ulubionego koloru jako pola dodatkowego. W takim przypadku, czy nadal mogę użyć typu formularza SignUpForm, który zadeklarowałeś (z ulubionym kolorem) powyżej i podłączyć do niego ACCOUNT_SIGNUP_FORM_CLASS, czy też muszę utworzyć formularz i obsłużyć zapisywanie danych we własnym kodzie?
Shreyas,
4
Oczywiście, nadal można używać mechanizmu ACCOUNT_SIGNUP_FORM_CLASS. Musisz tylko upewnić się, że metoda save () jest poprawnie zaimplementowana, tak aby ulubiony kolor był przechowywany w dowolnym modelu.
pennersr
5
@pennersr - Jak mogę to zrobić ACCOUNT_SIGNUP_FORM_CLASSpo pierwszym logowaniu w serwisach społecznościowych, aby zebrać i zapisać pola niestandardowego modelu użytkownika? Również użycie niestandardowego modelu użytkownika przez AUTH_USER_MODELzmiany z git: github.com/pennersr/django-allauth nie są przesyłane w pypi.
Babu
23

Korzystając z rozwiązania zaproponowanego przez pennersra, otrzymałem Ostrzeżenie o wycofaniu:

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

Dzieje się tak, ponieważ od wersji 0.15 metoda zapisu została wycofana na korzyść metody rejestracji def (żądanie, użytkownik).

Aby rozwiązać ten problem, kod przykładu powinien wyglądać następująco:

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
źródło
2
Odpowiedź @ pennsesr została zmieniona tak, aby używać signupzamiast save.
Flimm
18

Oto, co zadziałało w moim przypadku, łącząc kilka innych odpowiedzi (żadna z nich nie jest w 100% kompletna i SUCHA).

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

A w settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

W ten sposób używa form wzorcowych, aby były SUCHE i używa nowych def signup. Próbowałem umieścić, 'myproject.myapp.forms.SignupForm'ale to jakoś spowodowało błąd.

Howardwlo
źródło
użycie „yourapp.forms.SignupForm” zamiast „myproject.myapp.forms.SignupForm” również
zadziałało
6

@Shreyas: Poniższe rozwiązanie może nie jest najczystsze, ale działa. Daj mi znać, jeśli masz jakieś sugestie dotyczące dalszego uporządkowania.

Aby dodać informacje, które nie należą do domyślnego profilu użytkownika, najpierw utwórz model w yourapp / models.py. Przeczytaj ogólną dokumentację django, aby dowiedzieć się więcej na ten temat, ale zasadniczo:

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)

Następnie utwórz formularz w 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()
znak
źródło
To jest dokładnie to, czego ostatecznie użyłem w aplikacji Django 2.0, która działa pod kontrolą Wagtail CMS. Pracowałeś dla zwykłej rejestracji, ale wydaje się, że mniej z uwierzytelnianiem społecznościowym?
Kalob Taulien
Jak dodać to dodatkowe pole do strony administratora dla użytkownika w Pliszka?
Joshua,
5

W swoim users/forms.pyumieszczasz:

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

W settings.py umieściłeś:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

W ten sposób nie łamiesz zasady DRY przez wielokrotność Definicja pól modeli użytkownika.

Adam Dobrawy
źródło
4

Wypróbowałem wiele różnych tutoriali i wszystkim brakuje czegoś, powtarzając niepotrzebny kod lub robiąc dziwne rzeczy, poniżej podążam za moim rozwiązaniem, które łączy wszystkie opcje, które znalazłem, działa, już je wdrożyłem, ALE to nadal mnie nie przekonuje, ponieważ spodziewałbym się otrzymywać imię i nazwisko w funkcjach, które dołączyłem do użytkowników, aby uniknąć tworzenia profilu w formularzu, ale nie mogłem, na razie myślę, że to ci pomoże.

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
źródło
Jak na ironię, brakuje też kilku rzeczy. Pola profilu nie mają wartości domyślnych ani nie zezwalają na null, więc create_user_profilesygnał nie działa zgodnie z projektem. Po drugie, możesz zredukować to do jednego sygnału, na podstawie created, zwłaszcza gdy mówisz DRY. Po trzecie, zapisujesz profil, wywołując user.save () w swoim widoku, a następnie zapisując profil ponownie z rzeczywistymi danymi.
Melvyn
@Melvyn Czy nie powinno być fields = [...]z nawiasami kwadratowymi zamiast fields = (...) nawiasów?
Ahtisham,
Może, ale nie musi. Jest używany tylko do odczytu, aby sprawdzić, czy pole w modelu powinno być częścią formularza. Może to być lista, krotka lub zbiór lub dowolna ich pochodna. Ponieważ krotki nie są zmienne, bardziej sensowne jest używanie krotek i zapobieganie przypadkowym mutacjom. Z punktu widzenia wydajności kolekcje te są w praktyce zbyt małe, aby mieć jakikolwiek wpływ. Gdy kolekcja stanie się zbyt długa, warto przełączyć się na 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'

To jest to. (:

Milovan Tomašević
źródło
-10

Utwórz model profilu z użytkownikiem jako 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
źródło
3
Dzięki, ale nie sądzę, że to wszystko, co jest potrzebne. Moje pytanie dotyczy konkretnie aplikacji allauth.
Shreyas,