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:
models.py:

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' % (self.name)

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:

forms.py:

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:

views.py:

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 = teacher_form.save()
return redirect('myapps.views.add_teacher_view')
else:
teacher_form = TeacherForm(instance=teacher, student_set=student_set)

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



References:

[0] http://stackoverflow.com/questions/291945/how-do-i-filter-foreignkey-choices-in-a-django-modelform/291968#291968
[1] https://docs.djangoproject.com/en/1.7/topics/db/queries/