Django: How to limit the selection of fields in a form set?

I'm having problems limiting my choices in a form set. I have the following models: employees, department, project, project type, membership and role. An employee can add / remove the roles that they play for a given project of departments in a set of forms, the form should limit selected projects only to those that belong to the department to which the employee belongs.

MODELS:

class Department(models.Model): name = models.CharField(max_length=20) def __unicode__(self): return self.name class Employee(models.Model): fname = models.CharField(max_length=15) department = models.ForeignKey(Department) def __unicode__(self): return self.fname class Projecttype(models.Model): name = models.CharField(max_length=20) def __unicode__(self): return self.name class Project(models.Model): projecttype = models.ForeignKey(Projecttype) department = models.ForeignKey(Department) members = models.ManyToManyField(Employee, through='Membership') def __unicode__(self): return "%s > %s" % (self.department, self.projecttype) class Role(models.Model): name = models.CharField(max_length=20) def __unicode__(self): return self.name class Membership(models.Model): project = models.ForeignKey(Project, null=True) department = models.ForeignKey(Department) employee = models.ForeignKey(Employee) role = models.ManyToManyField(Role, blank=True, null=True) class Meta: unique_together = (("project", "employee",),) 

VIEW:

 def employee_edit(request, employee_id): i = get_object_or_404(Employee, pk=employee_id) MembershipFormSet = modelformset_factory(Membership, exclude=('department', 'employee'),) f = MembershipFormSet(queryset=Membership.objects.filter(employee=i),) return render_to_response('gcs/edit.html', {'item': i, 'formset': f, }, context_instance=RequestContext(request)) 

Currently, the EU can choose the role to play for any project departments. It acts as follows:

Project Parameters:

 Projects.objects.all() 

I want to limit projects to something like this: LIMIT PROJECT CHOCIES TO:

 Projects.objects.filter(department=i.department) 
+6
source share
2 answers

This stack overflow question is pretty similar. I like Matthew's answer approach, where you dynamically create a form in a function that has access to an employee through 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 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)) 
+7
source

EDIT

darn. All this prints because I missed one piece of code;). As @Alasdair mentions in the comments, you excluded department from the form, so you can limit it with Django. I am going to leave my initial answer, although just in case this may help someone else.

In 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 cannot limit this in Django, because the value for the department is variable, and therefore, the list of projects may vary depending on which specific department is currently selected. To validate the form, you will need to submit all possible projects that Django may allow, so your only option is AJAX.

Create a view that returns a JSON response consisting of projects for a specific department filed in the view. Sort 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 some JavaScript to get this view whenever the department selection field changes:

 (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); 
+3
source

Source: https://habr.com/ru/post/910944/


All Articles