多用户类型使用django-allauth

时间:2017-06-12 17:20:02

标签: django django-forms django-allauth

修改

  

请不要浪费你的时间阅读这个问题......这是错误的做法!

     

查看我自己的答案,了解正确解决方案的分步指南(及解释)

TL; DR

如何使用django-allauth为私人和公司用户注册?

我跟随的方法(它是否正确?)

我有以下models

class PrivateUser(models.Model):
    """Models a private user account"""
    user = models.OneToOneField(User, on_delete=models.CASCADE)


class CompanyUser(models.Model):
    """Models the company's contact person user account"""
    user = models.OneToOneField(User, on_delete=models.CASCADE)


class Company(models.Model):
    """Models the company attributes"""
    contact_person = models.OneToOneField(User, related_name='company')
    name = models.CharField(max_length=50, null=False, blank=False)
    vat_no = models.CharField(
        # some config and validators
    )
    # ... other non-relevant fields

现在,我必须在注册过程中区分两个用户PrivateUserCompanyUser,django-allauth只有official django-allauth documentation中指定的一个注册表单:< / p>

  

ACCOUNT_SIGNUP_FORM_CLASS(=无)

     

指向自定义表单类的字符串(例如myapp.forms.SignupForm)      在注册期间用于询问用户是否有其他输入      (例如通讯报名,出生日期)。这个类应该实现一个      def signup(self, request, user)方法,用户表示      新注册的用户。

因此,为了创建一个独特的表单,我创建了一个包含所有的抽象模型类 PrivateUserCompanyUser加一个字段(请注意user_type字段):

class AbstractComprehensiveUser(models.Model):
    """
    Little hackish model class needed to handle one single sign up
    form for multiple users
    """

    USER_TYPE_CHOICES = (
        ('private', 'Private'),
        ('company', 'Company'),
    )

    user_type = models.CharField(
        max_length=10,
        blank=False,
        choices=USER_TYPE_CHOICES
    )

    # Common fields for either private and company users
    first_name = models.CharField(max_length=30, blank=False)
    last_name = models.CharField(max_length=30, blank=False)

    # Company specific fields
    company_name = models.CharField(max_length=50, null=True, blank=True)
    company_vat_no = models.CharField(
        # some config and validators
        null=True,
        blank = True
    )
    # other non-relevant fields

    class Meta:
        abstract = True

N.B:此类中的所有非公共字段都包含属性null=Trueblank=True

然后我创建了自定义SignupForm,如下所示:

class SignupForm(forms.ModelForm):
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)

    class Meta:
        model = AbstractComprehensiveUser
        fields = (
            # Field to differentiate from private and company
            # user sign up
            'user_type',
            # Common fields for either private and company users
            'first_name', 'last_name',
            # Company specifc fields
            'company_name', 'company_vat_no', # etc etc
        )

现在的想法是使用两种形式的模板:

  • 隐藏user_type='private'且隐藏first_namelast_name字段的
  • 隐藏user_type='company'的内容和Company模型
  • 中的字段

然后,在SignupForm我将收到user_type字段,我可以设置正确的表单,例如:

class PrivateUserSignupForm(forms.ModelForm):
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)

    class Meta:
        model = PrivateUser
        fields = ('first_name', 'last_name')

问题在于,当我在SignupForm.signup()方法中检索数据时,User模型已经写入数据库。

我想不保存它,但只是:

  • 验证
  • signup方式接收数据以填充正确的表单(PrivateUserSignupFormCompanyUserSignupForm
  • 验证表单
    • 如果没有错误,请保存用户和其他模型
    • 如果出现错误,请不保存任何内容并向用户发出有关错误的警告

问题是......

  • 这种方法是否正确?如果没有这些补偿,还有其他方法可以实现这一目标吗?
  • 如果这种方法是正确的,我怎么能处理上面描述的工作流程?

2 个答案:

答案 0 :(得分:5)

TL; DR

我上面写的所有杂乱的东西都是垃圾!

(最终)正确解决方案

settings.py删除ACCOUNT_SIGNUP_FORM_CLASS时,我们不会使用它。

假设有以下models

class PrivateUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

class CompanyUser(models.Model):
    contact_person = models.OneToOneField(User, on_delete=models.CASCADE)
    company_name = models.CharField(max_length=50, null=False, blank=False)

现在,我们想要的是让我们的应用以不同的形式注册PrivateUserCompanyUser

为了实现这一点,我们将扩展django-allauth的SignupFormSignupView

forms.py

from myapp.models import CompanyUser

class CompanySignupForm(SignupForm):
    # declare here all the extra fields in CompanyUser model WITHOUT
    # the OneToOneField to User
    # (N.B: do NOT try to declare Meta class with model=CompanyUser,
    # it won't work!)
    company_name = forms.CharField(max_length=50, required=True, strip=True)

    # Override the save method to save the extra fields
    # (otherwise the form will save the User instance only)
    def save(self, request):
        # Save the User instance and get a reference to it
        user = super(CompanySignupForm, self).save(request)
        # Create an instance of your model with the extra fields
        # then save it.
        # (N.B: the are already cleaned, but if you want to do some
        # extra cleaning just override the clean method as usual)
        company_user = CompanyUser(
            contact_person=user,
            company_name=self.cleaned_data.get('company_name')
        )
        company_user.save()

        # Remember to return the User instance (not your custom user,
        # the Django one), otherwise you will get an error when the
        # complete_signup method will try to look at it.
        return company_user.contact_person

现在,我们有CompanyUser模型和CompanySignupForm表单。我们使用以下代码在CompanyUserSignupView中创建views.py视图:

class CompanyUserSignupView(SignupView):
    # The referenced HTML content can be copied from the signup.html
    # in the django-allauth template folder
    template_name = 'account/signup_company.html'
    # the previously created form class
    form_class = CompanySignupForm

    # the view is created just a few lines below
    # N.B: use the same name or it will blow up
    view_name = 'company_signup'

    # I don't use them, but you could override them
    # (N.B: the following values are the default)
    # success_url = None
    # redirect_field_name = 'next'

# Create the view (we will reference to it in the url patterns)
company_signup = CompanyUserRegistrationView.as_view()

最后一步,urls.py

urlpatterns = [
    # ...
    url(
        r'^accounts/signup/company/$',
        views.company_signup,
        name='signup-company'
    ),
]

现在,只需使用您的浏览器转到http://localhost:8000/accounts/signup/company(或根据您的配置使用正确的网址格式)。

您将找到额外的字段,您可以注册公司用户。

现在重复上述所有步骤,以创建PrivateSignupForm表单,PrivateUserSignupView视图,并添加正确的网址格式,让用户注册为私有。

  

上次警告

     

除非你覆盖,否则django-allauth默认注册网址仍然有效   它与你的一个网址...你应该这样做!

答案 1 :(得分:5)

我遇到了同样的问题。我需要将allauth用于不同的用户配置文件类型。我扩展了allauth SignupView并将其用作我的案例我有一个MemberProfile和PartnerProfile:

#profile models

class MemberProfile(models.Model):
  user = models.OneToOneField(
    settings.AUTH_USER_MODEL,
    on_delete=models.CASCADE,
  )


class PartnerProfile(models.Model):
  user = models.OneToOneField(
    settings.AUTH_USER_MODEL,
    on_delete=models.CASCADE,
  )

我想为每种类型的个人资料提供单独的注册页面。幸运的是,allauth SignupView将用户存储在form_value()方法中的实例上。我将SignupView扩展为ProfileView,它需要profile_class:

#mixin

from allauth.account.views import SignupView
from allauth.account.forms import SignupForm


class ProfileSignupView(SignupView):

  template_name = 'profiles/register.html'
  success_url = ''  # profile specific success url
  form_class = SignupForm
  profile_class = None  # profile class goes here

  def form_valid(self, form):
    response = super(ProfileSignupView, self).form_valid(form)
    profile = self.profile_class(user=self.user)
    profile.save()

    return response

然后我的观点如下:

#views

from .mixins import ProfileSignupView
from .models import PartnerProfile, MemberProfile

class MemberSignupView(ProfileSignupView):

   success_url = '/member/profile'
   profile_class = MemberProfile


class PartnerSignupView(ProfileSignupView):

    success_url = '/partner/profile'
    profile_class = PartnerProfile