开发者

Validation on a dynamically set MultipleChoiceField, where the field is initially empty?

I've got a view with a few different forms in it. The first form has a search field which populates a multiple choice field, and when the user types in their query in the s开发者_StackOverflowearch field, an AJAX call is sent to bring all records which match the query. The user then selects the choices from the first multiple choice field and clicks "Add" to move them to a different Multi Select Box

This works fine, but when the form submits, I receive an error that reads "Select a valid choice.1 is not one of the available choices.". I have tried setting the choices after receiving arguments in the form init, but that doesn't seem to work.

My Form:

class SiteCoordinatorForm(forms.ModelForm):
    selected_studies = forms.MultipleChoiceField(required = False)
    site = forms.ChoiceField(required = False)    
    studies = forms.MultipleChoiceField(required = False)
    study_search = forms.CharField(max_length = 50, required = False)

    def __init__(self, *args, **kwargs):
        super(SiteCoordinatorForm, self).__init__(*args, **kwargs)        
        if args:
            study_list = []
            query_dict = args[0]
            self.fields['selected_studies'].choices = [(int(x), x) for x in   query_dict.getlist('selected_studies')]

        self.fields['site'].choices = [(x.pk, "%s (%s)" % (x.primary_name, x.primary_number)) for x in Site.objects.all().order_by('primary_name')]

    class Meta:
        model = SiteCoordinator
        exclude = ('studies', 'site', 'selected_studies')

My AJAX function which populates the box:

def search_studies(request):
    return_data = {}
    studies = []
    make_query = lambda terms, fieldname: reduce(lambda x, y: x & Q(**{fieldname + '__icontains': y}), terms, Q())
    if 'search_text' in request.POST:
        terms = request.POST['search_text'].split()
        for rec in Study.objects.filter(make_query(terms, 'name')):
            studies.append({
                'study': {'id': rec.id, 'name': rec.name, 'number': rec.id}
            })

    response = {'status': 'success', 'count': len(studies), 'studies': studies}
    return HttpResponse(simplejson.dumps(response), mimetype="application/json")

The function to populate the MultiSelectBox:

function show_results(data, status_code, request){
    recs = data.studies;
    var select_box = document.getElementById('id_studies');
    select_box.options.length = 0;

    for (var index = 0; index < recs.length; index ++){
        select_box.options[index] = new Option(recs[index].study.name,
                        recs[index].study.id,
                        false, false);
    }
}

Then the JQuery to move the choices:

$('#add').click(function() {
    $('#id_studies option:selected').remove().appendTo('#id_selected_studies');
    return false;

});

$('#remove').click(function() {  
    $('#id_selected_studies option:selected').remove().appendTo('#id_studies');
    return false;
});


It is clear that we don't want validations of MultipleChoiceField but do want MultipleSelectWidget so solution is simply to change field to

studies = forms.CharField(widget=forms.SelectMultiple)

and do your own validations in

def clean_studies(self):
    ...


Here is an example that uses the solution proposed by @AnuragUniyal and that also answers @JohnLehmann's comment:

class MyForm(forms.Form):
    new_tags = forms.CharField(required=False, widget=forms.SelectMultiple)

    def save(self, commit=True):
        instance = super(MyForm, self).save(commit=commit)
        for tag in eval(self.cleaned_data['new_tags']):
            models.Tag.objects.get_or_create(name=tag)
        return instance

Basically we transform the string of a list of unicode strings back into a Python list applying eval() on it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜