-
-
Save dokterbob/828117 to your computer and use it in GitHub Desktop.
| class LimitedAdminInlineMixin(object): | |
| """ | |
| InlineAdmin mixin limiting the selection of related items according to | |
| criteria which can depend on the current parent object being edited. | |
| A typical use case would be selecting a subset of related items from | |
| other inlines, ie. images, to have some relation to other inlines. | |
| Use as follows:: | |
| class MyInline(LimitedAdminInlineMixin, admin.TabularInline): | |
| def get_filters(self, obj): | |
| return (('<field_name>', dict(<filters>)),) | |
| """ | |
| @staticmethod | |
| def limit_inline_choices(formset, field, empty=False, **filters): | |
| """ | |
| This function fetches the queryset with available choices for a given | |
| `field` and filters it based on the criteria specified in filters, | |
| unless `empty=True`. In this case, no choices will be made available. | |
| """ | |
| assert formset.form.base_fields.has_key(field) | |
| qs = formset.form.base_fields[field].queryset | |
| if empty: | |
| logger.debug('Limiting the queryset to none') | |
| formset.form.base_fields[field].queryset = qs.none() | |
| else: | |
| qs = qs.filter(**filters) | |
| logger.debug('Limiting queryset for formset to: %s', qs) | |
| formset.form.base_fields[field].queryset = qs | |
| def get_formset(self, request, obj=None, **kwargs): | |
| """ | |
| Make sure we can only select variations that relate to the current | |
| item. | |
| """ | |
| formset = \ | |
| super(LimitedAdminInlineMixin, self).get_formset(request, | |
| obj, | |
| **kwargs) | |
| for (field, filters) in self.get_filters(obj): | |
| if obj: | |
| self.limit_inline_choices(formset, field, **filters) | |
| else: | |
| self.limit_inline_choices(formset, field, empty=True) | |
| return formset | |
| def get_filters(self, obj): | |
| """ | |
| Return filters for the specified fields. Filters should be in the | |
| following format:: | |
| (('field_name', {'categories': obj}), ...) | |
| For this to work, we should either override `get_filters` in a | |
| subclass or define a `filters` property with the same syntax as this | |
| one. | |
| """ | |
| return getattr(self, 'filters', ()) |
This code worked perfectly for existing entries. Unfortunately, I was getting a 'NoneType' object has no attribute 'vehicle' exception when adding a new item (Django 1.7, Python 3). I had to do this:
class QuoteAdminInline(LimitedAdminInlineMixin, admin.TabularInline):
def get_filters(self, obj):
return getattr(self, 'filters', ()) if obj is None else (('product', dict(vehicle=obj.vehicle)),)Not sure if that's the best solution. Maybe it will help others though.
@mhulse you likely got this error because you forgot to implement get_filters in your inline class.
My fork contains some minor changes and was tested on Django 2.0b1 with Python 3.6.
https://gist.github.com/flebel/418d4eac306084259b4a4f1d71e043f2/revisions#diff-debb229a55dd842402fd853a9baacdd1
Only the assert line needed to be updated to support Python 3.
@flebel I still see all entities in the popup

Is there a way to open popup with filter?
Thanks for the snippet. It helped me a lot.
I needed also to restrict the queryset according to exclude conditions so I've tweaked a bit get_formset as:
def get_formset(self, request, obj=None, **kwargs):
"""
Make sure we can only select variations that relate to the current
item.
"""
formset = super(LimitedAdminInlineMixin, self).get_formset(
request, obj, **kwargs)
qs_attributes = {
'filter': self.get_filters(obj),
'exclude': self.get_excludes(obj)
}
for attr, options in qs_attributes.items():
for (field, filters) in options:
if obj:
self.limit_inline_choices(formset, field, qs_attr=attr, **filters)
else:
self.limit_inline_choices(formset, field, empty=True)
return formsetWhere self.get_excludes has the same format as self.filters
Also limit_inline_choices accepts qs_attr='filter' and Line-31 became: qs = getattr(qs, qs_attr)(**filters)
Thanks a lot. BTW, has_key is deprecated (i'm using Django >= 2.2) and if someone is getting error like:
AttributeError: 'collections.OrderedDict' object has no attribute 'has_key'
Just change Line-24:
assert formset.form.base_fields.has_key(field)
For:
assert field in formset.form.base_fields
Great tip! If you are perhaps able to make this a pull request (cllick the edit button on the file in question), I promise that it’ll be immediately accepted! :)
I dont think that is possible, but i created a fork.
Thanks!