【问题标题】:How to customize user profile when using django-allauth使用 django-allauth 时如何自定义用户配置文件
【发布时间】:2012-08-31 11:22:26
【问题描述】:

我有一个带有 django-allauth 应用程序的 django 项目。我需要在注册时从用户那里收集其他数据。我遇到了类似的question here,但不幸的是,没有人回答配置文件自定义部分。

the documentation provided for django-allauth

ACCOUNT_SIGNUP_FORM_CLASS (=None)

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

我是 django 的新手,正在为此苦苦挣扎。有人可以提供这种自定义表单类的示例吗?我是否还需要添加一个模型类以及指向用户对象的链接,例如 this

【问题讨论】:

    标签: django profile django-allauth


    【解决方案1】:

    假设您想在注册期间询问用户他的名字/姓氏。您需要将这些字段放入您自己的表单中,如下所示:

    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 覆盖的表单在同一文件中定义,因为这会导致循环导入错误。

    就是这样。

    【讨论】:

    • 谢谢。听到原作者的来信总是很高兴:)。我是否需要创建一个额外的类来存储这些信息,还是 allauth 会自动处理这些信息?
    • 这实际上取决于您要询问的信息。无论如何,这都超出了 allauth 的范围。如果您像上面的示例一样询问名字/姓氏,那么您不需要额外的模型,可以直接将内容放入 User 模型中。如果您要询问用户的生日、他最喜欢的颜色或其他信息,那么您需要为此设置自己的配置文件模型。请在此处查看如何执行此操作:docs.djangoproject.com/en/dev/topics/auth/…
    • 这正是我想要的——一个额外的字段,比如最喜欢的颜色。如果我有兴趣说最喜欢的颜色,我相信我应该创建一个新的 UserProfile 类,然后使用 User 作为一对一字段,最喜欢的颜色作为附加字段。在这种情况下,我是否仍然可以使用您在上面声明的类型的 SignUpForm(使用最喜欢的颜色)并将 ACCOUNT_SIGNUP_FORM_CLASS 连接到它,还是我需要创建表单并在我自己的代码中处理数据的保存?
    • 当然,ACCOUNT_SIGNUP_FORM_CLASS 机制仍然可以使用。您只需确保 save() 方法已正确实现,以便将喜欢的颜色存储在您想要的任何模型中。
    • @pennersr - 在第一次社交登录以收集和保存自定义用户模型字段后,我如何才能使用此 ACCOUNT_SIGNUP_FORM_CLASS?此外,AUTH_USER_MODEL 对自定义用户模型的使用从 git:github.com/pennersr/django-allauth 更改不会上传到 pypi。
    【解决方案2】:

    使用 pennersr 建议的解决方案,我收到了 DeprecationWarning:

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

    这是因为as of version 0.15 save 方法已被弃用,取而代之的是 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()
    

    【讨论】:

    • @pennsesr 的答案现已编辑为使用 signup 而不是 save
    【解决方案3】:

    这就是结合其他一些答案对我有用的方法(没有一个是 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',但不知何故导致了错误。

    【讨论】:

    • 使用 'yourapp.forms.SignupForm' 而不是 'myproject.myapp.forms.SignupForm' 也对我有用
    【解决方案4】:

    @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 应用程序的内容。为常规注册工作,但似乎不太适合社交身份验证?
    • 如何将这个额外的字段添加到 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 原则。

    【讨论】:

      【解决方案6】:

      我尝试了许多不同的教程,但它们都缺少一些东西,重复不必要的代码或做奇怪的事情,下面是我的解决方案,它加入了我找到的所有选项,它正在工作,我已经把它放进去了生产但它仍然不能说服我,因为我希望在我附加到用户创建的函数中接收 first_name 和 last_name 以避免在表单中创建配置文件,但我不能,到目前为止,我认为它会对你有所帮助。

      模型.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'
      

      【讨论】:

      • 具有讽刺意味的是,这也缺少一些东西。 Profile 字段没有默认值,也不允许为 null,因此您的 create_user_profile 信号设计失败。其次,您可以根据created 将其减少为一个信号,尤其是在说话干燥时。第三,通过在视图中调用 user.save() 来实现配置文件保存,然后使用实际数据再次保存配置文件。
      • @Melvyn 不应该是带有方括号fields = [...]而不是fields = (...)括号吗?
      • 可以,但不是必须的。它仅以只读方式用于检查模型上的字段是否应该是表单的一部分。所以它可以是一个列表、元组或集合或其任何衍生物。由于元组是不可变的,因此使用元组并防止意外突变更有意义。从性能的角度来看,这些集合实际上太小而不会产生任何影响。一旦集合变得太长,改用exclude 可能是有意义的。
      【解决方案7】:
      #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'
      

      就是这样。 (:

      【讨论】:

        【解决方案8】:

        创建一个用户为 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'))
        

        【讨论】:

        • 谢谢,但我认为这还不够。我的问题专门针对 allauth 应用程序。
        猜你喜欢
        • 2013-12-06
        • 1970-01-01
        • 2019-10-05
        • 2018-01-15
        • 2012-01-18
        • 2018-08-19
        • 2013-09-08
        • 2017-02-10
        • 2017-04-10
        相关资源
        最近更新 更多