views:

1456

answers:

4

How can you perform complex sorting on an object before passing it to the template? For example, here is my view:

@login_required
def overview(request):
   physicians = PhysicianGroup.objects.get(pk=physician_group).physicians

for physician in physicians.all():
    physician.service_patients.order_by('bed__room__unit', 'bed__room__order', 'bed__order')

return render_to_response('hospitalists/overview.html', RequestContext(request,  {'physicians': physicians,}))

The physicians object is not ordered correctly in the template. Why not?

Additionally, how do you index into a list inside the template? For example, (this doesn't work):

{% for note_type in note_types %}
   <div><h3>{{ note_type }}</h3>
   {% for notes in note_sets.index(parent.forloop.counter0) %}
   #only want to display the notes of this note_type!
      {% for note in notes %}
         <p>{{ note }}</p>
      {% endfor %}
   {% endfor %}
   </div>
{% endfor %}

Thanks a bunch, Pete

+3  A: 

"I'd like to do this from within a template:"

Don't. Do it in the view function where it belongs.

Since the question is incomplete, it's impossible to guess at the data model and provide the exact solution.

results= physician.patients.order_by('bed__room__unit', 'bed__room__order', 'bed__order')

Should be sufficient. Provide results to the template for rendering. It's in the proper order.

If this isn't sorting properly (perhaps because of some model subtletly) then you always have this kind of alternative.

def by_unit_room_bed( patient ):
    return patient.bed.room.unit, patient.bed.room.order, patient.bed.order

patient_list = list( physician.patients )
patient_list.sort( key=by_unit_room_bed )

Provide patient_list to the template for rendering. It's in the proper order.

"how do you index into a list inside the template"

I'm not sure what you're trying to do, but most of the time, the answer is "Don't". Do it in the view function.

The template just iterate through simple lists filling in simple HTML templates.

If it seems too complex for a template, it is. Keep the template simple -- it's only presentation. The processing goes in the view function

S.Lott
How do you propose I sort the objects within the object prior to passing it to the view? For example, this doesn't work: physician.service_patients = physician.service_patients.all().order_by('bed__room__unit', 'bed__room__order', 'bed__order')
slypete
What do you mean "doesn't work". What does it do wrong?
S.Lott
The assignment makes the object unusable since service_patients is a ForeignKey.
slypete
It was just an example. What I'm looking for is a way to sort the patients within each physician in the physicians query_set prior to passing it to the template.
slypete
@slypete: `physician.service_patients = ...order_by(...)` to force ordering doesn't make any sense at all. You can't impose ordering by "updating" anything in the database.
S.Lott
@S.Lott, I'm trying to sort the object in memory before passing it to the template. I'm not trying to modify any table. In your solution, you create patient_list. I'm trying to avoid this and simply pass in the physicians object.
slypete
+1 Django templates cannot be used to perform program logic (like sorting). Don't use django templates for this. either use a different part of django (the views) or different templates (Jinja/Genshi)
TokenMacGuy
@TokenMacGuy, I just said I'm trying to sort the object in memory before passing it to the template. No one can seem to provide me with an example of how to sort the original object.
slypete
@slypete: The "original object" is a query set. It comes back from the database as sorted by SQL, via the `order_by` method. That's the sorting that's done.
S.Lott
A: 

You should be able to construct the ordered query set in your view and pass it to your template:

def myview(request):
    patients = Physician.patients.order_by('bed__room__unit', 
                                           'bed__room__order', 
                                           'bed__order')
    return render_to_response('some_template.html',
                              dict(patients=patients), 
                              mimetype='text/html')

Your template can then loop over patients which will contain the ordered results. Does this not work for you?

EDIT: For indexing, just use the dot syntax: mylist.3 in a template becomes mylist[3] in python. See http://docs.djangoproject.com/en/dev/ref/templates/api/#rendering-a-context for more information.

ars
No it doesn't since I pass in a physician set to the template. Is it possible to sort each Physician's patients in the physician set prior to passing it to the template?
slypete
I guess I could pass in a physician set and a list of ordered patient sets, but it doesn't feel right... if you know what I mean. I rather just pass in one set and I figured that I just don't know how to do it.
slypete
I assumed Physician was a model and patients was a model manager. I guess you could create an instance method on a physician: get_patients, which just returns a query set of ordered patients. You should be able to call that in a template and iterate over the result set.
ars
Thanks that worked. Still trying to learn how to index into a list within a template!
slypete
Updated the answer for indexing into a list.
ars
It works fine with a hardcoded number. I need this to work: {% for note in note_sets.parent.forloop.counter0 %}, but it doesn't.
slypete
I guess you could use the slice filter (for a slice of one): http://docs.djangoproject.com/en/dev/ref/templates/builtins/#slice ... or write your own custom filter .. or just use the same method as above, i.e. write an instance method for this particular case.
ars
+3  A: 

As others have indicated, both of your problems are best solved outside the template -- either in the models, or in the view. One strategy would be to add helper methods to the relevant classes.

Getting a sorted list of a physician's patients:

class Physician(Model):
   ...
   def sorted_patients(self):
      return self.patients.order_by('bed__room__unit',
                                    'bed__room__order',
                                    'bed__order')

And in the template, use physician.sorted_patients rather than physician.patients.

For the "display the notes of this note_type", it sounds like you might want a notes method for the note_type class. From your description I'm not sure if this is a model class or not, but the principle is the same:

class NoteType:
   ...
   def notes(self):
      return <calculate note set>

And then the template:

{% for note_type in note_types %}
   <div><h3>{{ note_type }}</h3></div>
   {% for note in note_type.notes %}
      <p>{{ note }}</p>
   {% endfor %}
   </div>
{% endfor %}
John
Thanks, it's a little hard for me to get my head around the fact that almost nothing can be done in the template! Not even indexing into a list!
slypete
A: 

This is one way of doing it, although very ugly :

{% for note in note_sets|slice:"forloop.counter0"|first %}

Nicolo John Davis
Sorry, this doesn't work. Just tried it.
Nicolo John Davis
lol, this is nasty.
slypete