Assuming you're using a form and do not want to do this in the admin, you can do the following. First, add a related_name=
argument to your bed
field on the Student
model, like so (could be any string) and make migrations and migrate:
bed = models.ForeignKey(Bed, on_delete=models.CASCADE, null=True, blank=True, related_name='all_beds')
Then, using a model form with a custom __init__
method, you can populate your dropdown with only those beds that are not assigned.
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ['name', 'cell_no', 'emergency_cell_no', 'bed', 'photo']
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
self.fields['bed'].queryset = Bed.objects.filter(all_beds__bed__isnull=True)
Here's what's happening here. The init method is populating the choices on the bed
field with whatever .queryset
you pass to it.
In this case, you're using the related_name
"all_beds" to refer back to the Student
model. The all_beds__bed
is looking at Student.bed
, and all_beds__bed__isnull=True
is asking the database to return all Bed
instances that do not have some existing relation to Student.bed
. In other words, if a Bed
instance has some populated bed
field on the Student
model, do not add it to the queryset.
When you call this model form in your view, for example:
form = StudentForm()
...it will populate accordingly. If you need that form to be dynamically created with data only available in your view, say an ID number or user data, you can instantiate the modelform class in your view. It doesn't HAVE to be in a seperate forms.py
file.
Using Dynamic Forms
Now let's suppose you want to have an update view that excludes all the assigned beds, BUT includes the bed that was assigned to the student being updated.
Here's an example of how that could work, using a Q Object query in a dynamically instantiated form class within the view. You can do this by writing and calling a function that creates the ModelForm
. The Q
Object query combines unassigned beds with the one bed that IS assigned to the student
instance.
Note that the create_form
function takes in the request
object. This isn't necessary as written, but I'm showing it because you may have a case where your logged in user wants to see the bed they have selected, in which case you could use Q(all_beds__user=request.user)
, assuming you have added a user
foreign key to Student
your model. Of course another way of doing this is to query the student
based on request.user.
from django.db.models import Q
def updatestudent(request,id):
student = Student.objects.get(pk=id)
form = StudentForm(instance=student)
def create_form(request, student):
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ['name', 'cell_no', 'emergency_cell_no', 'bed', 'photo']
def __init__(self, *args, **kwargs):
super (StudentForm, self).__init__(*args, **kwargs)
self.fields['bed'].queryset = Bed.objects.filter(Q(all_beds__bed__isnull=True)|Q(all_beds__id=student.id))
return StudentForm
StudentForm = create_form(request, student)
if request.method == 'POST':
form = StudentForm(request.POST, instance=student)
if form.is_valid():
form.save()
messages.success(request, "Student updated successfully.")
return redirect('/student/view/'+id)
else:
form = StudentForm(instance=student)
context= {'form':form}
return render(request, 'new-student.html', context)
For more information about complex queries using Q objects, check out the documentation here.