Login

Search child models in django admin changelist

Author:
navedr
Posted:
October 3, 2014
Language:
Python
Version:
Not specified
Score:
0 (after 0 ratings)

If you use django admin interface and have added an admin page for a model, django gives out-of-box search functionality in the model fields or foreignkey fields. One thing it doesn't support is searching in child models. For example you have created an admin page for Student model and there is model for courses which stores one or more courses taken by students and if you want to search by course name on the student page to see which students took a particular course. Django doesn't let you do that. I have written a small utility which will let you do that. Just copy the snippet in a file and then inherit from the ChildSearchAdmin instead of ModelAdmin and then you can specify which model/fields you want it to search on. The syntax is: **child_searches = [(ChildModel, 'field_to_search_on', 'foreign_key_field_in_child_model'),..] Example: class StudentAdmin(ChildSearchAdmin): child_searches = [(StudentCourse, 'course', 'student')]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class ChildSearchChangeList(ChangeList):
    # list of tuples (child_model, field_to_search, foreign_key_field)
    searches = []

    def get_query_set(self, request):
        qs = super(ChildSearchChangeList, self).get_query_set(request)
        if request.GET and request.GET.get('q') and len(self.searches) > 0:
            query = request.GET.get('q')
            # applying all child filters
            for (child_model, field_to_search, foreign_key_field) in self.searches:
                filters = {self.construct_search(field_to_search): query}
                qs = qs | qs.model.objects.filter(id__in=child_model.objects.filter(**filters).values_list(
                    self.get_db_name(child_model, foreign_key_field), flat=True))
            # collect all the declared list filters.
            (self.filter_specs, self.has_filters, remaining_lookup_params,
             use_distinct) = self.get_filters(request)

            # let every list filter modify the queryset to its liking.
            for filter_spec in self.filter_specs:
                new_qs = filter_spec.queryset(request, qs)
                if new_qs is not None:
                    qs = new_qs
        return qs

    @staticmethod
    def construct_search(field_name):
        if field_name.startswith('^'):
            return "%s__istartswith" % field_name[1:]
        elif field_name.startswith('='):
            return "%s__iexact" % field_name[1:]
        elif field_name.startswith('@'):
            return "%s__search" % field_name[1:]
        else:
            return "%s__icontains" % field_name

    @staticmethod
    def get_db_name(model, field_name):
        for field in model._meta.fields:
            if field.name == field_name:
                return field.get_attname()
        return None


class ChildSearchAdmin(admin.ModelAdmin):
    child_searches = []

    def get_changelist(self, request, **kwargs):
        ChildSearchChangeList.searches = self.child_searches
        return ChildSearchChangeList

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.