使用django-allauth注册多个用户类型 [英] Multiple user type sign up with django-allauth

查看:75
本文介绍了使用django-allauth注册多个用户类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑




请不要浪费时间阅读问题……这是错误的方法!



看看我自己的答案,以找到正确解决方案的分步指南(带说明)




TL; DR



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



我正在采用的方法(是否正确?)



我有以下模型

  class PrivateUser(models.Model):
为私人用户帐户建模
用户= models.OneToOneField(User,on_delete = models.CASCADE)


class CompanyUser(models.Model):
公司的联系人用户帐户
user = models.OneToOneField(User,on_delete = models.CASCADE)


class Company(models.Model):
建模公司属性
contact_person = m odels.OneToOneField(User,related_name ='company')
name = models.CharField(max_length = 50,null = False,blank = False)
vat_no = models.CharField(
#一些配置和验证器

#...其他不相关字段

现在,我必须在注册过程中区分两个用户 PrivateUser CompanyUser ,而django-allauth仅官方django中指定的一种注册表单-allauth文档


ACCOUNT_SIGNUP_FORM_CLASS(=无)



指向自定义表单类的字符串(例如 myapp.forms.SignupForm
,用于在注册期间要求用户提供其他输入
(例如,简报注册,生日)。此类应实现
def signup(自身,请求,用户)方法,其中user表示新注册的
用户。


因此,为了创建唯一的表单,我创建了一个抽象模型类,其中包含 PrivateUser CompanyUser 加一个(请注意 user_type 字段):

 类AbstractComprehensiveUser(models.Model):

需要处理一个单一注册$ b的小的hackish模型类$ b多个用户的表单


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


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


#私人和公司用户的公用字段
first_name = models.CharField(max_length = 30,blank = Fals e)
last_name = models.CharField(max_length = 30,blank = False)

#公司特定字段
company_name = models.CharField(max_length = 50,null = True, blank = True)
company_vat_no = models.CharField(
#一些配置和验证器
null = True,
blank = True

#其他非相关字段

class Meta:
abstract = True

注意:在此类中所有非常见字段的属性为 null = True blank = True



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

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

类元:
模型= AbstractComprehensiveUser
字段=(
#区分私有和c的字段公司
#用户注册
'user_type',
#私人和公司用户的公用字段
'first_name','last_name',
#公司指定字段
'company_name','company_vat_no',#等等等

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




  • 一个隐藏了 user_type ='private'的模板以及 first_name last_name 字段

  • 隐藏了 user_type ='company'的人和 Company 模型中的字段



然后,在 SignupForm 中,我将收到 user_type 字段,然后可以设置适当的格式,例如:

  class PrivateUserSignupForm(forms.ModelForm):
first_name =形式.CharField(最大长度= 30)
last_name =形式.CharField(最大长度= 30)

类元:
模型= PrivateUser
字段=('first_name','last_name')

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



我不想保存它,只是:




  • 验证它

  • signup 方法中接收数据以填充正确的格式( PrivateUserSignupForm CompanyUserSignupForm

  • 验证表单


    • 在没有错误的情况下保存用户和其他模型

    • 在没有错误的情况下不要保存任何内容并警告用户有关错误




问题是...




  • 这种方法正确吗?如果没有这种编译方法,还有其他方法可以实现此目标吗?

  • 如果这种方法正确,那么我该如何处理上述工作流程?


解决方案

TL; DR



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

(最终)正确解决方案



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



假设具有以下模型

  class PrivateUser(models.Model):
用户=模型。 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)

现在,我们想要什么是让我们的应用使用diff注册 PrivateUser CompanyUser 错误的表单。



要实现这一点,我们将扩展django-allauth的 SignupForm SignupView



forms.py 中:

 从myapp.models导入CompanyUser 

类CompanySignupForm(SignupForm):
#在此处声明CompanyUser模型中的所有其他字段没有
#用户的OneToOneField
#(注意:不要尝试使用model = CompanyUser,
#声明元类!)
company_name = form。 CharField(max_length = 50,required = True,strip = True)

#覆盖save方法以保存多余的字段
#(否则该表单将仅保存User实例)
def save(self,request):
#保存用户实例并获得对它的引用
user = super(CompanySignupForm,self).save(request)
#创建一个实例带有额外字段
#的模型,然后将其保存。
#(注意:已经清洗过了,但是如果您想做一些
#额外的清洗工作,请像往常一样覆盖清理方法)
company_user = CompanyUser(
contact_person = user ,
company_name = self.cleaned_data.get('company_name')

company_user.save()

#记住要返回User实例(不是您的自定义用户,
#Django用户),否则当
#complete_signup方法尝试查看它时,您将得到一个错误。
return company_user.contact_person

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

  class CompanyUserSignupView(SignupView):
#可以从signup.html
#复制引用的HTML内容在django-allauth模板文件夹中
template_name ='account / signup_company.html'
#先前创建的表单类
form_class = CompanySignupForm

#仅在$ b $以下几行创建视图b#注意:使用相同的名称,否则会炸掉
view_name ='company_signup'

#我不使用它们,但是您可以覆盖它们
#(注意:以下值为默认值)
#success_url = None
#redirect_field_name ='next'

#创建视图(我们将在url模式中引用该视图)
company_signup = CompanyUserRegistrationView.as_view()

最后一步, urls .py

  urlpatterns = [
#...
url(
r'^帐户/注册/公司/ $',
views.company_signup,
name ='signup-company'
),
]

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



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



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


最后警告



除非您将
替换为您的其中一个url,否则django-allauth默认注册URL仍然可以使用。



EDIT

Please, do not waste your time reading the question... it is the wrong approach!

Look at my own answer for a step-by-step guide (with explanation) of the right solution

TL;DR

How could I implement sign up for private and company users using django-allauth?

The approach I'm following (is it correct?)

I have the following 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

Now, I have to distinguish between the two users PrivateUser and CompanyUser during the sign up process with django-allauth having just one sign up form as specified in the official django-allauth documentation:

ACCOUNT_SIGNUP_FORM_CLASS (=None)

A string pointing to a custom form class (e.g. myapp.forms.SignupForm) that is used during signup to ask the user for additional input (e.g. newsletter signup, birth date). This class should implement a def signup(self, request, user) method, where user represents the newly signed up user.

So, to create a unique form I created an abstract model class with all the fields from the PrivateUser and the CompanyUser plus one (note the user_type field):

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: all the non-common fields have in this class the attributes null=True and blank=True.

Then I created my custom SignupForm as follow:

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
        )

The idea, now, is to use a template with two forms:

  • the one with hidden user_type='private' and just the first_name and last_name fields
  • the one with hidden user_type='company' and the fields from Company model

Then, in the SignupForm I will receive the user_type field and I could set the proper form, for example:

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

The problem is that when I retrieve data in the SignupForm.signup() method, the User model is already written in the database.

I would like to do not save it, but just:

  • validating it
  • receive data in the signup method to populate the correct form (PrivateUserSignupForm or CompanyUserSignupForm)
  • validate the form
    • in case of no errors save the user and the other models
    • in case of error do not save nothing and warn the user about the error(s)

The question are...

  • is this approach correct? There's some other way to accomplish this without these compilcation?
  • if this approach is correct, how could I handle the workflow described just above?

解决方案

TL;DR

All the messy stuff I wrote above are junk!

The (final) right solution

In settings.py remove ACCOUNT_SIGNUP_FORM_CLASS, we won't use it.

Suppose to have the following 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)

Now, what we want is to let our app signup the PrivateUser and the CompanyUser with different forms.

To accomplish that we'll extends the django-allauth's SignupForm and SignupView.

In 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

Now, we have CompanyUser model and CompanySignupForm form. Let's create a CompanyUserSignupView view in views.py with the following code:

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

Last step, the urls.py:

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

Now, just use your browser to go to http://localhost:8000/accounts/signup/company (or the proper url pattern based on your configuration).

You will find the extra fields and you can signup a company user.

Now repeat all the previous steps to create a PrivateSignupForm form, a PrivateUserSignupView view and add the proper url pattern to let users signup as privates.

LAST WARNING

The django-allauth default signup url will still works unless you override it with one of your url... and you should do that!

这篇关于使用django-allauth注册多个用户类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆