将信息从 GET 参数传递到 Django 中的 POST 表单的方法?
Way to pass information from a GET parameter to a POST form in django?
我有一个邮件列表系统,用户可以在其中单击 link 取消订阅。此 link 在 GET 参数中包含用户的电子邮件,它指向的页面包含一个简短的表单以征求反馈。此反馈需要指向提交它的用户的电子邮件。
我尝试实现的方法是:
- 从 GET 参数中获取电子邮件
- 将其作为
initial
值放在反馈表的隐藏字段中
- 发送表单时从表单数据中检索它
问题在于,如果不禁用此隐藏字段,用户可以随意更改其值并伪装自己的身份,甚至声称反馈来自其他用户。但是如果我将该字段设置为禁用,request.POST 字典根本不包含该字段。
我还尝试保持该字段处于启用状态并检查它在 form.changed_data
中的存在,但它似乎总是存在,即使它的值没有改变。
这是表格 class:
class UnsubscribeForm(forms.Form):
reason = forms.ChoiceField(choices=UnsubscribeFeedback.Reasons.choices)
comment = forms.CharField(widget=forms.Textarea, required=False)
user_email = forms.CharField(widget=forms.HiddenInput, required=False, disabled=False)
这是我在方法为 GET 时在视图中填充 user_email
的方式:
email = request.GET.get("email", "")
# ...
context["form"] = UnsubscribeForm(initial={"user_email": email})
请注意,我还尝试在此行之后以及表单的初始化方法中手动禁用该字段。结果是一样的:如果该字段被禁用,则该值不会被传递。
设置初始值后,我 print()ed 它以确保它设置正确,确实如此。我还检查了页面的源代码,它正确显示了值。
这就是我在接收到数据绑定表单时检查视图 POST 部分中的值的方式:
form = UnsubscribeForm(request.POST)
if form.is_valid(): # This passes whether I change the value or not.
if "user_email" in form.changed_data: # This too passes whether I change the value or not.
print("Changed!")
email = form.cleaned_data["user_email"] # This is "" if user_email is disabled, else the correct value.
我不知道为什么当该字段被禁用时我设置的初始值被忽略了。据我所知,无论任何更改如何,禁用字段都会传递初始值,但这里根本不会传递初始值。正如我在上面概述的那样,我不能让用户可以编辑此字段,即使它是隐藏的。
Django 是 3.0.3 版,如果重要的话。
有什么解决办法吗?这是一个错误吗?
我找到了我的问题的解决方案,虽然它没有完全回答为什么禁用字段忽略运行时初始值的问题,所以从某种意义上说,这个问题仍然有待回答。
在最初的问题中,我完全忽略了指定(为了使代码最小化和可重现),包含用户电子邮件地址的 GET 请求还包含我用不可预测的数据生成的令牌,以验证电子邮件是真实的并且对应于订阅用户。为了成功地干扰电子邮件,用户还必须伪造一个有效的令牌,这是不太可能的(也不值得付出努力),除非他们可以访问我的数据库和代码库(在这种情况下,我遇到的问题比反馈表)。
我只会保持隐藏字段不被禁用,同时传递令牌,以验证地址确实有效。
我有一个邮件列表系统,用户可以在其中单击 link 取消订阅。此 link 在 GET 参数中包含用户的电子邮件,它指向的页面包含一个简短的表单以征求反馈。此反馈需要指向提交它的用户的电子邮件。
我尝试实现的方法是:
- 从 GET 参数中获取电子邮件
- 将其作为
initial
值放在反馈表的隐藏字段中 - 发送表单时从表单数据中检索它
问题在于,如果不禁用此隐藏字段,用户可以随意更改其值并伪装自己的身份,甚至声称反馈来自其他用户。但是如果我将该字段设置为禁用,request.POST 字典根本不包含该字段。
我还尝试保持该字段处于启用状态并检查它在 form.changed_data
中的存在,但它似乎总是存在,即使它的值没有改变。
这是表格 class:
class UnsubscribeForm(forms.Form):
reason = forms.ChoiceField(choices=UnsubscribeFeedback.Reasons.choices)
comment = forms.CharField(widget=forms.Textarea, required=False)
user_email = forms.CharField(widget=forms.HiddenInput, required=False, disabled=False)
这是我在方法为 GET 时在视图中填充 user_email
的方式:
email = request.GET.get("email", "")
# ...
context["form"] = UnsubscribeForm(initial={"user_email": email})
请注意,我还尝试在此行之后以及表单的初始化方法中手动禁用该字段。结果是一样的:如果该字段被禁用,则该值不会被传递。
设置初始值后,我 print()ed 它以确保它设置正确,确实如此。我还检查了页面的源代码,它正确显示了值。
这就是我在接收到数据绑定表单时检查视图 POST 部分中的值的方式:
form = UnsubscribeForm(request.POST)
if form.is_valid(): # This passes whether I change the value or not.
if "user_email" in form.changed_data: # This too passes whether I change the value or not.
print("Changed!")
email = form.cleaned_data["user_email"] # This is "" if user_email is disabled, else the correct value.
我不知道为什么当该字段被禁用时我设置的初始值被忽略了。据我所知,无论任何更改如何,禁用字段都会传递初始值,但这里根本不会传递初始值。正如我在上面概述的那样,我不能让用户可以编辑此字段,即使它是隐藏的。
Django 是 3.0.3 版,如果重要的话。
有什么解决办法吗?这是一个错误吗?
我找到了我的问题的解决方案,虽然它没有完全回答为什么禁用字段忽略运行时初始值的问题,所以从某种意义上说,这个问题仍然有待回答。
在最初的问题中,我完全忽略了指定(为了使代码最小化和可重现),包含用户电子邮件地址的 GET 请求还包含我用不可预测的数据生成的令牌,以验证电子邮件是真实的并且对应于订阅用户。为了成功地干扰电子邮件,用户还必须伪造一个有效的令牌,这是不太可能的(也不值得付出努力),除非他们可以访问我的数据库和代码库(在这种情况下,我遇到的问题比反馈表)。
我只会保持隐藏字段不被禁用,同时传递令牌,以验证地址确实有效。