Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limit foreign key choices in select in an inline form in admin

The logic is of the model is:

  • A Building has many Rooms
  • A Room may be inside another Room (a closet, for instance--ForeignKey on 'self')
  • A Room can only be inside another Room in the same building (this is the tricky part)

Here's the code I have:

#spaces/models.py from django.db import models      class Building(models.Model):     name=models.CharField(max_length=32)     def __unicode__(self):         return self.name  class Room(models.Model):     number=models.CharField(max_length=8)     building=models.ForeignKey(Building)     inside_room=models.ForeignKey('self',blank=True,null=True)     def __unicode__(self):         return self.number 

and:

#spaces/admin.py from ex.spaces.models import Building, Room from django.contrib import admin  class RoomAdmin(admin.ModelAdmin):     pass  class RoomInline(admin.TabularInline):     model = Room     extra = 2  class BuildingAdmin(admin.ModelAdmin):     inlines=[RoomInline]  admin.site.register(Building, BuildingAdmin) admin.site.register(Room) 

The inline will display only rooms in the current building (which is what I want). The problem, though, is that for the inside_room drop down, it displays all of the rooms in the Rooms table (including those in other buildings).

In the inline of rooms, I need to limit the inside_room choices to only rooms which are in the current building (the building record currently being altered by the main BuildingAdmin form).

I can't figure out a way to do it with either a limit_choices_to in the model, nor can I figure out how exactly to override the admin's inline formset properly (I feel like I should be somehow create a custom inline form, pass the building_id of the main form to the custom inline, then limit the queryset for the field's choices based on that--but I just can't wrap my head around how to do it).

Maybe this is too complex for the admin site, but it seems like something that would be generally useful...

like image 770
mightyhal Avatar asked Dec 01 '09 06:12

mightyhal


2 Answers

Used request instance as temporary container for obj. Overrided Inline method formfield_for_foreignkey to modify queryset. This works at least on django 1.2.3.

class RoomInline(admin.TabularInline):      model = Room      def formfield_for_foreignkey(self, db_field, request=None, **kwargs):          field = super(RoomInline, self).formfield_for_foreignkey(db_field, request, **kwargs)          if db_field.name == 'inside_room':             if request._obj_ is not None:                 field.queryset = field.queryset.filter(building__exact = request._obj_)               else:                 field.queryset = field.queryset.none()          return field    class BuildingAdmin(admin.ModelAdmin):      inlines = (RoomInline,)      def get_form(self, request, obj=None, **kwargs):         # just save obj reference for future processing in Inline         request._obj_ = obj         return super(BuildingAdmin, self).get_form(request, obj, **kwargs) 
like image 154
nogus Avatar answered Oct 13 '22 01:10

nogus


There is limit_choices_to ForeignKey option that allows to limit the available admin choices for the object

like image 33
user1022684 Avatar answered Oct 13 '22 01:10

user1022684