Thursday, October 9, 2014

Filter ManyToMany field's choices in a Django ModelForm

In my current project, I'm required to build a form in which a certain user group can only create with a certain kind of data. For example:

  • User 1 of group A can only create Teacher objects with a set of students (student_numbers, let say ['123', '234'])
  • User 2 of group B can only  create Teacher objects with a set of students (student_numbers, ['345', '678'])

Here is what the model looks like:

from django.db import models

class Student(models.Model):
student_number = models.CharField(max_length = 6, primary_key = True)
student_name = models.CharField(max_length = 20)

def __unicode__(self):
return self.student_name

class Meta:
ordering = ['student_number']

class Teacher(models.Model):
name = models.CharField(max_length=50)
students = models.ManyToManyField(Student)

def __unicode__(self):
return u'%s' % (

class Meta:
ordering = ['name']
verbose_name = "Teachers"

To be able to generate a form and filter by student_number, I just need to define my __init__ function for the TeacherForm:

from django.forms import ModelForm
from myapps.models import Teacher, Student

class TeacherForm(ModelForm):

def __init__(self, student_set, *args,**kwargs):
super (TeacherForm,self ).__init__(*args,**kwargs)
self.fields['students'].queryset = Student.objects.filter(student_number__in=student_set)

class Meta:
model = Teacher
                fields = ['name', 'students']
exclude = []

So, in the view, I first check the user's group to decide which set of students that user can create from, then pass it to the form instance:

from django.contrib.auth.models import User, Group
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from myapps.models import Teacher, Student
from myapps.forms import TeacherForm

SETA = ['123', '234']
SETB = ['345', '678']

def get_student_set(user):
        student_set = []
        if Group.objects.get(name='A') in user.groups.all():
                 student_set += SETA
        elif Group.objects.get(name='B') in user.groups.all():
                 student_set += SETB

        return student_set

def add_teacher_view(request):
user = request.user
student_set = get_student_set(user)

teacher = Teacher()
if request.method == 'POST':
teacher_form = TeacherForm(request.POST, request.FILES)
if teacher_form.is_valid():
teacher =
return redirect('myapps.views.add_teacher_view')
teacher_form = TeacherForm(instance=teacher, student_set=student_set)

return render_to_response('teacher.html', locals(), context_instance=RequestContext(request))