请不要浪费时间阅读问题...这是错误的方法!
查看我自己的答案,获取正确解决方案的分步指南(带解释)
如何使用 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
现在,我必须在注册过程中区分两个用户
PrivateUser
和 CompanyUser
,django-allauth 只有一张注册表单,如 官方 django-allauth 文档中指定的那样:
因此,为了创建一个独特的形式,我创建了一个抽象模型类,其中包含所有ACCOUNT_SIGNUP_FORM_CLASS(=无)
指向自定义表单类的字符串(例如
myapp.forms.SignupForm
) 在注册过程中用于要求用户提供额外的输入 (例如时事通讯注册、出生日期)。这个类应该实现一个def signup(self, request, user)
方法,其中 user 代表 新注册用户。
PrivateUser
和
CompanyUser
字段加一(注意
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
注意:此类中所有非公共字段都具有属性 null=True
和
blank=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_name
和
last_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
方法中接收数据以填充正确的表单(
PrivateUserSignupForm
或
CompanyUserSignupForm
)
(最终)正确的解决方案
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)
现在,我们想要的是让我们的应用程序以不同的形式注册
PrivateUser
和
CompanyUser
。为了实现这一目标,我们将扩展 django-allauth 的
SignupForm
和
SignupView
。在
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(或根据您的配置使用正确的 URL 模式)。
您将找到额外的字段,您可以注册公司用户。现在重复前面的所有步骤来创建一个
PrivateSignupForm
表单、一个
PrivateUserSignupView
视图并添加正确的 url 模式以让用户以私人身份注册。
最后警告
django-allauth 默认注册 url 仍然有效,除非您覆盖 它与您的网址之一...您应该这样做!
#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
模型.py
class AccountTypeOne(User):
class Meta:
proxy = True
class AccountTypeTwo(User):
class Meta:
proxy = True
表格.py
class AccountSignupForm(SignupForm):
USER_CHOICES = (
("type_one", "TypeOne"),
("type_two", "TypeTwo"),
)
account_type = forms.ChoiceField(choices=USER_CHOICES, widget=forms.Select)
def save(self, request):
account_type = self.cleaned_data.get("account_type")
if account_type == "type_one":
user = AccountTypeOne()
elif account_type == "type_two":
user = AccountTypeTwo()
else:
raise ValueError("Invalid user type")
user.username = self.cleaned_data.get("username")
user.email = self.cleaned_data.get("email")
user.set_password(self.cleaned_data.get("password1"))
user.save()
return user
希望有帮助!