80

I want to call a function from my model at a template such as:

class ChannelStatus(models.Model):
 ..............................
 ..............................

    def get_related_deltas(self,epk):
        mystring = ""
        if not self.get_error_code_delta(epk):
            return mystring
        else:
            for i in self.get_listof_outage():
                item = i.error_code.all()
                for x in item:
                    if epk == x.id:
                        mystring= mystring +" "+str(i.delta())
        return mystring         

And when I want to call this from the template: assume while rendering, I pass channel_status_list as

channel_status_list = ChannelStatus.objects.all()

{% for i in channel_status_list %}
  {{ i.get_related_deltas(3) }}
{% endfor %}

This doesn't work, I am able to call a function that consumes nothing, but couln't find what to do if it has parameter(s)

Cheers

Serjik
  • 10,543
  • 8
  • 61
  • 70

5 Answers5

96

You can't call a function with parameters from the template. You can only do this in the view. Alternatively you could write a custom template filter, which might look like this:

@register.filter
def related_deltas(obj, epk):
    return obj.get_related_deltas(epk)

So now you can do this in the template:

{% for i in channel_status_list %}
  {{ i|related_deltas:3 }}
{% endfor %}
Paolo Stefan
  • 10,112
  • 5
  • 45
  • 64
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • hello, thanks alot for the backup! I am getting "related_deltas requires 1 arguments, 0 provided" error. I am doing exactly as you said. Regards –  Aug 26 '09 at 09:17
  • Hmm, that should work. Can you post the full traceback (probably somewhere like dpaste.com rather than here)? – Daniel Roseman Aug 26 '09 at 09:59
  • That seems to be a different error, probably caused by a double pipe character in `{{ forloop.parentloop.counter0||add:"5" }}`. – Daniel Roseman Aug 26 '09 at 11:07
  • actually at my template, it is {{ forloop.parentloop.counter0|add:"5" }}, It is really weird it display it as "||". –  Aug 26 '09 at 11:10
  • I agree with Daniel, this does appear to be an issue with your template. – wlashell Aug 26 '09 at 15:16
  • 3
    Hey guys, where is that related_deltas function defined in the code? I'm getting a Name Error: name 'register' is not defined when i try to put it in my view. – Tyler Brock Jul 24 '11 at 17:39
  • So you have to create a template tag for every function you want to call with parameters? – quantumpotato Aug 07 '12 at 20:27
  • @TylerBrock i get the same error, did you manage to solve it ? – Fady Kamal Sep 26 '12 at 10:01
  • @FadyKamal too long ago... sorry. I totally forget what I wound up doing. – Tyler Brock Sep 26 '12 at 13:07
  • @TylerBrock `register` can be used like this: `from django import template` and then `register = template.Library()`. [see the docs](https://docs.djangoproject.com/en/dev/howto/custom-template-tags/) – vlz Mar 21 '23 at 13:51
42

If the method doesn't require any arguments, you can use the @property decorator and access it normally in the template.

class ChannelStatus(models.Model):
    ...
    @property
    def function_you_want_as_property(self):
        mystring = ""
        ...
sabalaba
  • 1,296
  • 12
  • 17
9

For > 1 argument, use simple tags:

@register.simple_tag
def related_deltas(obj, epk, second_arg):
    return obj.get_related_deltas(epk, second_arg)

Template:

{% for i in channel_status_list %}
  {% related_deltas i 3 4 %}
{% endfor %}

(Note the change of syntax from {{ to {%)

Can take positional parameters (e.g. related_deltas i 3 second_arg=4 debug=true).

Community
  • 1
  • 1
laffuste
  • 16,287
  • 8
  • 84
  • 91
6

If you find that there are too many properties running around everywhere or you have a template filter for every other method that you write, another solution was suggested on IRC thanks @FunkyBob. It's a little well, erm, funky but it is nice in certain cases.

  class MethodProxy(object):
        """For consolidating into 1 method the calling of methods with various single args
        (suitable dictionary keys)

        class MyModel(models.Model):
            ...

            def magic(self, value):
                # Free the ponies

            def thing(self):
                return MethodProxy(self.magic)

        # Usage
        >>> m = MyModel()
        ...
        >>> m.thing['value'] == m.magic('value')

        # template
        {{ m.thing.value }}

        """

        def __init__(self, method):
            self.method = method
        def __getitem__(self, key):
            return self.method(key)
Skylar Saveland
  • 11,116
  • 9
  • 75
  • 91
0

Another option is to define a property. See https://web.archive.org/web/20160407103752/http://adam.gomaa.us/blog/2008/aug/11/the-python-property-builtin/ .

You write your function that can do pretty much anything you want. You make it a read only property. You call the property from the template.

Et voilà !!!!

bjrne
  • 355
  • 5
  • 14
  • 14
    this is pointless... the Django template system already allows to to call methods as if they were properties (as long as the method does not take any args) – Anentropic May 23 '11 at 13:16