Showing posts with label sorting. Show all posts
Showing posts with label sorting. Show all posts

Tuesday, June 19, 2012

Django verbose_name in your template

There are many situations when you want to display model field verbose_name in template when displaying tables and other data. Ideally you would put this value in header section of your table for DRY purposes.
The problem is that it's unable to use model _meta in templates. You can achive the same using an example verbose_name_tags.py templatetag.
# verbose_name_tags.py

from django import template

register = template.Library()

def get_field_verbose_name(instance, arg):
    return instance._meta.get_field(arg).verbose_name
register.filter('field_verbose_name', get_field_verbose_name)

def get_queryset_field_verbose_name(queryset, arg):
    return queryset.model._meta.get_field(arg).verbose_name
register.filter('queryset_field_verbose_name', get_queryset_field_verbose_name)

you can get your header names rendered the DRY way.
Assuming your model looks like this:
class Product(models.Model):
    name = models.CharField(_(u"Name"), max_length=20)
    weight = models.PositiveIntegerField(_(u"Weight"))

Your template would be similar to the one below:
{% load verbose_name_tags %}

<table>
  <thead>
  <tr>    
    <th>{{ object_list|queryset_field_verbose_name:"name" }}</th>
    <th>{{ object_list|queryset_field_verbose_name:"weight" }}</th>
  </tr>
  </thead>
  <tbody>
{% for product in object_list %}    
  <tr>
    <td>{{ product.name }}</td>
    <td>{{ product.weight }}</td>
  </tr>
{% endfor %}
</table>

What if you need sorting ?
For some time I've been using a django-sorting for sorting my tabular data.
The new code
{% load sorting_tags %}
{% autosort object_list %}

<table>
  <thead>
  <tr>    
    {# _("Name").. need to specify the display name all the time.. bad.. #}
    <th>{% autosort 'name' _("Name") %}</th>
    <th>{% autosort 'weight' _("Weight") %}</th>
  </th></tr>
  </thead>
  <tbody>
{% for product in object_list %}  
  <tr>
    <td>{{ product.name }}</td>
    <td>{{ product.weight }}</td>
  </tr>
{% endfor %}
  </tbody>
</table>

Great, but this is not DRY! You can do this the other way. Note: I think {% autosort %} should take verbose_name by default (maybe will take a look into the code later, but for now we can achieve the same using a {% with %} and verbose_name_tags.py template tags.
{% load sorting_tags %}
{% load verbose_name_tags %}

{% autosort object_list %}

{% endfor %}
<table>
  <thead>
  <tr>
    {% with name=object_list|queryset_field_verbose_name:"name" %}    
    <th>{% autosort 'name' name%}</th>
    {% endwith %}
    {% with weight=object_list|queryset_field_verbose_name:"weight" %}
    <th>{% autosort 'weight' weight %}</th>
    {% endwith %}
  </tr>
  </thead>
  <tbody>
  <tr>
    <td>{{ product.name }}</td>
    <td>{{ product.weight }}</td>
  </tr>
  </tbody>
</table>

Now we can just change verbose_name in models.py and we don't have to worry about the table headers.
You can also use a "field_verbose_name" for model instances:
{# assuming {{ product }} is an instance of Product model #}
{% load verbose_name_tags %}

{{ product|field_verbose_name:"name" }}: {{ product.name }}

Some might wonder why didn't I simply use one templatetag name which would identify if it checks for queryset or instance field. I decided to use different names to quickly see If I act with querysets or instances. Hope you like it.

Sunday, July 5, 2009

django-sorting a generic way of queryset sorting


Just found a great project django-sorting. It's a generic way of queryset sorting.
It's based on middleware, and templatetag.

Please read the README.txt of the project.

In couple easy steps you get sorting:

1. Download & install django-sorting
2.In your settings.py
- add 'django-sorting' to INSTALLED_APPS
- add 'django_sorting.middleware.SortingMiddleware' to MIDDLEWARE_CLASSES
(make sure 'django.core.context_processors.request' is in your TEMPLATE_CONTEXT_PROCESSORS
3. In your template code: (the one below goes directly from README.txt

...
{% anchor first_name "First Name" %}
{% anchor last_name "Last Name" %}
{% anchor creation_date Creation flojax my_container %}
...

The first argument is a field of the objects list, and the second

one(optional) is a title that would be displayed. The previous

snippet will be rendered like this:


...
First Name
Last Name
Creation
...



It's BRILLIANT!


You should also use django-pagination for pagination support written by Eric as well.


Thank you ERIC!

Ps. Big thanks go to Karim (first author of django-sorting code) and other contributtors of these 2 great projects.

Thursday, October 9, 2008

django-tables generic way for displaying tables (pagination included)

In thnis short post I would like to let you know about the great project
django-tables.

You can read more about it at:
http://blog.elsdoerfer.name/2008/07/09/django-tables-a-queryset-renderer/

In a couple of words it lets you:
- display the table using one template which lets you sort & paginate easily (filtering in plans!)

By reading the text below you will find out how to create table with sort & pagination quickly.

In usage it's very similar do django newforms (forms as of 1.0)
Example: (views.py)

class MyTable(tables.ModelTable):

  class Meta:
      model = SomeModel

def view_something(request):
  table = MyTable(queryset, order_by=request.GET.get('sort','some_field))

  return render_to_response('templates/table.html', {'table': table, 'rows' : table.rows})
# we need to return table, and  rows separately to use django-pagination (though django-tables provides its own pagination mechanism)

Now let's get to template:

{% load pagination_tags %}
{% autopaginate rows 10 %}



  {% for column in table.columns %}

  {% endfor %}


{% for row in rows %}

  {% for value in row %}

{% endfor %}


{% if column.sortable %}
{{ column }}
{% if column.is_ordered_reverse %}

{% else %}

{% endif %}
{% else %}
{{ column }}
{% endif %}
{{ value }}
{% endfor %}
{% paginate %}


That's it!

This way you get generic way of displaying tables + sorting ability.
By default, django-tables takes field names for table headers when using ModelTable (verbose_name is in plans as well).
You can exclude displaying fields too!

As for now, you can specify your own names in MyTable class.

Let's get back to our class:

class MyTable(tables.ModelTable):
  class Meta:
      model = SomeModel

  field1 = tables.Column(name="Name")
  field_we_want_to_exclude = tables.Column(visiable=Fale, sortable=False)


NOTE:
Django tables is also aware of ForeignKey relations.

Check it out!