Ticket #2197: generic-search.diff

File generic-search.diff, 12.8 KB (added by Matias Hermanrud Fjeld <mhf@…>, 9 years ago)
  • django/db/models/manager.py

     
    9999    def values(self, *args, **kwargs):
    100100        return self.get_query_set().values(*args, **kwargs)
    101101
     102    def search(self, *args, **kwargs):
     103        return self.get_query_set().search(*args, **kwargs)
     104
    102105class ManagerDescriptor(object):
    103106    # This class ensures managers aren't accessible via model instances.
    104107    # For example, Poll.objects works, but poll_obj.objects raises AttributeError.
  • django/db/models/options.py

     
    1313
    1414DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
    1515                 'unique_together', 'permissions', 'get_latest_by',
    16                  'order_with_respect_to', 'app_label')
     16                 'order_with_respect_to', 'app_label', 'search_fields')
    1717
    1818class Options(object):
    1919    def __init__(self, meta):
     
    2222        self.verbose_name_plural = None
    2323        self.db_table = ''
    2424        self.ordering = []
     25        self.search_fields = []
    2526        self.unique_together =  []
    2627        self.permissions =  []
    2728        self.object_name, self.app_label = None, None
     
    197198
    198199class AdminOptions(object):
    199200    def __init__(self, fields=None, js=None, list_display=None, list_filter=None,
    200         date_hierarchy=None, save_as=False, ordering=None, search_fields=None,
     201        date_hierarchy=None, save_as=False, ordering=None,
    201202        save_on_top=False, list_select_related=False, manager=None, list_per_page=100):
    202203        self.fields = fields
    203204        self.js = js or []
     
    205206        self.list_filter = list_filter or []
    206207        self.date_hierarchy = date_hierarchy
    207208        self.save_as, self.ordering = save_as, ordering
    208         self.search_fields = search_fields or []
    209209        self.save_on_top = save_on_top
    210210        self.list_select_related = list_select_related
    211211        self.list_per_page = list_per_page
  • django/db/models/query.py

     
    233233                "Cannot change a query once a slice has been taken."
    234234        return self._clone(_limit=1, _order_by=('-'+latest_by,)).get()
    235235
     236    def search(self, query, field_names=None):
     237        """
     238        Return all objects who has any the words in the given query
     239        in any of it's fields given in the model's 'search_fields'
     240        option or in the optional argument field_names.
     241        """
     242        search_fields = field_names or self.model._meta.search_fields
     243        assert bool(search_fields), "search_fields() requires either a field_names parameter or 'search_fields' in the model"
     244        assert self._limit is None and self._offset is None, \
     245                "Cannot change a query once a slice has been taken."
     246
     247        or_queries = []
     248        for bit in query.split():
     249            for field_name in search_fields:
     250                or_queries.append(Q(**{'%s__icontains' % field_name: bit}))
     251
     252        if or_queries:
     253            return self._clone().filter(reduce(operator.or_, or_queries))
     254        else:
     255            return self
     256
    236257    def in_bulk(self, id_list):
    237258        """
    238259        Returns a dictionary mapping each of the given IDs to the object with
  • django/core/management.py

     
    930930                except models.FieldDoesNotExist:
    931931                    e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
    932932
     933        # Check search_fields attribute.
     934        if opts.search_fields:
     935            for field_name in opts.search_fields:
     936                try:
     937                    opts.get_field(field_name, many_to_many=False)
     938                except models.FieldDoesNotExist:
     939                    e.add(opts, '"search_fields" refers to "%s", a field that doesn\'t exist.' % field_name)
     940
    933941        # Check core=True, if needed.
    934942        for related in opts.get_followed_related_objects():
    935943            try:
  • django/views/generic/list_detail.py

     
    77def object_list(request, queryset, paginate_by=None, page=None,
    88        allow_empty=False, template_name=None, template_loader=loader,
    99        extra_context=None, context_processors=None, template_object_name='object',
    10         mimetype=None):
     10        mimetype=None, allow_search=True):
    1111    """
    1212    Generic list of objects.
    1313
     
    3333            number of pages, total
    3434        hits
    3535            number of objects, total
     36        query
     37            the search query string
    3638    """
    3739    if extra_context is None: extra_context = {}
    3840    queryset = queryset._clone()
     41
     42    query = request.GET.get('query', '')
     43    if searchable and queryset.model._meta.search_fields and query:
     44        queryset = queryset.search(query)
     45
    3946    if paginate_by:
    4047        paginator = ObjectPaginator(queryset, paginate_by)
    4148        if not page:
     
    5966            'previous': page - 1,
    6067            'pages': paginator.pages,
    6168            'hits' : paginator.hits,
     69            'query': query,
    6270        }, context_processors)
    6371    else:
    6472        c = RequestContext(request, {
    6573            '%s_list' % template_object_name: queryset,
    66             'is_paginated': False
     74            'is_paginated': False,
     75            'query': query,
    6776        }, context_processors)
    6877        if not allow_empty and len(queryset) == 0:
    6978            raise Http404
  • django/contrib/auth/models.py

     
    2929        verbose_name = _('group')
    3030        verbose_name_plural = _('groups')
    3131        ordering = ('name',)
    32     class Admin:
    3332        search_fields = ('name',)
    3433
     34    class Admin:
     35        pass
     36
    3537    def __str__(self):
    3638        return self.name
    3739
     
    7072        verbose_name = _('user')
    7173        verbose_name_plural = _('users')
    7274        ordering = ('username',)
     75        search_fields = ('username', 'first_name', 'last_name', 'email')
    7376    class Admin:
    7477        fields = (
    7578            (None, {'fields': ('username', 'password')}),
     
    8083        )
    8184        list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
    8285        list_filter = ('is_staff', 'is_superuser')
    83         search_fields = ('username', 'first_name', 'last_name', 'email')
    8486
    8587    def __str__(self):
    8688        return self.username
  • django/contrib/redirects/models.py

     
    1515        db_table = 'django_redirect'
    1616        unique_together=(('site', 'old_path'),)
    1717        ordering = ('old_path',)
     18        search_fields = ('old_path', 'new_path')
    1819
    1920    class Admin:
    2021        list_filter = ('site',)
    21         search_fields = ('old_path', 'new_path')
    2222
    2323    def __str__(self):
    2424        return "%s ---> %s" % (self.old_path, self.new_path)
  • django/contrib/comments/models.py

     
    9090        verbose_name = _('comment')
    9191        verbose_name_plural = _('comments')
    9292        ordering = ('-submit_date',)
     93        search_fields = ('comment', 'user__username')
    9394    class Admin:
    9495        fields = (
    9596            (None, {'fields': ('content_type', 'object_id', 'site')}),
     
    100101        list_display = ('user', 'submit_date', 'content_type', 'get_content_object')
    101102        list_filter = ('submit_date',)
    102103        date_hierarchy = 'submit_date'
    103         search_fields = ('comment', 'user__username')
    104104
    105105    def __repr__(self):
    106106        return "%s: %s..." % (self.user.username, self.comment[:100])
     
    176176        verbose_name = _('free comment')
    177177        verbose_name_plural = _('free comments')
    178178        ordering = ('-submit_date',)
     179        search_fields = ('comment', 'person_name')
    179180    class Admin:
    180181        fields = (
    181182            (None, {'fields': ('content_type', 'object_id', 'site')}),
     
    185186        list_display = ('person_name', 'submit_date', 'content_type', 'get_content_object')
    186187        list_filter = ('submit_date',)
    187188        date_hierarchy = 'submit_date'
    188         search_fields = ('comment', 'person_name')
    189189
    190190    def __repr__(self):
    191191        return "%s: %s..." % (self.person_name, self.comment[:100])
  • django/contrib/flatpages/models.py

     
    1818        verbose_name = _('flat page')
    1919        verbose_name_plural = _('flat pages')
    2020        ordering = ('url',)
     21        search_fields = ('url', 'title')
    2122    class Admin:
    2223        fields = (
    2324            (None, {'fields': ('url', 'title', 'content', 'sites')}),
    2425            ('Advanced options', {'classes': 'collapse', 'fields': ('enable_comments', 'registration_required', 'template_name')}),
    2526        )
    2627        list_filter = ('sites',)
    27         search_fields = ('url', 'title')
    2828
    2929    def __str__(self):
    3030        return "%s -- %s" % (self.url, self.title)
  • django/contrib/sites/models.py

     
    1515        verbose_name = _('site')
    1616        verbose_name_plural = _('sites')
    1717        ordering = ('domain',)
     18        search_fields = ('domain', 'name')
    1819    class Admin:
    1920        list_display = ('domain', 'name')
    20         search_fields = ('domain', 'name')
    2121
    2222    def __str__(self):
    2323        return self.domain
  • django/contrib/admin/views/main.py

     
    710710        qs = qs.order_by((self.order_type == 'desc' and '-' or '') + lookup_order_field)
    711711
    712712        # Apply keyword searches.
    713         if self.lookup_opts.admin.search_fields and self.query:
    714             for bit in self.query.split():
    715                 or_queries = [models.Q(**{'%s__icontains' % field_name: bit}) for field_name in self.lookup_opts.admin.search_fields]
    716                 other_qs = QuerySet(self.model)
    717                 other_qs = other_qs.filter(reduce(operator.or_, or_queries))
    718                 qs = qs & other_qs
     713        if self.opts.search_fields and self.query:
     714            qs = qs.search(self.query)
    719715
    720716        if self.opts.one_to_one_field:
    721717            qs = qs.complex_filter(self.opts.one_to_one_field.rel.limit_choices_to)
  • django/contrib/admin/templates/admin/search_form.html

     
    11{% load adminmedia %}
    22{% load i18n %}
    3 {% if cl.lookup_opts.admin.search_fields %}
     3{% if cl.opts.search_fields %}
    44<div id="toolbar"><form id="changelist-search" action="" method="get">
    55<div><!-- DIV needed for valid HTML -->
    66<label for="searchbar"><img src="{% admin_media_prefix %}img/admin/icon_searchbox.png" alt="Search" /></label>
  • docs/generic_views.txt

     
    673673    * ``mimetype``: The MIME type to use for the resulting document. Defaults
    674674      to the value of the ``DEFAULT_MIME_TYPE`` setting.
    675675
     676    * ``allow_search``: A boolean specifying whether searching is allowed
     677      for theese objects. If this is ``False``, objects will not be
     678      searchable. By default, this is ``True``.
     679
    676680**Template name:**
    677681
    678682If ``template_name`` isn't specified, this view will use the template
     
    713717    * ``hits``: The total number of objects across *all* pages, not just this
    714718      page.
    715719
     720    * ``query``: The search query string. if searching is disabled,
     721      this is an empty string.
     722
    716723Notes on pagination
    717724~~~~~~~~~~~~~~~~~~~
    718725
Back to Top