filtering_queryset.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from django import forms
  4. from django.core.exceptions import ObjectDoesNotExist
  5. import logging
  6. logger = logging.getLogger(__name__)
  7. class LimitedAdminInlineMixin(object):
  8. """
  9. InlineAdmin mixin limiting the selection of related items according to
  10. criteria which can depend on the current parent object being edited.
  11. A typical use case would be selecting a subset of related items from
  12. other inlines, ie. images, to have some relation to other inlines.
  13. Use as follows::
  14. class MyInline(LimitedAdminInlineMixin, admin.TabularInline):
  15. def get_filters(self, obj):
  16. return (('<field_name>', dict(<filters>)),)
  17. """
  18. @staticmethod
  19. def limit_inline_choices(formset, field, empty=False, **filters):
  20. """
  21. This function fetches the queryset with available choices for a given
  22. `field` and filters it based on the criteria specified in filters,
  23. unless `empty=True`. In this case, no choices will be made available.
  24. """
  25. try:
  26. assert formset.form.base_fields.has_key(field)
  27. qs = formset.form.base_fields[field].queryset
  28. if empty:
  29. logger.debug('Limiting the queryset to none')
  30. formset.form.base_fields[field].queryset = qs.none()
  31. else:
  32. qs = qs.filter(**filters)
  33. logger.debug('Limiting queryset for formset to: %s', qs)
  34. formset.form.base_fields[field].queryset = qs
  35. except:
  36. pass
  37. def get_formset(self, request, obj=None, **kwargs):
  38. """
  39. Make sure we can only select variations that relate to the current
  40. item.
  41. """
  42. formset = \
  43. super(LimitedAdminInlineMixin, self).get_formset(request,
  44. obj,
  45. **kwargs)
  46. for (field, filters) in self.get_filters(obj):
  47. if obj and filters:
  48. self.limit_inline_choices(formset, field, **filters)
  49. else:
  50. self.limit_inline_choices(formset, field, empty=True)
  51. return formset
  52. def get_filters(self, obj):
  53. """
  54. Return filters for the specified fields. Filters should be in the
  55. following format::
  56. (('field_name', {'categories': obj}), ...)
  57. For this to work, we should either override `get_filters` in a
  58. subclass or define a `filters` property with the same syntax as this
  59. one.
  60. """
  61. return getattr(self, 'filters', ())