116š
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)
20š
There is limit_choices_to ForeignKey option that allows to limit the available admin choices for the object
- [Django]-Django-social-auth django-registration and django-profiles ā together
- [Django]-Why is logged_out.html not overriding in django registration?
- [Django]-Django signals vs. overriding save method
18š
After reading through this post and experimenting a lot I think I have found a rather definitive answer to this question. As this is a design pattern that is ofter used I have written a Mixin for the Django admin to make use of it.
(Dynamically) limiting the queryset for ForeignKey fields is now as simple as subclassing LimitedAdminInlineMixin
and defining a get_filters(obj)
method to return the relevant filters. Alternateively, a filters
property can be set on the admin if dynamic filtering is not required.
Example usage:
class MyInline(LimitedAdminInlineMixin, admin.TabularInline):
def get_filters(self, obj):
return (('<field_name>', dict(<filters>)),)
Here, <field_name>
is the name of the FK field to be filtered and <filters>
is a list of parameters as you would normally specify them in the filter()
method of querysets.
- [Django]-How to update() a single model instance retrieved by get() on Django ORM?
- [Django]-Remove leading and trailing slash / in python
- [Django]-How to check if a user is logged in (how to properly use user.is_authenticated)?
8š
You can create a couple of custom classes that will then pass along a reference to the parent instance to the form.
from django.forms.models import BaseInlineFormSet
from django.forms import ModelForm
class ParentInstInlineFormSet(BaseInlineFormSet):
def _construct_forms(self):
# instantiate all the forms and put them in self.forms
self.forms = []
for i in xrange(self.total_form_count()):
self.forms.append(self._construct_form(i, parent_instance=self.instance))
def _get_empty_form(self, **kwargs):
return super(ParentInstInlineFormSet, self)._get_empty_form(parent_instance=self.instance)
empty_form = property(_get_empty_form)
class ParentInlineModelForm(ModelForm):
def __init__(self, *args, **kwargs):
self.parent_instance = kwargs.pop('parent_instance', None)
super(ParentInlineModelForm, self).__init__(*args, **kwargs)
in class RoomInline just add:
class RoomInline(admin.TabularInline):
formset = ParentInstInlineFormset
form = RoomInlineForm #(or something)
In your form you now have access in the init method to self.parent_instance!
parent_instance can now be used to filter choices and whatnot
something like:
class RoomInlineForm(ParentInlineModelForm):
def __init__(self, *args, **kwargs):
super(RoomInlineForm, self).__init__(*args, **kwargs)
building = self.parent_instance
#Filtering and stuff
- [Django]-Model not showing up in django admin
- [Django]-Can't connect to local MySQL server through socket '/tmp/mysql.sock
- [Django]-Convert seconds to hh:mm:ss in Python
8š
The problem in @nogus answer thereās still wrong url in popup /?_to_field=id&_popup=1
which allow user to select wrong item in popup
To finally make it work I had to change field.widget.rel.limit_choices_to
dict
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':
building = request._obj_
if building is not None:
field.queryset = field.queryset.filter(
building__exact=building)
# widget changed to filter by building
field.widget.rel.limit_choices_to = {'building_id': building.id}
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)
- [Django]-South migration: "database backend does not accept 0 as a value for AutoField" (mysql)
- [Django]-Foreign key from one app into another in Django
- [Django]-Django REST framework: non-model serializer
5š
This question and answer is very similar, and works for a regular admin form
Inside of an inlineāand thatās where it falls apartā¦ I just canāt get at the main formās data to get the foreign key value I need in my limit (or to one of the inlineās records to grab the value).
Hereās my admin.py. I guess Iām looking for the magic to replace the ???? withāif I plug in a hardcoded value (say, 1), it works fine and properly limits the the available choices in the inlineā¦
#spaces/admin.py
from demo.spaces.models import Building, Room
from django.contrib import admin
from django.forms import ModelForm
class RoomInlineForm(ModelForm):
def __init__(self, *args, **kwargs):
super(RoomInlineForm, self).__init__(*args, **kwargs)
self.fields['inside_room'].queryset = Room.objects.filter(
building__exact=????) # <------
class RoomInline(admin.TabularInline):
form = RoomInlineForm
model=Room
class BuildingAdmin(admin.ModelAdmin):
inlines=[RoomInline]
admin.site.register(Building, BuildingAdmin)
admin.site.register(Room)
- [Django]-Django filter queryset __in for *every* item in list
- [Django]-Django error message "Add a related_name argument to the definition"
- [Django]-Django-allauth social account connect to existing account on login
4š
I found a fairly elegant solution that works well for inline forms.
Applied to my model, where Iām filtering the inside_room field to only return rooms that are in the same building:
#spaces/admin.py
class RoomInlineForm(ModelForm):
def __init__(self, *args, **kwargs):
super(RoomInlineForm, self).__init__(*args, **kwargs) #On init...
if 'instance' in kwargs:
building = kwargs['instance'].building
else:
building_id = tuple(i[0] for i in self.fields['building'].widget.choices)[1]
building = Building.objects.get(id=building_id)
self.fields['inside_room'].queryset = Room.objects.filter(building__exact=building)
Basically, if an āinstanceā keyword is passed to the form, itās an existing record showing in the inline, and so I can just grab the building from the instance. If not an instance, itās one of the blank āextraā rows in the inline, and so it goes through the hidden form fields of the inline that store the implicit relation back to the main page, and grabs the id value from that. Then, it grabs the building object based on that building_id. Finally, now having the building, we can set the queryset of the drop downs to only display the relevant items.
More elegant than my original solution, which crashed and burned as inline (but workedāwell, if you donāt mind saving the form partway to make the drop downs fill inā for the individual forms):
class RoomForm(forms.ModelForm): # For the individual rooms
class Meta:
mode = Room
def __init__(self, *args, **kwargs): # Limits inside_room choices to same building only
super(RoomForm, self).__init__(*args, **kwargs) #On init...
try:
self.fields['inside_room'].queryset = Room.objects.filter(
building__exact=self.instance.building) # rooms with the same building as this room
except: #and hide this field (why can't I exclude?)
self.fields['inside_room']=forms.CharField( #Add room throws DoesNotExist error
widget=forms.HiddenInput,
required=False,
label='Inside Room (save room first)')
For non-inlines, it worked if the room already existed. If not, it would throw an error (DoesNotExist), so Iād catch it and then hide the field (since there was no way, from the Admin, to limit it to the right building, since the whole room record was new, and no building was yet set!)ā¦once you hit save, it saves the building and on reload it could limit the choicesā¦
I just need to find a way to cascade the foreign key filters from one field to another in a new recordāi.e., new record, select a building, and it automatically limits the choices in the inside_room select boxābefore the record gets saved. But thatās for another dayā¦
- [Django]-Django ā Overriding the Model.create() method?
- [Django]-Django's forms.Form vs forms.ModelForm
- [Django]-Strange PostgreSQL "value too long for type character varying(500)"
2š
I have to admit, I didnāt follow exactly what youāre trying to do, but I think itās complex enough that you might want to consider not basing your site off of the admin.
I built a site once that started out with the simple admin interface, but eventually became so customized that it became very difficult to work with within the constraints of the admin. I would have been better off if Iād just started from scratchāmore work at the beginning, but a lot more flexibility and less pain at the end. My rule-of-thumb would be if that what youāre trying to do is not documented (ie. involves overriding admin methods, peering into the admin source code etc.) then youāre probably better off not using the admin. Just me two cents. š
- [Django]-How do I reference a Django settings variable in my models.py?
- [Django]-How do I run tests for all my Django apps only?
- [Django]-How to add clickable links to a field in Django admin?
2š
In django 1.6:
form = SpettacoloForm( instance = spettacolo )
form.fields['teatro'].queryset = Teatro.objects.filter( utente = request.user ).order_by( "nome" ).all()
- [Django]-How to create user from django shell
- [Django]-Django models.py Circular Foreign Key
- [Django]-Sending an SMS to a Cellphone using Django
1š
If Daniel, after editing your question, hasnāt answered ā I donāt think I will be much helpā¦ š
Iām going to suggest that you are trying to force fit into the django admin some logic that would be better off implemented as your own group of views, forms and templates.
I donāt think it is possible to apply that sort of filtering to the InlineModelAdmin.
- [Django]-How can i test for an empty queryset in Django?
- [Django]-Django select_for_update cannot be used outside of a transaction
- [Django]-Unique fields that allow nulls in Django