views:

49

answers:

3

I'm using generic types in my Profile model:

user_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
details = generic.GenericForeignKey('user_type', 'object_id')

But now I want to check if a user is a certain type from within my template. I can get the user type with {{ user.get_profile.user_type }} but then what? Or how would I add a method to the model like is_type_xxx so that I could use it in the template?


I've added the following methods to my Profile model:

def is_user_type(self, type):
    return self.user_type == ContentType.objects.get_for_model(type),

@property
def is_shipper(self):
    return self.is_user_type(Shipper),

... *face palm*

The trailing comma was causing it to return a tuple, which evaluates to true.

+1  A: 

user_type is a ForeignKey to a ContentType model, so treat it as you would any relation.

Ignacio Vazquez-Abrams
+1  A: 

Although Ignacio is basically correct, I find that there can be a really tendency to get a lot of unintended DB hits if you're not careful. Since the number of ContentTypes tends to be small and relatively unchanging, I cache a dict of name/id pairs to avoid it. You can use a signal to update your dict on the off chance a new ContentType is created.

You can then auto-create the necessary is_type_xxx() functions/methods as needed. It's a little klunky, but the code isn't very complicated.

Peter Rowell
Supposedly the framework already caches the content types to minimize db hits.
Mark
Yes, supposedly. Have you run the DB query stats and checked? And since you can't pass args in Django's template "language" you will still need to create (by hand or automatically) all of the necessary `is_type_xxx()` methods, something the ORM will *not* do for you. We have one app where there are over 150 ContentTypes (with more added on the fly), so it's a lot cheaper to do one fetch of the whole mess and build the dict and type-testing methods.
Peter Rowell
+1  A: 

Maybe I don't fully understand the question, but it seems you can just define a function in your model that returns true if the type is what you need, then you can call that function from the template just as you would if you were accessing a variable.

In the model...

user_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
details = generic.GenericForeignKey('user_type', 'object_id')

def IsTypeX():
    return user_type == x

In the template...

{% if user.get_profile.IsTypeX %}
{% endif %}
Fraser Graham
More precisely `return self.user_type == ContentType.objects.get_for_model(type)` but you're right.
Mark