Django ManyToManyField
在HTML中呈现这样的内容:
<form action="" method="post">
<select name="answers" multiple="multiple">
<option value="1" >Question 1, Answer 1</option>
<option value="2">Question 1, Answer 2</option>
<option value="3">Question 1, Answer 3</option>
<option value="4">Question 2, Answer 1</option>
<option value="5">Question 2, Answer 2</option>
</select>
<input type="submit">
</form>
我可以从问题1组中手动选择一个项目,从问题2组中选择一个项目。当这是在POST请求中发送时,我得到一个包含answers = [1, 3]
或类似的POST数组。
我希望从单选按钮组中获得相同的行为,因为这是一个更适合单选按钮的问题。例如,如果我执行以下操作:
<form action="" method="post">
<fieldset>
<legend>Question 1</legend>
<input name="answers" id="id_1" value="1" type="radio">
<label for="id_1">Answer 1</label>
<br>
<input name="answers" id="id_2" value="2" type="radio">
<label for="id_2">Answer 2</label>
<br>
<input name="answers" id="id_3" value="3" type="radio">
<label for="id_3">Answer 3</label>
<br>
</fieldset>
<fieldset>
<legend>Question 2</legend>
<input name="answers" id="id_4" value="4" type="radio">
<label for="id_4">Answer 1</label>
<br>
<input name="answers" id="id_5" value="5" type="radio">
<label for="id_5">Answer 2</label>
<br>
</fieldset>
</form>
实际上并没有让用户一次选择多个单选按钮。另一方面,如果我将单选按钮命名为answers[0]
和answers[1]
,则在POST中,它会发送两个具有这些名称的独立实体,而不是发送合并的answers
。
我问,因为在后端,我有一个带有自定义小部件的Django ManyToManyField
和ModelForm
,我正在尝试将数据保存到ManyToManyField
这个自定义小部件没有在后端使用过多的技巧,但我不断收到错误“输入值列表。”
编辑:JavaScript是可以接受的,只要它不发送原始数据,就像Django继承/自定义解析一样。
编辑2:这是我现在的ManyToManyField的小部件。
{% if questions %}
{% for question in questions %}
<fieldset>
<legend>{{ question.question }}</legend>
{% if question.options %}
{% for option in question.options %}
<input type="checkbox" class="form-check-input" name="answers" id="id_{{ option.id }}" value="{{ option.id }}">
<label for="id_{{ option.id }}">{{ option.text }}</label>
<br>
{% endfor %}
{% else %}
<p>No options for this question.</p>
{% endif %}
</fieldset>
{% endfor %}
{% else %}
<p>No questions in test.</p>
{% endif %}
答案 0 :(得分:2)
实际上并没有让用户选择多个单选按钮 一旦。另一方面,如果我将单选按钮命名为[0]和 答案[1],在POST中,它发送两个带有这些名称的独立实体 而不是发送综合答案。
这并不能满足你的要求,但至少解决了一个谜团。当您使用answers[]
作为名称时,后端会将其识别为数组并将所有值连接到数组中。除单一(无线电/选择)外,任何输入类型均可使用此功能。
这是你可以使用的js解决方案,但是你的情况表明你使用的模型对于这种情况并不是最佳的。当然,它可以像轰鸣声一样解决hacky方式,但是当你遇到这个代码时,你总是必须解决变通方法。
所以我的建议是:在后端改变方法
document.getElementById('answers').onsubmit = function(e) {
e.preventDefault(); //prevent submit;
let clone = this.cloneNode(true);
let inputs = clone.querySelectorAll('input[type=radio]');
let formData = new FormData(clone); // HTML5
// dumb check for validity
if (Array.from(formData.values()).length != clone.querySelectorAll('fieldset').length) {
alert('Invalid form!')
return false;
}
for (let i=0;i<inputs.length;i++) {
inputs[i].type = "checkbox";
inputs[i].name = "answers[]";
}
// only for snippet
formData = new FormData(clone);
console.log(Array.from(formData.entries()));
// commented in snippet
//clone.submit();
}
/* This is cleaner submit by JS without reload
document.getElementById('answers').onsubmit = function(e) {
let formData = new FormData(this); // HTML5
let answers = Array.from(formData.values());
// dumb check for validity
if (answers.length != this.querySelectorAll('fieldset').length) {
alert('Invalid form!')
return false;
}
let newFormData = new FormData();
newFormData.set('answers', answers);
let request = new XMLHttpRequest();
request.open(this.method || "POST", this.action || '/default/post/link');
request.send(newFormData);
return false;
}
*/
&#13;
<form action="" method="post" id="answers">
<fieldset>
<legend>Question 1</legend>
<input name="question_1" id="id_1" value="1" type="radio">
<label for="id_1">Answer 1</label>
<br>
<input name="question_1" id="id_2" value="2" type="radio">
<label for="id_2">Answer 2</label>
<br>
<input name="question_1" id="id_3" value="3" type="radio">
<label for="id_3">Answer 3</label>
<br>
</fieldset>
<fieldset>
<legend>Question 2</legend>
<input name="question_2" id="id_4" value="4" type="radio">
<label for="id_4">Answer 1</label>
<br>
<input name="question_2" id="id_5" value="5" type="radio">
<label for="id_5">Answer 2</label>
<br>
</fieldset>
<button type="submit">Submit</button>
</form>
&#13;
答案 1 :(得分:0)
将单选按钮更改为复选框应允许您选择多个值。
select
此表单生成的HTTP POST应与上面使用rb t@ip-XXX-XX-XX-XX-app:/var/www/discourse# vi
config/environments/development.r
root@ip-172-31-25-46-app:/var/www/discourse# rails s
=> Booting Puma
=> Rails 5.1.4 application starting in production
=> Run `rails server -h` for more startup options
Exiting
bundler: failed to load command: script/rails (script/rails)
Redis::CommandError: ERR Error running script (call to f_b06356ba4628144e123b652c99605b873107c9be): @user_script:14: @user_script: 14: -MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/redis-3.3.5/lib/redis/client.rb:121:in `call'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/redis-3.3.5/lib/redis.rb:2399:in `block in _eval'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/redis-3.3.5/lib/redis.rb:58:in `block in synchronize'
/usr/local/lib/ruby/2.4.0/monitor.rb:214:in `mon_synchronize'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/redis-3.3.5/lib/redis.rb:58:in `synchronize'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/redis-3.3.5/lib/redis.rb:2398:in `_eval'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/redis-3.3.5/lib/redis.rb:2450:in `evalsha'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/message_bus-2.1.1/lib/message_bus/backends/redis.rb:380:in `cached_eval'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/message_bus-2.1.1/lib/message_bus/backends/redis.rb:140:in `publish'
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/message_bus-2.1.1/lib/message_bus.rb:248:in `publish'
/var/www/discourse/lib/distributed_cache.rb:72:in `publish'
标记的示例完全相同。