# -*- coding: utf-8 -*- from __future__ import unicode_literals from django import forms from django.core.exceptions import ObjectDoesNotExist import logging logger = logging.getLogger(__name__) 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 (('', dict()),) """ @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. """ try: 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 except: pass 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 and filters: 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', ())