如何在使用django-allauth时定制用户配置文件

117

我有一个使用django-allauth应用程序的Django项目。我需要在注册时从用户那里收集其他数据。我遇到了一个类似的问题在这里,但不幸的是,没有人回答配置个人资料的部分。

根据提供给django-allauth的文档

ACCOUNT_SIGNUP_FORM_CLASS(=None

一个字符串,指向自定义表单类(例如'myapp.forms.SignupForm'),该类在注册期间用于向用户询问其他输入(例如新闻通讯注册,出生日期)。此类应实现一个'save'方法,接受新注册的用户作为其唯一参数。

我是django的新手,对此感到困惑。能否有人提供一个这样的自定义表单类的例子?我需要添加一个模型类,并将其链接到用户对象,例如this吗?

8个回答

181

假设您在注册时想要询问用户的名字和姓氏。您需要将这些字段添加到自己的表单中,如下所示:

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'
请注意,SignupForm不能与通过ACCOUNT_FORMSSOCIALACCOUNT_FORMS进行的表单重写定义在同一文件中,因为这会导致循环导入错误。
就这些。

11
谢谢。从原始作者那里听到消息总是很好的 :)。我需要创建一个额外的类来存储这些信息吗,还是allauth会自动处理? - Shreyas
13
这实际上取决于你所询问的信息。无论如何,这都超出了allauth的范围。如果像上面的例子那样要求名字和姓氏,那么就不需要额外的模型,可以直接将这些内容放入用户模型中。如果你要问用户的出生日期、最喜欢的颜色或其他信息,那么你需要为此设置自己的个人资料模型。请参考这里的操作方法:https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users - pennersr
6
这正是我在寻找的——像喜爱颜色这样的附加字段。如果我对喜爱颜色感兴趣,我认为我应该创建一个新的UserProfile类,然后将User作为一对一字段,将喜爱颜色作为额外字段。在这种情况下,我仍然可以使用您上面声明的带有喜爱颜色的SignUpForm类型,并将ACCOUNT_SIGNUP_FORM_CLASS与其连接吗?还是我需要创建表单并在自己的代码中处理数据保存? - Shreyas
4
可以使用ACCOUNT_SIGNUP_FORM_CLASS机制,只需确保适当实现save()方法以使喜欢的颜色存储在您想要的任何模型中。 - pennersr
5
在第一次社交登录之后,如何修改ACCOUNT_SIGNUP_FORM_CLASS以收集并保存自定义用户模型字段?另外,由于AUTH_USER_MODEL的自定义用户模型的使用方式在git:https://github.com/pennersr/django-allauth中的更改没有上传到pypi。 - Babu
显示剩余18条评论

24

使用pennersr建议的解决方案时,我收到了一个DeprecationWarning警告:

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

这是因为自0.15版本起,保存方法已被弃用,建议使用def signup(request, user)方法。因此,为了解决这个问题,示例代码应该像这样:
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()

2
@pennsesr的回答现已被编辑为使用“signup”而不是“save”。 - Flimm

20

以下是对我起作用的,结合其他答案的一些方法(它们都不是100%完整和DRY)。

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'

这样它使用模型表单,使其DRY,并使用新的def signup。我尝试放置'myproject.myapp.forms.SignupForm',但结果出现了错误。


使用“yourapp.forms.SignupForm”而不是“myproject.myapp.forms.SignupForm”对我也起作用了。 - alpalalpal

7

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

这正是我最终在运行Wagtail CMS的Django 2.0应用程序中使用的内容。它适用于常规注册,但似乎对Social Auth不太适用? - Kalob Taulien
1
我该如何在Wagtail的管理员页面中为用户添加额外字段? - Joshua

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,以避免在表单中创建个人资料,但我无法实现。顺便说一下,我认为这会对你有所帮助。
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()来实现Profile保存,然后再次使用实际数据保存配置文件。 - user1600649
@Melvyn 应该使用方括号而不是圆括号,即fields = [...]而不是fields = (...) - Ahtisham
它可以是,但不一定要是。它仅用于只读模式,以检查模型上的字段是否应该成为表单的一部分。因此,它可以是列表、元组、集合或任何派生物。由于元组是不可变的,使用元组并防止意外变异更有意义。从性能角度来看,这些集合在实践中太小,没有任何影响。一旦集合变得太长,可能需要切换到“排除”模式。 - user1600649

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
创建一个Profile模型,其中包含一个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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接