views:

62

answers:

2

So basically, I've got a rather large Django project going. It's a private web portal that allows users to manage various phone-related tasks.

Several pages of the portal provide a listing of Model objects to users, and list all of their attributes in a HTML table (so that users can visually look through a list of these items).

The problem I'm having is: I cannot find a Django-ish or pythonic way to handle the sorting of these Model objects by field name. As an example of what I'm talking about, here is one of my views which lists all Partyline Model objects:

def list_partylines(request):
    """
    List all `Partyline`s that we own.
    """

    # Figure out which sort term to use.
    sort_field = request.REQUEST.get('sortby', 'did').strip()
    if sort_field.startswith('-'):
        search = sort_field[1:]
        sort_toggle = ''
    else:
        search = sort_field
        sort_toggle = '-'

    # Check to see if the sort term is valid.
    if not (search in Partyline._meta.get_all_field_names()):
        sort_field = 'did'

    if is_user_type(request.user, ['admin']):
        partylines = Partyline.objects.all().order_by(sort_field)
    else:
        partylines = get_my_partylines(request.user, sort_field)

    variables = RequestContext(request, {
        'partylines': partylines,
        'sort_toggle': sort_toggle
    })
    return render_to_response('portal/partylines/list.html', variables)

The sorting code basically allows users to specify a /url/?sortby=model_field_name parameter which will then return a sorted listing of objects whenever users click on the HTML table name displayed on the page.

Since I have various views in various apps which all show a listing of Model objects, and require sorting, I'm wondering if there is a generic way to do this sorting so that I don't have to?

I'm sorry if this question is a bit unclear, I'm struggling to find the right way to phrase this question.

Thanks.

A: 

There's a great example of how this is done in a generic way in django.contrib.admin.views.main.ChangeList although that does much more than sorting you can browse it's code for some hints and ideas. You may also want to look at django.contrib.admin.options.ModelAdmin the changelist method in particular to get more context.

Vasil
+1  A: 

The way that I'd look at doing this is through a custom QuerySet. In your model, you can define the class QuerySet and add your sorting there. In order to maintain all the logic in the model object, I'd also move the contents of get_my_partylines into the QuerySet, too.

## This class is used to replicate QuerySet methods into a manager.
## This way:  Partyline.objects.for_user(foo) works the same as
## Partyline.objects.filter(date=today).for_user(foo)
class CustomQuerySetManager(models.Manager):
    def get_query_set(self):
        return self.model.QuerySet(self.model)
    def __getattr__(self, attr, *args):
        try:
            return getattr(self.__class__, attr, *args)
        except AttributeError:
            return getattr(self.get_query_set(), attr, *args)


class Partyline(models.Model):
    ## Define fields, blah blah.
    objects = CustomQuerySetManager()
    class QuerySet(QuerySet):
        def sort_for_request(self, request):
            sort_field = request.REQUEST.get('sortby', 'did').strip()
            reverse_order = False
            if sort_field.startswith('-'):
                search = sort_field[1:]
            else:
                search = sort_field
                reverse_order = True

            # Check to see if the sort term is valid.
            if not (search in Partyline._meta.get_all_field_names()):
                sort_field = 'did'

            partylines = self.all().order_by(sort_field)
            if reverse_order:
                partylines.reverse()
            return partylines
        def for_user(self, user):
            if is_user_type(request.user, ['admin']):
                return self.all()
            else:
                ## Code from get_my_partylines goes here.
                return self.all() ## Temporary.

views.py:

def list_partylines(request):
    """
    List all `Partyline`s that we own.
    """
    partylines = Partylines.objects.for_user(request.user).sort_for_request(request)
Jack M.
I like the for_user approach. Is that somehow a convention, or just common sense?
Agos
Just something I hacked together (with some help from SO) as a convention for my own projects. It seems to make logical sense and keeps the code readable.
Jack M.