我的表格:
class PlanForm(forms.ModelForm):
owner = forms.ModelChoiceField(label="",
queryset=Profile.objects.all(),
widget=forms.HiddenInput())
etc...
class Meta:
model = Plan
模型中的所有者是个人资料的ForeignKey。
当我设置此表单时,我将“owner”的值设置为Profile对象。
但是当它出现在表单上时,它似乎包含了配置文件的名称,如下所示:
<input type="hidden" name="owner" value="phil" id="id_owner" />
当提交表单并返回我的views.py时,我尝试像这样处理它:
form = PlanForm(request.POST)
...
if form.is_valid():
plan = form.save()
return HttpResponseRedirect('/plans/%s'%plan.id) # Redirect after POST
但是,我得到的是类型转换错误,因为它无法将字符串“phil”(保存到“owner”字段中的用户名称)转换为Int以将其转换为ForeignKey。
那么这里发生了什么。 ModelForm是否应将外键表示为数字并透明地处理它?或者我是否需要将自己的ID提取到表单的所有者字段中?如果是这样,在我尝试验证表单之前,如何以及何时将其映射回去?
答案 0 :(得分:18)
我怀疑Profile模型实例的__unicode__
方法或其repr
设置为返回self.id
以外的值。例如,我只是设置了它:
# models.py
class Profile(models.Model):
name = models.CharField('profile name', max_length=10)
def __unicode__(self):
return u'%d' % self.id
class Plan(models.Model):
name = models.CharField('plan name', max_length=10)
profile = models.ForeignKey(Profile, related_name='profiles')
def __unicode__(self):
return self.name
# forms.py
class PlanForm(forms.ModelForm):
profile = forms.ModelChoiceField(queryset=Profile.objects.all(),
widget=forms.HiddenInput())
class Meta:
model = Plan
# views.py
def add_plan(request):
if request.method == 'POST':
return HttpResponse(request.POST['profile'])
profile = Profile.objects.all()[0]
form = PlanForm(initial={'profile':profile})
return render_to_response('add_plan.html',
{
'form':form,
},
context_instance=RequestContext(request))
有了这个,我看到在模板中呈现了PlanForm.profile:
<input type="hidden" name="profile" value="1" id="id_profile" />
答案 1 :(得分:11)
嗯...
这实际上可能是一个安全漏洞。
假设恶意攻击者制作了一个POST(例如,通过使用FireBug中的XmlHttpRequest)并将配置文件术语设置为某个古怪的值,例如您的个人资料ID。可能不是你想要的?
如果可能,您可能希望从请求对象本身获取配置文件,而不是从POST值提交的配置文件。
form = PlanForm(request.POST)
if form.is_valid():
plan = form.save(commit=False)
plan.owner = request.user.get_profile()
plan.save()
form.save_m2m() # if neccesary
答案 2 :(得分:8)
当您将Profile对象分配给表单时,Django会对其进行字符串化并将输出用作表单中的值。你会期望的是Django使用对象的ID代替。
幸运的是,解决方法很简单:只需给出Profile对象的表单主键值:
form = PlanForm(initial={'profile': profile.pk})
另一方面,当你使用绑定表格时,他们的工作更明智:
form = PlanForm(request.POST)
if form.is_valid():
print form.cleaned_data['profile'] # the appropriate Profile object
答案 3 :(得分:2)
由于ModelChoiceField继承自ChoiceFIeld,因此您应该使用MultipleHiddenInput小部件:
class PlanForm(forms.ModelForm):
owner = forms.ModelChoiceField(
queryset=Profile.objects.all(),
widget=forms.MultipleHiddenInput())
class Meta:
model = Plan
答案 4 :(得分:1)
通常不需要将相关对象放入表单字段中。有一种更好的方法,这是在表单URL中指定父ID。
假设您需要为新的Plan对象呈现一个表单,然后在表单被填充时创建一个表单。这是你的urlconf的样子:
(r"/profile/(?P<profile_id>\d+)/plan/new", view.new_plan), # uses profile_id to define proper form action
(r"/profile/(?P<profile_id>\d+)/plan/create", view.create_plan) # uses profile_id as a Plan field
如果您要更改现有对象,您只需要plan_id,就可以从中推断出任何相关记录。