mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #3397: You can now order by non-DB fields in the admin by telling Django which field to actually order by. Thanks, marcink@elksoft.pl
git-svn-id: http://code.djangoproject.com/svn/django/trunk@4596 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -84,22 +84,31 @@ def result_headers(cl): | ||||
|                     header = attr.short_description | ||||
|                 except AttributeError: | ||||
|                     header = field_name.replace('_', ' ') | ||||
|             # Non-field list_display values don't get ordering capability. | ||||
|             yield {"text": header} | ||||
|  | ||||
|             # It is a non-field, but perhaps one that is sortable | ||||
|             if not getattr(getattr(cl.model, field_name), "admin_order_field", None): | ||||
|                 yield {"text": header} | ||||
|                 continue | ||||
|  | ||||
|             # So this _is_ a sortable non-field.  Go to the yield | ||||
|             # after the else clause. | ||||
|         else: | ||||
|             if isinstance(f.rel, models.ManyToOneRel) and f.null: | ||||
|                 yield {"text": f.verbose_name} | ||||
|                 continue | ||||
|             else: | ||||
|                 th_classes = [] | ||||
|                 new_order_type = 'asc' | ||||
|                 if field_name == cl.order_field: | ||||
|                     th_classes.append('sorted %sending' % cl.order_type.lower()) | ||||
|                     new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()] | ||||
|                 header = f.verbose_name | ||||
|  | ||||
|                 yield {"text": f.verbose_name, | ||||
|                        "sortable": True, | ||||
|                        "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), | ||||
|                        "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')} | ||||
|         th_classes = [] | ||||
|         new_order_type = 'asc' | ||||
|         if field_name == cl.order_field: | ||||
|             th_classes.append('sorted %sending' % cl.order_type.lower()) | ||||
|             new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()] | ||||
|  | ||||
|         yield {"text": header, | ||||
|                "sortable": True, | ||||
|                "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), | ||||
|                "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')} | ||||
|  | ||||
| def _boolean_icon(field_val): | ||||
|     BOOLEAN_MAPPING = {True: 'yes', False: 'no', None: 'unknown'} | ||||
|   | ||||
| @@ -655,10 +655,17 @@ class ChangeList(object): | ||||
|             order_field, order_type = ordering[0], 'asc' | ||||
|         if params.has_key(ORDER_VAR): | ||||
|             try: | ||||
|                 field_name = lookup_opts.admin.list_display[int(params[ORDER_VAR])] | ||||
|                 try: | ||||
|                     f = lookup_opts.get_field(lookup_opts.admin.list_display[int(params[ORDER_VAR])]) | ||||
|                     f = lookup_opts.get_field(field_name) | ||||
|                 except models.FieldDoesNotExist: | ||||
|                     pass | ||||
|                     # see if field_name is a name of a non-field | ||||
|                     # that allows sorting | ||||
|                     try: | ||||
|                         attr = getattr(lookup_opts.admin.manager.model, field_name) | ||||
|                         order_field = attr.admin_order_field | ||||
|                     except IndexError: | ||||
|                         pass | ||||
|                 else: | ||||
|                     if not isinstance(f.rel, models.ManyToOneRel) or not f.null: | ||||
|                         order_field = f.name | ||||
|   | ||||
| @@ -1295,10 +1295,30 @@ A few special cases to note about ``list_display``: | ||||
|  | ||||
|           list_display = ('__str__', 'some_other_field') | ||||
|  | ||||
|     * For any element of ``list_display`` that is not a field on the model, the | ||||
|       change list page will not allow ordering by that column. This is because | ||||
|       ordering is done at the database level, and Django has no way of knowing | ||||
|       how to order the result of a custom method at the SQL level. | ||||
|     * Usually, elements of ``list_display`` that aren't actual database fields | ||||
|       can't be used in sorting (because Django does all the sorting at the | ||||
|       database level). | ||||
|        | ||||
|       However, if an element of ``list_display`` represents a certain database | ||||
|       field, you can indicate this fact by setting the ``admin_order_field`` | ||||
|       attribute of the item. | ||||
|        | ||||
|       For example:: | ||||
|        | ||||
|         class Person(models.Model): | ||||
|             first_name = models.CharField(maxlength=50) | ||||
|             color_code = models.CharField(maxlength=6) | ||||
|  | ||||
|             class Admin: | ||||
|                 list_display = ('first_name', 'colored_first_name') | ||||
|  | ||||
|             def colored_first_name(self): | ||||
|                 return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name) | ||||
|             colored_first_name.allow_tags = True | ||||
|             colored_first_name.admin_order_field = 'first_name' | ||||
|      | ||||
|       The above will tell Django to order by the ``first_name`` field when | ||||
|       trying to sort by ``colored_first_name`` in the admin. | ||||
|  | ||||
| ``list_display_links`` | ||||
| ---------------------- | ||||
|   | ||||
		Reference in New Issue
	
	Block a user