对于我的应用程序,我使用用户组来表示一种用户。在我的特定情况下,用户只能在一个组中。在实施中,我有两个选择:
我使用了选项2,因为有时候,让用户成为2组的一部分对于测试很有用(只是方便)。我不认为两者在实施方面存在差异(但建议表示赞赏)。
在我看来,然后我编写代码来关联这两个代码(这是User的UserProfile扩展类中的ManyToMany) - 我不确定这是否正常。
我遇到的主要错误是表单不允许验证,并且说ManyToMany需要“值列表”才能继续。
我有以下一组代码:
forms.py
from django.forms import ModelForm, Textarea
from django.contrib.auth.models import User, Group
from registration.models import UserProfile
from django import forms
from django.db import models
class RegistrationForm(ModelForm):
class Meta:
model = User
fields = ('username', 'password', 'email', 'first_name', 'last_name', 'groups')
widgets = {
'groups': forms.Select,
'password': forms.PasswordInput,
# 'text': Textarea(attrs = {'rows': 3, 'class': 'span10', 'placeholder': 'Post Content'}),
}
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['groups'].label = 'Which category do you fall under?'
views.py
def get_registration(request):
if request.method == 'POST':
register_form = RegistrationForm(request.POST)
company_form = CompanyRegistrationForm(request.POST, request.FILES)
if register_form.is_valid() and company_form.is_valid(): # check CSRF
if (request.POST['terms'] == True):
new_user = register_form.save()
new_company = company_form.save()
new_profile = UserProfile(user = user, agreed_terms = True)
new_profile.companies_assoc.add(new_company)
new_profile.save()
return HttpResponseRedirect(reverse('companyengine.views.get_company'))
return render(request, 'registration/register.html', { 'register_form': register_form, 'company_form': company_form } )
else:
first_form = RegistrationForm
second_form = CompanyRegistrationForm
return render(request, 'registration/register.html', { 'register_form': register_form, 'company_form': company_form } )
和 templates.html
<h2>Sign Up</h2>
<form action="/register" method="POST" enctype="multipart/form-data">{% csrf_token %}
<p>{{ register_form.non_field_error }}</p>
{% for field in register_form %}
<div class="control-group">
{{ field.errors }}
<label class="control-label">{{ field.label }}</label>
<div class="controls">
{{ field }}
</div>
</div>
{% endfor %}
<div id="company_fields">
<p>{{ register_form.non_field_error }}</p>
{% for field in company_form %}
<div class="control-group">
{{ field.errors }}
<label class="control-label">{{ field.label }}</label>
<div class="controls">
{{ field }}
</div>
</div>
{% endfor %}
</div>
<label><input type="checkbox" name="terms" id="terms"> I agree with the <a href="#">Terms and Conditions</a>.</label>
<input type="submit" value="Sign up" class="btn btn-primary center">
<div class="clearfix"></div>
</form>
一切似乎都非常好。但是表单不会超过is_valid(),因为“组”字段需要“值列表”。我见过其他人问过如何从TextField / TextArea解析信息,但我不明白为什么我需要拆分我的信息,因为它只有1。
非常感谢您的建议。
答案 0 :(得分:2)
首先,我认为您应该重新考虑使用M:M关系来表示1:M的关系。很可能在某些情况下,用户会获得多个组,并且可能会在稍后阶段导致代码中的错误,这些问题很难追踪。
由于您已经在使用UserProfile类,我会在用户配置文件模型上放置一个外键,这将提供应该存在的数据结构的准确表示(即使它意味着在测试时登录和注销)
您可以通过更改模型来执行此操作:
class UserProfile(models.Model):
# existing fields here
single_group = models.ForeignKey(Group)
如果您有许多使用现有用户组关系的现有代码,这是一个不太实际的解决方案。但是,如果你真的需要强制这个限制(每个用户/ userprofile一个组),那么这样做。
如果出于某种原因,您认为我的上述评论不合适(我不知道您的代码存在的具体情况)......
我认为您遇到的问题是由于select小部件将单个项目返回到表单,而SelectMultiple将返回值列表。由于表单需要一个列表,这就是您的问题所在。
我建议继承SelectMultiple小部件,以便它实际上在表单上呈现为选择单,但仍使用现有逻辑返回列表。
这是SelectMultiple Widget中的当前渲染功能:
class SelectMultiple(Select):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<select multiple="multiple"%s>' % flatatt(final_attrs)]
options = self.render_options(choices, value)
if options:
output.append(options)
output.append('</select>')
return mark_safe(u'\n'.join(output))
如果你按照以下方式对render方法进行了子类化和覆盖:
class CustomSelectSingleAsList(SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<select %s>' % flatatt(final_attrs)] # NOTE removed the multiple attribute
options = self.render_options(choices, value)
if options:
output.append(options)
output.append('</select>')
return mark_safe(u'\n'.join(output))
这将呈现一个选择单一,但检索项目列表。
然后在表单元中,只需使用新的自定义类:
小部件= { 'groups':myforms.CustomSelectSingleAsList, 'password':forms.PasswordInput, #'text':Textarea(attrs = {'rows':3,'class':'span10','placeholder':'Post Content'}), }
或者,您可以覆盖“选择”小部件以返回列表:
class SelectSingleAsList(Select):
def value_from_datadict(self, data, files, name):
if isinstance(data, (MultiValueDict, MergeDict)):
return data.getlist(name) # NOTE this returns a list rather than a single value.
return data.get(name, None)
如果其中任何一个解决了您的问题,请告诉我。