views:

143

answers:

2

Here are my models:

class Activity(models.Model):
    title = models.CharField(blank=False, max_length=100)
    description = models.TextField(blank=False)


class UserActivityWork(models.Model):
    activity = models.ForeignKey(Activity)
    user  = models.ForeignKey(User)
    hours_worked = models.FloatField()
    comment = models.TextField()

Example data would be, an Activity of "climbing Mt Everest" and each user would be able to input how long it took them and a comment.

Here's my question: How can I display a list of all the Activities, and if the user has entered data for that Activity, display the pertinent details next to the Activity?

So far, I have considered:

  • creating a dictionary of UserActivityWork with a key of the Activity id and a value of the user's UserActivityWork. This would be fine with me, but I have no idea of how to do this in django's templating system (ie, how do you say: {{ user_work[activity.id] }})
  • creating an object that would hold both the Activity and UserActivityWork. I haven't done this one, because I am hoping that django has a better way to do this.

Any insight would be greatly appreciated!

+1  A: 

Assuming you have 2 querysets accessable from within your template (say as activities and user_activities)

A naive way would be to iterate over each activity and then over each user activity.

{% for activity in activities %}
    {{ activity.title }} 
    {% for user_activity in user_activities %}
        {% ifequal user_activity.activity activity %}
            Display userdata
        {% endifequal %}    
    {% endfor %}
{% endfor %}

Dictionary lookups can be performed in templates by using a dot (.)

Technically, when the template system encounters a dot, it tries the following lookups, in this order:

  • Dictionary lookup
  • Attribute lookup
  • Method call
  • List-index lookup

Another option would be to create a custom template tag. You could loop over the activity list as before and then pass the activity and either the user_activity list or the user to the tag to perform the lookup and render the required data.

Gerry
A: 

Thanks for the hint, Gerry. I found that writing a custom template tag as you suggested was the way to go. Here are the gory details, in case anyone stumbles across this.

In the view method, I published a dictionary "user_activity_status" which contains a key of activity.id and value of UserActivityWork object for the logged in user's work on that activity

This is the the relevant section of the template. Basically this going to add a variable "map_value" with a value of

getattr(user_activity_status[activity.id], "comment")

Here's the template:

{% load *file-name-of-the-templatetag-file* %}
{% access_map_method user_activity_status activity.id comment %}
{% if map_value %}
    {{ map_value }}
{% else %}
    get working sucka!
{% endif %}

here is the section of the templatetag file (see Gerry's links for the details of how to set this up)

from django import template

register = template.Library()

@register.tag(name="access_map_method")
def do_access_map_method(parser, token):
    try: 
     tag_name, dict_name , key_name, method_name = token.contents.split()
    except ValueError:
     msg = '%r tag requires three arguments' % token.contents[0]
     raise template.TemplateSyntaxError(msg)

    return MapNode(dict_name , key_name, method_name)

class MapNode(template.Node):
    def __init__(self, dict_name, key_name, method_name):
     self.dict_var = template.Variable(dict_name)
     self.key_var  = template.Variable(key_name)
     self.method_name = method_name

    def render(self, context):
     try:
      dict_obj = self.dict_var.resolve(context)
      key_obj  = self.key_var.resolve(context)
      if key_obj in dict_obj.keys():
       if self.method_name:
        context['map_value'] = getattr(dict_obj[key_obj], self.method_name)
       else:
        context['map_value'] = dict_obj[key_obj] 
      else:
       context['map_value'] = ''
     except template.VariableDoesNotExist:
      context['map_value'] = ''

     return ''
Michael