-
-
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 formset
Where 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!