【问题标题】:How to create a filtered dropdown using Django forms?如何使用 Django 表单创建过滤下拉列表?
【发布时间】:2013-11-04 08:47:19
【问题描述】:

我有这个模型:

class QuestionInstance(models.Model):
    questionsSet = models.ForeignKey(QuestionsSet)
    question     = models.ForeignKey(Question)
    parent       =  models.ForeignKey('self',null=True,blank=True)
    optional     = models.BooleanField(default=False)

我想创建一个下拉列表,用户可以选择一个 QuestionInstance。 它必须用 questionsSet 过滤。

我已经使用这样的模型进行了测试,但它不起作用:

(基于此How do I filter values in a Django form using ModelForm?

class FormQuestionSelect(ModelForm):
    instanceList = forms.ChoiceField(choices=[(questionInstance.id, questionInstance.question) for questionInstance in QuestionInstance.objects.all()])

    class Meta:
        model = QuestionInstance
        fields = ('instanceList', )
        widgets = {
            'instanceList': Select(attrs={'class': 'select'}),
        }

    def __init__(self, questionsSet=None, **kwargs):
        super(FormQuestionSelect, self).__init__(**kwargs)
        if questionsSet:
            #Tested many code here to filter, None of them worked :(
            #Is that possible to create instanceList there ?                        

我不确定使用模型表单是否适合这种目的。

模型表单在创建或更新模型实例时非常有用。 在使用特定表单时,例如在本例中,我在模板中使用自定义表单:

查看

questionInstanceList = QuestionInstance.objects.filter(questionsSet=questionsSet)

模板

<select name="questionInstanceSelect">
    {% for instance in questionInstanceList %}
        <option value="{{ instance.id }}">{{ instance.question.text }}</option>
    {% endfor %}
</select>

并以这种方式处理它们:

instanceList = request.POST.get('questionInstanceSelect')

我很确定有合适的方法。

【问题讨论】:

  • 也许这个问题可以帮到你:how-to-get-interdependent-dropdowns
  • 在更改用户对QuestionSet 的选择而不提交表单后,您的表单应该如何表现?
  • 您希望根据选定的 QuestionSet 过滤 Question 外键。我说的对吗?
  • @oleg 我只想显示 QuestionInstance 列表,由当前 QuestionSet 过滤(从视图中设置)
  • @arulmr 没错

标签: python django


【解决方案1】:

您可以在表单实例化后更改 ModelChoiceField 的查询集,无论是在表单 __init__ 中还是在视图中。但这不会解决客户端的问题。当有人更改 QuestionSet Question 时,选择框将保持不变

要更新查询集,只需更新表单字段的一个

form.fields['parent'].queryset = (QuestionInstance.objects
                                           .filter(questionsSet=questionsSet))

或者如果您更改表单__init__

self.fields['parent'].queryset = (QuestionInstance.objects
                                           .filter(questionsSet=questionsSet))

但是应该记住,如果 questionsSet 在客户端父列表上发生更改将保持不变。

你会考虑添加客户端代码更新父母的选择列表

让我解释一下。

你有模特

class QuestionInstance(models.Model):
    questionsSet = models.ForeignKey(QuestionsSet)
    question     = models.ForeignKey(Question)
    parent       =  models.ForeignKey('self',null=True,blank=True)
    optional     = models.BooleanField(default=False)

这里父字段链接到self(同型号)。

让我们为这个模型使用`模型形式

class FormQuestionSelect(ModelForm):
    class Meta:
        model = QuestionInstance

ModelForm 将为每个模型字段创建具有相同名称的字段 然后在它创建之后我们更新ModelChoiceField(为ForeignKey创建)queryset

【讨论】:

  • 我不确定是否理解正确。但是实例化后我不需要刷新列表。
  • 不起作用。你为什么使用“父”字段?表单创建的字段名为 questionInstance
  • 请看我的解释
【解决方案2】:

如果您希望您的字段是动态的,您需要使用 jQuery 和 ajax 来实现此功能。我已经给出了在 django admin 中使用的代码。如果您想在自定义页面中使用它,您可以稍微调整一下。但两者的概念仍然相同。

question_set_change.js

(function($){   
    $(function(){
        $(document).ready(function() {
            $('#id_questionsSet').bind('change', question_set_change);            
            $('#id_question > option').show();
            if ($('#id_questionsSet').val() != '') {
                var question_set_id = $('#id_questionsSet').val();
                $.ajax({
                "type"      : "GET",
              "url"         : "/product_change/?question_set_id="+question_set_id,
                "dataType"  : "json",
              "cache"       : false,
                "success"   : function(json) {
                    $('#id_question >option').remove();
                    for(var j = 0; j < json.length; j++){
                        $('#id_question').append($('<option></option>').val(json[j][0]).html(json[j][1]));
                    }
                }           
            });
            }
        });
    });  
})(django.jQuery);

// based on the questionsSet, questions will be loaded

var $ = django.jQuery.noConflict();

function question_set_change()
{
    var question_set_id = $('#id_questionsSet').val();
    $.ajax({
    "type"      : "GET",
  "url"         : "/product_change/?question_set_id="+question_set_id,
    "dataType"  : "json",
  "cache"       : false,
    "success"   : function(json) {
        $('#id_question > option').remove();
        for(var j = 0; j < json.length; j++){
            $('#id_question').append($('<option></option>').val(json[j][0]).html(json[j][1]));
        }
    }           
})(jQuery);
}

在views.py中包含以下内容:

import simplejson
from django.shortcuts import HttpResponse

from app.models import Question

def question_choices(request): 
    question_list = []
    question_set_id = request.GET.get('question_set_id')
    questions       = Question.objects.filter(question_set = question_set_id)    
    [question_list.append((each_question.pk,each_question.name)) for each_question in questions]
    json = simplejson.dumps(question_list)
    return HttpResponse(json, mimetype='application/javascript')

在 urls.py 中:

from app.views import question_choices

urlpatterns = patterns(
    (r'^question_set_change/', question_choices),
)

在 admin.py 中,您要根据 question_set 加载问题:

class Media:
    js = ['/path/to/question_set_change.js',]

【讨论】:

  • 我的下拉菜单是由视图生成的。我现在不需要 Ajax。
猜你喜欢
  • 2020-10-01
  • 1970-01-01
  • 2018-11-17
  • 2018-06-17
  • 1970-01-01
  • 2019-05-20
  • 1970-01-01
  • 2015-11-02
相关资源
最近更新 更多