Как настроить профиль пользователя при использовании django-allauth

107

У меня есть проект django с приложением django-allauth. Мне нужно получить дополнительные данные от пользователя при регистрации. Я столкнулся с аналогичным вопросом здесь, но, к сожалению, никто не ответил на часть настройки профиля.

Согласно предоставленной документацииdjango-allauth :

ACCOUNT_SIGNUP_FORM_CLASS(= None)

Строка, указывающая на настраиваемый класс формы (например ‘myapp.forms.SignupForm’), который используется во время регистрации, чтобы запросить у пользователя дополнительные данные (например, подписку на информационный бюллетень, дату рождения). Этот класс должен реализовать ‘save’метод, принимающий нового зарегистрированного пользователя в качестве единственного параметра.

Я новичок в django и борюсь с этим. Может ли кто-нибудь предоставить пример такого настраиваемого класса формы? Нужно ли мне добавить класс модели , а также со ссылкой на объект пользователя , как это ?

Шреяс
источник

Ответы:

170

Предположим, вы хотите спросить у пользователя его имя / фамилию во время регистрации. Вам нужно будет поместить эти поля в свою форму, например:

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

Затем в настройках укажите эту форму:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

Вот и все.

Pennersr
источник
10
Спасибо. Всегда приятно слышать от автора оригинала :). Нужно ли мне создавать дополнительный класс для хранения этой информации или allauth позаботится об этом автоматически?
Shreyas
12
На самом деле это зависит от информации, которую вы запрашиваете. В любом случае, это все выходит за рамки правдивости. Если вы запрашиваете имя / фамилию, как в приведенном выше примере, вам не нужна дополнительная модель, и вы можете помещать вещи непосредственно в модель User. Если вы спросите дату рождения пользователя, его любимый цвет или что-то еще, вам необходимо настроить для этого свою собственную модель профиля. Пожалуйста, посмотрите здесь, как это сделать: docs.djangoproject.com/en/dev/topics/auth/…
pennersr
6
Это именно то, что я искал - дополнительное поле вроде любимого цвета. Если меня интересует, скажем, любимый цвет, я считаю, что мне следует создать новый класс UserProfile, а затем использовать User как однозначное поле и любимый цвет как дополнительное поле. В этом случае могу ли я использовать объявленный вами тип SignUpForm (с любимым цветом) и подключить к нему ACCOUNT_SIGNUP_FORM_CLASS или мне нужно создать форму и обрабатывать сохранение данных в моем собственном коде?
Shreyas
4
Конечно, механизм ACCOUNT_SIGNUP_FORM_CLASS все еще можно использовать. Вам просто нужно убедиться, что метод save () реализован должным образом, чтобы любимый цвет сохранялся в любой модели, которую вы хотите.
pennersr
5
@pennersr - Как я могу сделать это ACCOUNT_SIGNUP_FORM_CLASSпосле самого первого входа в социальную сеть для сбора и сохранения пользовательских полей модели? Также использование пользовательской модели путем AUTH_USER_MODELизменений из git: github.com/pennersr/django-allauth не загружается в pypi.
Babu
23

Используя решение, предложенное pennersr, я получал DeprecationWarning:

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

Это связано с тем, что с версии 0.15 метод сохранения устарел в пользу метода регистрации def (запрос, пользователь).

Итак, чтобы решить эту проблему, код примера должен быть таким:

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
источник
2
Ответ @pennsesr теперь отредактирован для использования signupвместо save.
Flimm 01
18

Вот что сработало для меня, объединив несколько других ответов (ни один из них не является на 100% полным и СУХИМ).

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

И в settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

Таким образом, он использует формы модели, так что они СУХИЕ, и использует новые def signup. Я пытался поставить, 'myproject.myapp.forms.SignupForm'но это как-то привело к ошибке.

Howardwlo
источник
использование yourapp.forms.SignupForm вместо myproject.myapp.forms.SignupForm у меня тоже
сработало
6

@Shreyas: приведенное ниже решение может быть не самым чистым, но оно работает. Пожалуйста, дайте мне знать, если у вас есть предложения по дальнейшему очищению.

Чтобы добавить информацию, которая не принадлежит профилю пользователя по умолчанию, сначала создайте модель в yourapp / models.py. Прочтите общую документацию по django, чтобы узнать об этом больше, но в основном:

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)

Затем создайте форму в 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()
отметка
источник
Это именно то, что я в итоге использовал для приложения Django 2.0, работающего под управлением Wagtail CMS. Работает для регулярной регистрации, но, похоже, в меньшей степени с Social Auth?
Kalob Taulien 06
Как мне добавить это дополнительное поле на страницу администратора для пользователя в Wagtail?
Джошуа
5

В вашем users/forms.pyвы кладете:

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

В settings.py вы помещаете:

ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'

Таким образом, вы не нарушите принцип DRY на определение полей множественности моделей пользователей.

Адам Добрави
источник
4

Я пробовал много разных руководств, и во всех них чего-то не хватает, повторения ненужного кода или странных вещей, ниже следует мое решение, которое объединяет все варианты, которые я нашел, оно работает, я уже запустил его в производство, НО это все еще не убеждает меня, потому что я ожидал получить first_name и last_name внутри функций, которые я прикрепил к Users, чтобы избежать создания профиля внутри формы, но я не мог, кстати, я думаю, что это поможет вам.

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'
Грегори
источник
По иронии судьбы, здесь тоже не хватает некоторых вещей. Поля профиля не имеют значений по умолчанию и не допускают нулевого значения, поэтому ваш create_user_profileсигнал не работает по дизайну. Во-вторых, вы можете уменьшить это значение до одного сигнала created, особенно при разговоре DRY. И в-третьих, вы выполняете сохранение профиля, вызывая user.save () в вашем представлении, а затем снова сохраняете профиль с фактическими данными.
Мелвин
@Melvyn Разве это не должно быть fields = [...]с квадратными скобками вместо fields = (...) скобок?
Ахтишам
Может, но не обязательно. Он используется только для чтения, чтобы проверить, должно ли поле модели быть частью формы. Таким образом, это может быть список, кортеж или набор или любая их производная. Поскольку кортежи неизменяемы, имеет смысл использовать кортежи и предотвращать случайные мутации. С точки зрения производительности эти коллекции на практике слишком малы, чтобы оказать какое-либо влияние. Если коллекция станет слишком длинной, возможно, имеет смысл переключиться на нее exclude.
Мелвин
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'

Вот и все. (:

Милован Томашевич
источник
-10

Создайте модель профиля с пользователем как 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'))
Джубин Томас
источник
3
Спасибо, но я не думаю, что это все, что нужно. Мой вопрос конкретно относится к приложению allauth.
Shreyas 06