Django: "limit_choices_to" doesn't work on ManyToManyField

Kyle Duncan picture Kyle Duncan · Aug 20, 2011 · Viewed 7.4k times · Source

I am running Django 1.1 and cannot get the "limit_choices_to" option for my ManytoManyField to work.

I have two models:

class MemberPhoto(ImageModel):
    title       = models.CharField(_('title'), max_length=255, blank=True, null=True)
    caption     = models.CharField(_('caption'), max_length=255, blank=True, null=True)
    date_added  = models.DateTimeField(_('date added'), default=datetime.now, editable=False)
    member      = models.ForeignKey(User)

    def __unicode__(self):
        return u'%s (%s)' % (self.member.username, self.id)

and

class lock(models.Model):
    user = models.ForeignKey(User, related_name="owner")
    to_user = models.ForeignKey(User, related_name="to_user")
    unlocked_photos = models.ManyToManyField(MemberPhoto, blank=True, null=True, limit_choices_to = {'member':'user'})
    objects = locking_manager()

in the second model, i want to make sure in Django's admin that the only "unlocked_photos" ("MemberPhoto" objects) presented in the multiple select field are those who have a "member" value (a User object) the same as the "lock" object's "user" (also a User object).

I thought I had followed the Django docs on this, but it doesn't work. I get the following error:

TemplateSyntaxError

Caught an exception while rendering: invalid input syntax for integer: "user"

I've tried changing the "limit_choices_to" to things like:

limit_choices_to = {'member': user} --- Doesnt work

limit_choices_to = {'member__username':'kyle'} --- this DOES work but it's useless, i'm just manually specifying a username

How can I instead get the user from the current "lock" object and filter the MemberPhoto "member" property by that?

Thanks to anybody who can help.

Kyle

Answer

Kyle Duncan picture Kyle Duncan · Aug 21, 2011

I found an answer that achieves exactly what I wanted at this link: Django MTMField: limit_choices_to = other_ForeignKeyField_on_same_model?, and I'm posting my working code here for anybody having the same problem. It seems from looking around that "limit_choices_to" may simply not be able to achieve what I wanted, and that customizing the form used by the admin is the way to go:

from django.contrib import admin
from django import forms
from gayhop.apps.locking.models import lock
from gayhop.apps.photos.models import MemberPhoto

class LockAdminForm(forms.ModelForm):
  class Meta:
    model = lock

  def __init__(self, *args, **kwargs):
    super(LockAdminForm, self).__init__(*args, **kwargs)
    self.fields['unlocked_photos'].queryset = MemberPhoto.objects.filter(member=self.instance.user)


class LockAdmin(admin.ModelAdmin):
  form = LockAdminForm
  filter_horizontal = ('unlocked_photos',)

django.contrib.admin.site.register(lock, LockAdmin)

All you have to change is:

  1. the name of your model (in the above example it's "lock")
  2. the name of the ManyToManyField field in your model (in the above example it's "unlocked_photos")
  3. the name of the related model (in the above example it's "MemberPhoto")
  4. the name of the field you want to filter related objects by (in the above example it's "member")
  5. the value for the field you want to use to filter related objects by (it will start with "self.instance." and then be the name of the field, in the above example it's "user")
  6. And finally make sure your class names for the custom admin form and admin model all match up.

Hope this helps somebody!