views:

40

answers:

1

I am building an application having to do with game achievements, and I want to display for each game, a page that lists the related achievements. That is done.

However, I also want each user to be able to see in that list which achievements they have completed.

My model is like this:

class Achievement(models.Model):
    game = models.ForeignKey(Game)
    ...

class Game(models.Model):
    ...

class GameProfile(models.Model):
    achievements = models.ManyToManyField(Achievement, through='Achieved')
    ...

class Achieved(models.Model):
    profile = models.ForeignKey(GameProfile)
    achievement = models.ForeignKey(Achievement)
    curr_count = models.PositiveSmallIntegerField()

In the game template page, I get all the achievements for the current game through the relationship with the Achievement model (ie achievements = game.achievement_set.all). Then I do a {% for ach in achievements %} on them and output a <li> for each. If a user is logged in, I also want to display the curr_count associated with the achievement. So I need a list of Achieved objects for the specific GameProfile (think of it as the current user).

The problem is that I cannot do {% if ach in achieved %} (where ach is an Achievement instance and achieved an Achieved QuerySet), obviously.

I also thought I could get a QuerySet with the Achievements the user has achieved, but that lacks the curr_count, which I want to display.

I think what I want is some kind of list or dictionary with the Achievements of the user against which I can test if a specific Achievement is a member, but which will also carry the curr_count variable. Another solution might be to somehow add curr_count as a field to a QuerySet of Achievement objects (which will be the ones the user has achieved).

Can you suggest how to accomplish this in django?

+2  A: 

If your GameProfile model has a link to (auth.)User then you can generate the following queryset and send it to the template.

def achievements_by_user(request, *args, **kwargs):
    user = request.user
    queryset = Achieved.objects.filter(profile__user = user)
    return render_to_response(...)

This queryset will filter all rows from Achieved based on the currently logged in user. If you also want to filter based on game (i.e. return all achievements for the currently logged in user for a specific game) then you can add another filter condition.

    queryset = Achieved.objects.filter(profile__user = user, 
            achievement__game = <a game>)

Update

Apparently I misunderstood the question. Here is another stab at it.

@register.filter
def achievement_in(achieved, achievement):
    count = achieved.filter(achievement = achievement).count()
    return count > 0

# In template
{% for ach in achievements %}
    {% if achieved|achievement_in:ach %} 
        ...
    {% else %}
        ...
    {% endif %}
{% endfor %}
Manoj Govindan
Thank you for your answer, however I believe it is irrelevant. I know how to produce a queryset with only the achievements of the current user. My question is, if I have that queryset, and I am looping through an Achievements queryset, how can I test whether an Achievement instance is contained in the Achieved queryset, which is a different model (hence, the {% if ... in %} test doesn't work), and then retrieve the value of a field from that specific Achieved entry of the queryset.
FrontierPsycho
Three points. (1) Apparently I did not understand your question properly. I'm sorry (2) I do not know of a comparison that will check if an instance of `Achievement` in an `Achieved` queryset using Django's built-in filters (3) IMHO you are better off doing this filtering in the view rather than in the template. That said, you can write a custom filter function that tests if an `Achievement` is present in an `Achieved` queryset. Behind the scenes the filter function would execute a filter query to er, achieve this.
Manoj Govindan
Have updated my answer with a rough example of such a filter.
Manoj Govindan
Thank you! This does solve the problem. I would also prefer to do this in the view rather than the template, but I don't see how it can be done. I somehow need to obtain the correct Achieved object in each iteration of the loop on the Achievement list, which is done on the template. Can you briefly sketch in what way I could do this in the view?
FrontierPsycho