django: How to limit field choices in formset?

不问归期 提交于 2019-11-29 10:39:59
Alasdair

This Stack Overflow question is fairly similar. I like the approach of Matthew's answer, where you build the form dynamically in a function that has access to the employee via closure. In your case, you want something like:

from django.http import HttpResponseRedirect

def make_membership_form(employee):
    """
    Returns a Membership form for the given employee, 
    restricting the Project choices to those in the 
    employee's department. 
    """
    class MembershipForm(forms.ModelForm):
        project = forms.ModelChoiceField(queryset=Projects.objects.filter(department=employee.department))
        class Meta:
            model = Membership
            excludes = ('department', 'employee',)
    return MembershipForm

def employee_edit(request, employee_id):
    employee = get_object_or_404(Employee, pk=employee_id)
    # generate a membership form for the given employee
    MembershipForm = make_membership_form(employee)
    MembershipFormSet = modelformset_factory(Membership, form=MembershipForm)

    if request.method == "POST":
        formset = MembershipFormSet(request.POST, queryset=Membership.objects.filter(employee=employee))
        if formset.is_valid():
            instances = formset.save(commit=False)
                for member in instances:
                    member.employee = employee
                    member.department = employee.department
                    member.save()
            formset.save_m2m()
            # redirect after successful update
            return HttpResponseRedirect("")
    else:
        formset = MembershipFormSet(queryset=Membership.objects.filter(employee=employee),)
    return render_to_response('testdb/edit.html', {'item': employee, 'formset': formset, }, context_instance=RequestContext(request))

EDIT

Darn. All that typing because I missed one part of the code ;). As @Alasdair mentions in the comments, you've excluded department from the form, so you can limit this with Django. I'm going to leave my original answer, though, just in case it might help someone else.

For your circumstances, all you need is:

class MembershipForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(MembershipForm, self).__init__(*args, **kwargs)
        self.fields['project'].queryset = self.fields['project'].queryset.filter(department_id=self.instance.department_id)

And, then:

MembershipFormSet = modelformset_factory(Membership, form=MembershipForm, exclude=('department', 'employee'),)

Original Answer (for posterity)

You can't limit this in Django, because the value for department is changeable, and thus the list of projects can vary depending on which particular department is selected at the moment. In order to validate the form, you'll have to feed all possible projects that could be allowed to Django, so your only option is AJAX.

Create a view that will return a JSON response consisting of projects for a particular department fed into the view. Something along the lines of:

from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_list_or_404
from django.utils import simplejson

def ajax_department_projects(request):
    department_id = request.GET.get('department_id')
    if department_id is None:
        return HttpResponseBadRequest()

    project_qs = Project.objects.select_related('department', 'project_type')
    projects = get_list_or_404(project_qs, department__id=department_id)
    data = []
    for p in projects:
        data.append({
            'id': p.id,
            'name': unicode(p),
        })

    return HttpResponse(simplejson.dumps(data), mimetype='application/json')

Then, create a bit of JavaScript to fetch this view whenever the department select box is changed:

(function($){
    $(document).ready(function(){
        var $department = $('#id_department');
        var $project = $('#id_project');

        function updateProjectChoices(){
            var selected = $department.val();
            if (selected) {
                $.getJSON('/path/to/ajax/view/', {department_id: selected}, function(data, jqXHR){
                    var options = [];
                    for (var i=0; i<data.length; i++) {
                        output = '<option value="'+data[i].id+'"';
                        if ($project.val() == data[i].id) {
                            output += ' selected="selected"';
                        }
                        output += '>'+data[i].name+'</option>';
                        options.push(output);
                    }
                    $project.html(options.join(''));
                });
            }
        }

        updateProjectChoices();
        $project.change(updateProjectChoices);
    });
})(django.jQuery);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!