I have just started working with Django, building an example application to find my way through the framework. Django seems to be more dynamic than I expected and the web is full of answers to questions I almost asked but help in no way at all.
I got totally stuck on this problem:
using a CBV and form_class, how can I limit a select in the form to a subset determined by a value known in the CBV
Essentially I wanted to pass a value from the View to the Form which could then limit a selector. For example, suppose we have Clients who have Contracts which have Tasks; when adding a Task for a Client we only want to see Contracts for that specific Client.
Here’s the basic structure:
#models.py
class Client(models.Model):
= models.CharField()
name
class Contract(models.Model):
= models.ForeignKey(Client)
client = models.Charfield)
name
class Task(models.Model):
= models.ForeignKey(Contract)
contract = models.TextField() notes
From a detail page for a Client, we want to be able to add a task to a Contract belong to the Client. We provide an URL to provide a form to add the task
#urls.py
r'^clients/(?P<cid>\d+)/addtask$', view=TaskCreateView.as_view()), url(
This will pass a value cid
to the class based view (CBV) representing the id
of the Client. We want the CBV to pass this value on to the form. Here’s how we can do this:
#views.py
class TaskCreateView(CreateView):
= Task
model = 'task_form.html'
template_name = TaskForm
form_class
def get_form_kwargs(self, **kwargs):
= self.kwargs.get('cid',0)
cid = super(TaskCreateView, self).get_form_kwargs(**kwargs)
kwargs 'initial']['cid'] = cid
kwargs[return kwargs
def get_context_data(self, **kwargs):
= super(TaskCreateView, self).get_context_data(**kwargs)
context if 'cid' in self.kwargs:
'client'] = get_object_or_404(Client, id=context['cid'])
context[return context
Here the get_form_kwargs
picks out cid
and passes it to the form as a keyword argument. In get_context_data
we also pick out cid
this time fetching the Client record and returning it in the context so the template can render appropriate information about the Client in the Task form.
The Contract choices can now be specified in the form:
#forms.py
class TaskForm(forms.ModelForm):
class Meta:
= Task
model
def __init__(self, *args, **kwargs):
super(TaskForm, self).__init__(*args, **kwargs)
= kwargs['initial'].get('cid',0)
cid if cid:
self.fields['contract'].choices = ((c.id, c.name) for c in Contract.objects.filter
Here, as the form is being initialised, if a cid
value has been passed from the View, we can filter the list of Contracts in the select widget