Django get ContentType in a template
Asked Answered
B

3

9

I have a page with a lot of objects with different content types. I need to have an ability to rate this objects. Here is a class for it:

class Score(models.Model):
    user            = models.ForeignKey(User)

    content_type    = models.ForeignKey(ContentType)
    object_id       = models.PositiveIntegerField()
    for_object      = generic.GenericForeignKey('content_type', 'object_id')

    like            = models.BooleanField(default=True)
    created_at      = models.DateTimeField(auto_now_add=True, blank=True, null=True)

    comment         = models.CharField(max_length=255, blank=True, null=True)

    objects = ChainerManager(ScoreQuerySet)

    def __unicode__(self):
        return u'Score for (%s, #%s) from user %s at %s' %\
            (self.content_type, self.object_id, self.user.get_full_name(), self.created_at)

    class Meta:
        unique_together = (('user', 'content_type', 'object_id'),)

And my template should look like:

...
{% for random_object in random_object_queryset %}
<a href={% url like_object random_object.<content_type> random_object.id %}>{{ random_object.name }}</a>
<a href={% url dislike_object random_object.<content_type> random_object.id %}>{{ random_object.name }}</a>
{% endfor %}
...

I can make template tag to get it, or get a classname, using i.e. this snippet: http://djangosnippets.org/snippets/294/ I can rewrite this snuppet to get the content_type_id for object, but i'm afraid a little about big amount of CT lookups in DB.

But is there some embedded method to get object's CT in a template?

The view code:

def rate_object(request, classname, object_id, like=True):
    user = request.user
    Klass = ContentType.objects.get(model=classname).model_class()
    obj = get_object_or_404(Klass, user=user, pk=object_id)

    try:
        score = Score.objects.for_object(user, obj)
        score.like = like
        score.save()
    except Score.DoesNotExist:
        score = Score.objects.like(user, obj) if like else Score.objects.dislike(user, obj)

    return HttpResponse(obj)
Blinking answered 9/10, 2012 at 18:36 Comment(1)
Just for information: You shouldn't add that much spaces after your variables. It's not PEP8 ( python.org/dev/peps/pep-0008/… )Bulldozer
M
12

To build on @Colleen 's answer, I ended up using a template filter like so:

from django import template
from django.contrib.contenttypes.models import ContentType

register = template.Library()

@register.filter
def content_type(obj):
    if not obj:
        return False
    return ContentType.objects.get_for_model(obj)

And used it in a template like so:

{% load helpers %}
{% with instance|content_type as ctype %}
    <input type="hidden" name="content_type" value="{{ ctype.pk }}">
{% endwith %}
Motherless answered 31/12, 2013 at 8:27 Comment(5)
for further discussion and forking, see this snippetMotherless
This {% with instance|content_type as ctype %} syntax, is it documented and supported? Does {% with a=b|c %} also work? What about {% with a=b|c d=e|f %}? I can't find it in the documentation.Osteoid
with is a built-in template tag (as opposed to the custom filter above), here it is in Django's official docsMotherless
I know. What I can't find out is whether you can use filters in with. Apparently you have tried it and it works, but I was wondering whether it is documented and supported behaviour.Osteoid
Good point, it seems it is not explicitly stated anywhere. Though it is implied in at least a couple of places. For instance in the introduction to tags/filters check out the part about if variables able to use a filter, or in the dictsort filter examples.Motherless
B
4

I prefer doing this with assignment tags (new in Django 1.4):

@register.assignment_tag
def content_type(obj):
    if not obj:
        return False
    return ContentType.objects.get_for_model(obj)

and used as

{% content_type object as object_ct %}
Backbencher answered 21/10, 2014 at 11:18 Comment(0)
M
2

I also had a situation where I needed the content type in the template and the only way I found I could get it was through a custom template tag.

In your situation, though, since you're storing content_type explicitly as a foreign key, I wouldn't worry about it. Worst case you can use prefetch_related() when you get your score objects in the view. I don't know if Django is smart enough to stop at the field if you ask for a foreignkey.id, is the only thing.

Misreckon answered 9/10, 2012 at 19:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.