I want to limit a queryset for a form based on the user sending the request. I am having some trouble getting a ModelForm to properly limit the queryset of a field when the form is submitted but invalid. The form gets redisplayed with the error text, but no longer has the queryset limited. What could be the cause here?
models.py
from django.db import models
from django.contrib.auth.models import User
class Patient(models.Model):
name = models.CharField(max_length=100)
doctor = models.ForeignKey(User)
def __unicode__(self):
return self.name
class Prescription(models.Model):
name = models.CharField(max_length=100)
patient = models.ForeignKey(Patient)
views.py
import medical.models as models
import medical.forms as forms
from django.shortcuts import render
def add_form(request):
if request.method == 'POST':
form = forms.PrescriptionForm(request.POST)
if form.is_valid():
form.save()
else:
form = forms.make_prescription_form(request.user)
return render(request, 'add_form.html', {'form': form})
forms.py
import medical.models as models
from django.forms import ModelForm, ModelChoiceField
class PrescriptionForm(ModelForm):
class Meta:
model = models.Prescription
def make_prescription_form(dr):
class PrescriptionForm(ModelForm):
patient = ModelChoiceField(queryset=models.Patient.objects.filter(doctor=dr))
class Meta:
model = models.Prescription
return PrescriptionForm
add_form.html
{{ request.user.first_name }}
{% if form.errors %}
<p style="color: red;">Please correct the error{{ form.errors|pluralize }} below.</p>
{% endif %}
<form action="" method="post">{% csrf_token %}
{{ form }}
<br>
<input type="submit" value="Submit">
</form>
I would greatly appreciate any help with this, or suggestion on a better way to achieve the same thing! Let me know if any more files would be helpful. I'm using Django 1.3.
First off, it looks like you left off a bit - make_prescription_form
returns a class, not a form instance, and you're passing the class directly to the rendering in the GET path. I am assuming that's a typo.
You're not using your make_prescription_form
wrapper in the POST path. The smallest change from this implementation would be:
def add_form(request):
form_class = forms.make_prescription_form(request.user)
if request.method == 'POST':
form = form_class(request.POST)
if form.is_valid():
form.save()
else:
form = form_class()
return render(request, 'add_form.html', {'form': form})
As for other ways to do this - you can just set the form field's queryset directly in your view.
forms.py
class PrescriptionForm(ModelForm):
class Meta:
model = models.Prescription
views.py
def add_form(request):
if request.method == 'POST':
form = PrescriptionForm(request.POST)
form.fields['patient'].queryset = models.Patient.objects.filter(doctor=request.user)
if form.is_valid():
form.save()
else:
form = PrescriptionForm()
form.fields['patient'].queryset = models.Patient.objects.filter(doctor=request.user)
return render(request, 'add_form.html', {'form': form})
Or set doctor
as an argument to PrescriptionForm's __init__
and update the queryset there:
forms.py
class PrescriptionForm(ModelForm):
class Meta:
model = models.Prescription
def __init__(self, *args, doctor=None, **kwargs):
super(PrescriptionForm, self).__init__(*args, **kwargs)
if self.doctor is not None:
self.fields['patient'] = models.Patient.objects.filter(doctor=doctor)
views.py
def add_form(request):
if request.method == 'POST':
form = PrescriptionForm(request.POST, doctor=request.user)
if form.is_valid():
form.save()
else:
form = PrescriptionForm(doctor=request.user)
return render(request, 'add_form.html', {'form': form})