Where to put a custom Jinja2 filter?

Showing 1-16 of 16 messages
Where to put a custom Jinja2 filter? Niklas R 11/8/11 1:16 AM
Thank you for the upgrade to SDK 1.6 that included many substantial improvements.

Now I add a Jinja2 filter to format my time and the best would be filters like django's timesince that automatically outputs the language of the i18n selected language, but first to make a quick solution I'd like to format my date just using numbers. The suggested solution from the Jinja2 manual is:

def datetimeformat(value, format='%H:%M / %d-%m-%Y'):
    return value.strftime(format)

jinja_environment.filters['datetimeformat'] = datetimeformat

However adding this code to my file doesn't make the filter available in the template:

{{ ad.modified|datetimeformat }}
TemplateAssertionError: no filter named 'datetimeformat'

If I add the code to the Jinja2 library's filters.py then it works. I shouldn't need to add to Jinja2 files manually. It should work just adding the Jinja2 to my app.yaml and put my filter in my code instead of in the Jinja2 code. Where should I put the filter code?

Best regards,
Niklas
Re: Where to put a custom Jinja2 filter? Ovidiu 11/8/11 1:39 AM
Hi Niklas,

Look at http://code.google.com/p/google-app-engine-samples/source/browse/trunk/python27/guestbook/guestbook.py

You can put that code below the jinja_environment definition and it should work.
Make sure you are using the same environment instance when you retrieve the templates.

Best,
Ovidiu
Re: Where to put a custom Jinja2 filter? Niklas R 11/8/11 9:25 PM
Thank you Ovidui. I'll try take the guestbook code to make a bare-bones example since my attempt is not working even though it looks right and if I put this in the Jinja2 file `filters.py`then it works:

from django.utils import translation
from django.utils.translation import gettext, ngettext, ugettext, ungettext, get_language, activate
from jinja2 import Environment, FileSystemLoader

class DjangoTranslator(object):
    def __init__(self):
        self.gettext = gettext
        self.ngettext = ngettext
        self.ugettext = ugettext
        self.ungettext = ungettext

class DjangoEnvironment(jinja2.Environment):
    def get_translator(self, context):
        return DjangoTranslator()

jinja_environment = DjangoEnvironment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), extensions=['jinja2.ext.i18n'])
jinja_environment.install_gettext_translations(translation)
Re: Where to put a custom Jinja2 filter? voscausa 11/9/11 2:18 AM
I had some problems with jinja filters and jinja includes. That did non work. 
Now I use template inheritance. Here is my code for initializing the environment. I use a global env.
And in the mainline I use : 
my_env = myEnv.reg_cms_function()  # init or get the global env and register the jinja functions

class myEnv:                                                # jinja environment global
    
    _my_env = None
    _flush_env = 'NO'
        
    @staticmethod
    def get(flush='NO'):
        if  myEnv._my_env == None or flush == 'YES' or myEnv._flush_env == 'YES' :
            if flush == 'YES' :
                myEnv._my_env = Environment(auto_reload=True, cache_size=0, line_statement_prefix='#', line_comment_prefix='##')
                logging.debug('create jinja environment, flush : %s' %(flush))
                myEnv._flush_env = 'YES'
            else :
                myEnv._my_env = Environment(auto_reload=False, cache_size=50, line_statement_prefix='#', line_comment_prefix='##')
                logging.debug('create jinja environment, flush : %s' %(flush))
                myEnv._flush_env = 'NO'
            
            myEnv._my_env.loader = ChoiceLoader([
                               FileSystemLoader(os.path.join(os.path.dirname(__file__), 'templates', 'NL-nld')),
                               FunctionLoader(load_Template)
                               ])
        return myEnv._my_env

    @staticmethod
    def reg_cms_functions(flush='NO'):

        env = myEnv.get(flush)
        env.globals.update(xlink=ExternalLink)
        env.globals.update(ilink=ImageLink)
        env.globals.update(classdiv=ClassDiv)
        env.filters['euros'] = Euros
        return env
Re: Where to put a custom Jinja2 filter? Niklas R 11/10/11 2:36 PM


On Wednesday, November 9, 2011 10:18:28 AM UTC, voscausa wrote:
I had some problems with jinja filters and jinja includes. That did non work. 

Exactly my experience too! But Jinja seems very good combined with GAE so I do include it and I've upgraded to python 2.7 and I also want to use WTForms but I can't get WTForms to work. Thank you very much for sharing your code, I'm a beginner / intermediate at python so I'm going to need some time to try the code you sent here.

Regards,
Niklas
 
Re: Where to put a custom Jinja2 filter? solsTiCe d'Hiver 11/14/11 12:47 AM
You can find bits and pieces of documentation here and there, but a real example or documentation is needed.

Reference:
jinja2 factory to use custom filter
jinja2 from webapp2_extras
how to configure a custom template path for webapp2

Here is the missing example:

import webapp2
import re
from webapp2_extras import jinja2

def datetimeformat(value, format='H:M d/m/Y'):
    return value.strftime(re.sub('([aAbBcdfHIjmMpSUwWxXyYzZ%])', '%\\1', format))

def jinja2_factory(app):
    j = jinja2.Jinja2(app)
    j.environment.filters.update({
        'datetimeformat': datetimeformat,
        })
    return j

class RequestWrapper(webapp2.RequestHandler):
    @webapp2.cached_property
    def jinja2(self):
        return jinja2.get_jinja2(app=self.app, factory=jinja2_factory)

    def render_template(self, template_name, template_values):
        self.response.write(self.jinja2.render_template(template_name, **template_values))

class MainPage(RequestWrapper):
    def get(self):
         # your code here
         self.response.write(self.render_template('yourtemplate.html', {'value':'It works'})

curr_path = os.path.abspath(os.path.dirname(__file__))
config = {}
config['webapp2_extras.jinja2'] = {'template_path': os.path.join(curr_path, 'your', 'custom', 'path')}
app = webapp2.WSGIApplication(
        [ ('/index.html', MainPage), ],
   debug=DEBUG,
   config=config)

it's way more complicated than webapp and django template. Especially because jinja2 lacks many django filters.
I am still looking for a good way to do this for every script I have. I need to redefine all the custom filter in every script ???

Re: Where to put a custom Jinja2 filter? Kyle Finley 11/14/11 10:33 AM
solsTiCe d'Hiver,

You can also add filters through the config directly, if that helps:

import my_cust_filters

config = {
    'webapp2_extras.jinja2': {
        'template_path': os.path.join(curr_path, 'your', 'custom', 'path'),
        'filters': {
            'datetimeformat': datetimeformat,
            'custfilter1': my_cust_filters.custfilter1,
            'custfilter2': my_cust_filters.custfilter2,
        },
    },
}

application = webapp2.WSGIApplication(routes, debug=DEBUG, config=config)

- Kyle
Re: Where to put a custom Jinja2 filter? solsTiCe d'Hiver 11/15/11 3:43 AM
oh good ! thank you
Re: Where to put a custom Jinja2 filter? solsTiCe d'Hiver 11/15/11 6:02 AM
say, I want to use an "inline" Template to respond to a rpc/ajax query
so I previously use:
template.Template('{{ somedatetime|timesince }}', template.Context({'somedatetime', datetimevalue}))

I can now use jinja2.jinja2.Template with jinja2 imported from webapp2_extras but I don't get my custom filter defined in self.jinja2 or webapp2 app

and self.jinja2 does not have a Template class.

So ?


Re: Where to put a custom Jinja2 filter? Kyle Finley 11/15/11 10:44 AM
I'm not completely sure about "inline" templates. Maybe something like this would work:

class RequestWrapper(webapp2.RequestHandler):
    @webapp2.cached_property
    def jinja2(self):
        return jinja2.get_jinja2(app=self.app, factory=jinja2_factory)

    def render_string(self, template_string, template_values):
        self.response.write(self.jinja2.environment.from_string(template_string).render(**template_values))
        
class MainPage(RequestWrapper):
    def get(self):
         # your code here
         self.render_string('The date was {{ somedatetime|timesince }}', {'somedatetime': datetimevalue})


Here's the section of the source where get_template is called:

It seems like you should be able to call from_string similarly, but I haven't tested it.



Re: Where to put a custom Jinja2 filter? solsTiCe d'Hiver 11/15/11 12:38 PM
thank you
I shouls have found that. I was looking at environment and did not see the from_string method /o\
Re: Where to put a custom Jinja2 filter? Niklas R 11/16/11 11:56 AM
Thank you for the interest in this topic! I really enjoyed reading through the answers that quickly went above my head.

It seems to me that I either must add boilerplate code or put my code in the Jinja2 filters.py and neither is a good way.

If we must add boilerplate code I think I should file an issue about it in the issue tracker that the boilerplate is requested to be added to the SDK so that this is solved once and for all. Do you agree or do you consider this more of Jinja2 issue than a GAE issue?

Thank you
Nick Rosencrantz
Re: [appengine-python] Re: Where to put a custom Jinja2 filter? Kyle Finley 11/16/11 2:47 PM
Nick,

Once you get everything set up, I don't think that there's all that much boilerplate. The set up process can be a little confusing, however. Here's a sample application to get you started:


With this setup you would put your custom filters in filters.py. You then explicitly list the filters that you would like your templates to have access to. In the example our config is in main.py

import filters
config = {
    'webapp2_extras.jinja2': {
        'filters': {
            'timesince': filters.timesince,
            'datetimeformat': filters.datetimeformat,
        },
    },
}

We then use a BaseHandler to setup the bollerplate code for us. As long as our handlers extend the BaseHander all of the filters will be available to the template.

Here's an example that extends the BaseHander to render the pages_test_filter.html file:

Does that help clarify things?




--
You received this message because you are subscribed to the Google Groups "google-appengine-python" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-appengine-python/-/QmTWnWZsN7gJ.

To post to this group, send email to google-appe...@googlegroups.com.
To unsubscribe from this group, send email to google-appengine-python+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-appengine-python?hl=en.

Re: [appengine-python] Re: Where to put a custom Jinja2 filter? Niklas R 11/17/11 5:28 AM
Thank you Kyle for the code! I'm upgrading my ubuntu now so I didn't
have time to try the github repository yet. I sure will a.s.a.p. I'm
glad we can have a common solution how to include our custom filters
and maybe I'm asking for too much when I want an easy way to use the
django filters such as timesince etc in localized languages that used
to work and now I had to step back to making my own filters instead of
using django's.
Thank you!
Re: [appengine-python] Re: Where to put a custom Jinja2 filter? Kyle Finley 11/17/11 10:54 AM
Nick, 

I agree it would be great to have a solution that made the transition easier. There's a project called coffin [ https://github.com/coffin/coffin ] that brings a lot of the feature of django templates to jinja2. It's designed to work with django, so django is very much integrated, but maybe there are elements that will help with your transition.

For timesince, localized, the tipfy wiki has a nice example:
You would just need to replace:
from tipfy.ext import i18n -> from webapp2_extras import i18n
and include babel [ http://babel.edgewall.org/ ].

I think once you start using jinja2 you'll really enjoy it. You'll find that some of the things that would have require a filter in django can now be done very easily. If you haven't already looked at the jinja2 site. Here's the list of built-in filters:

- Kyle



Re: [appengine-python] Re: Where to put a custom Jinja2 filter? Niklas R 11/17/11 1:25 PM
Thank you for all the info and code. webapp2_extras.i18n seems very interesting for me since my largest project makes extensive use of translations from django.po and .mo files that I read Babel also can use so I can keep my current translation format (gettext) I'm very glad succeeding to make the transition since I was worried that i18n might not work.
I could run the scotch-base examples which were very good and I'm looking at including an i18n version of timesince for completeness and following my original spec
that articles from today and yesterday should be labelled "today" and "yesterday" instead of date and in localized language so for that mini use-case a timesince filter that
works with i18n will be perfect. Here is my current i18b request handler that could enable Jinja2's {% trans %} tag which made me very glad that the translations could be manipulated to work without me rewriting all our django.po and django.mo that we have for 12 different languages. How I load the translations with the django translations that I might switch to, like you say, using babel instead of django's translations:

from django.utils import translation

class I18NHandler(webapp2.RequestHandler):

   def render_template(self, file, template_args):
       path = os.path.join(os.path.dirname(__file__), 'templates',
                           file)
       self.response.out.write(template.render(path, template_args))

   def initialize(self, request, response):
       webapp2.RequestHandler.initialize(self, request, response)
       self.request.COOKIES = Cookies(self)
       self.request.META = os.environ
       self.reset_language()

   def reset_language(self):

       # Decide the language from Cookies/Headers

       language = translation.get_language_from_request(self.request)
       translation.activate(language)
       self.request.LANGUAGE_CODE = translation.get_language()

       # Set headers in response
       self.response.headers['Content-Language'] = str(translation.get_language())

And using the above base class with Jinja2 becomes with my initial attempt the following:

class DjangoTranslator(object):
   def __init__(self):
       self.gettext = translation.gettext
       self.ngettext = translation.ngettext
       self.ugettext = translation.ugettext
       self.ungettext = translation.ungettext


class DjangoEnvironment(jinja2.Environment):
   def get_translator(self, context):
       return DjangoTranslator()

jinja_environment = DjangoEnvironment(
   loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), extensions=['jinja2.ext.i18n'])
jinja_environment.install_gettext_translations(translation)

Thanks again for the info, thanks for the code and for the links. /Nick Rosencrantz