form queryset: queries spanning multiple relationships
Date: April 10th 2016
Last updated: April 10th 2016
What if I want to limit the options in a dropdown (multi choice field) box thats based across multiple relationships?
In this example I have three models including Board, Surfer and SurfDiary. A surfer can add a board to the database which also saves their user id (the method to do this is not shown). But they can also select existing boards via the many to many relationship. When I a user adds an entry into their SurfDiary I want to provide a queryset that only contains boards they have either selected or added to the database.
EDIT: This only works if at least one entry has been created by the user. Not sure why this is the case?? I will return to this later... I was over thinking the filter step. Now updated. Stop the filtering at the Surfer model.
- https://docs.djangoproject.com/en/1.9/topics/db/queries/
- http://stackoverflow.com/questions/3010489/how-do-i-filter-values-in-a-django-form-using-modelform
- http://www.ilian.io/querying-manytomany-fields-in-django/
models.py
class Board(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
boardname = models.CharField('Board name', max_length=40, null=True, blank=True)
class Surfer(models.Model):
user = models.OneToOneField(User)
boards = models.ManyToManyField(Board)
class SurfDiary(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
board = models.ForeignKey(Board)
views.py
@login_required
def diaryentry(request, surfer_id):
surfer = get_object_or_404(Surfer, pk=surfer_id)
if request.method == "POST":
# Solution ================
# Add user argument to form
form = SurfDiaryForm(user=request.user)
#==========================
if form.is_valid():
diary = form.save(commit=False)
diary.user = get_object_or_404(User, pk=request.user.id)
diary.save()
return redirect('/{}/diary'.format(surfer_id))
else:
# Solution ================
# Add user argument to form
form = SurfDiaryForm(user=request.user)
#==========================
return render(request, 'surferprofile/adddiaryentry.html',
{'surfer': surfer, 'form': form})
forms.py
class SurfDiaryForm(forms.ModelForm):
class Meta:
model = SurfDiary
fields = [
#<- snipped ->,
'board',
#<- snipped ->]
exclude = ('user', )
def __init__(self, user=None, **kwargs):
super(SurfDiaryForm, self).__init__(**kwargs)
if user:
#================ Solution ======================
# THIS FAILED
#self.fields['board'].queryset =
# Board.objects.all().filter(
# surfer__boards__user=user.id)
# This filter looks at the 'boards' field in the
# Surfer model (which is a many-to-many relationship
# with the Board model) and associates the user field
# with the user.id that I added to 'form' in
# views.py
# NEW SOLUTION
self.fields['board'].queryset =
Board.objects.all().filter(
surfer__user=user.id)
#================================================