Flask - SubViews - python

I am looking to use a certain design pattern in my application, but I am not sure if my pattern really even fits with Flask mechanisms. I am just verifying that I have not overlooked existing solutions.
I would like to have a top-level View that renders the response of another proxied request. The thing is, I am not proxying external URLs, but rather views from within my same application (kind of like Blueprints that depend on other Blueprints). Similar to the 'render_template()' function, I am looking for something like render_view, or even better, *request_view_as_string*. I then need to process the response and re-render.
I am using template inheritance to the best of my abilities (jinja2), but much of my difficulty is coming from lots of non-template processing in between the template blocks. I am still getting a feel for jinja, and my templates are starting to feel polluted with hacks.
Edit
Basically, I misunderstood the role of jinja. My application needs to build heavier on jinja. I kept trying to get in and out of jinja as quickly as possible, and that is where my nested dependencies were starting to cause problems. Ultimately, most of the features I needed for my "subviews" was built right into Jinja, I just wasn't sure how to properly integrate them with FLask.

First off, Jinja2 supports macros, which let you share functionality between templates:
{# helpers.jinja #}
{% macro generate_select(itrbl) %}
<select{{kwargs|xmlattrs}}>
{% for item in itrbl %}
<option value="{{item.value}}">{{item.text}}</option>
{% endfor %}
</select>
{% endmacro %}
{# page1.jinja #}
{% import "helpers.jinja" as helpers %}
{{ helpers.generate_select(data, name="my_data_field") }}
For more complicated bits of functionality (A / B testing, loading different features depending on what the user's account has enabled, etc.) extends, include, and import can take variable values:
{# A custom template with a *lot* of hooks #}
{% extends base_template %}
{% import custom_functionality_provider as provider %}
{% block common_name %}
{% if features.feature_x %}
{% include feature_x_include %}
{% endif %}
{{ provider.operation() }}
{% endblock common_name %}
#app.route("/some-route")
def some_route():
# Of course, in real life you would determine these values
# on the basis of user / condition lookups, rather than
# hardcoding values in your render_template call
render_template("custom.jinja", base_template="AB/A/base.jinja",
custom_functionality_provider="macros/lowcostplan.jinja",
feature_x_include="AB/A/features/feature_x.jinja",
features=some_features_object)
Finally, you can pass callables that return strings to any Jinja template, giving you access to the full power of Python:
def custom_implimentation_a(**context_args):
return render_template("template_a.jinja", **context_args)
def custom_implimentation_b(**context_args):
return render_template("template_b.jinja", **context_args)
#app.route("/some-route")
def some_route():
if condition:
provider = custom_implimentation_a # Note, no parenthesis
else:
provider = custom_implimentation_b
return render_template("some_page.jinja", provider=provider)

I do consider Sean's first answer as the most appropriate, but I did run across this little mechanism for those times when Jinja is making Python tasks a bit difficult.
http://werkzeug.pocoo.org/docs/local/
This should be a little more productive than Flask's g variable. See also a slightly different usage (when one global 'namespace' is no longer manageable)
http://flask.pocoo.org/snippets/13/
I very often forget that Flask and Werkzeug are related projects. The huge benefit of this is that much of their functionality does not overlap with the other project.
When you are newbie approaching from the Flask side, if you feel like Flask is missing a few gears, there is a good chance its because they already included them in Werkzeug.

Related

How to detect debug mode in jinja?

Under flask, I want to include/exclude stuff in the jinja template based upon whether, or not, we are in debug mode. I'm not debating if this is a good, or bad, idea (i'd vote 'bad' but want to do it just for this case nonetheless :-), so how might this best happen?
I was hoping i would not have to pass the variable explicitly into the template, unlike this:
render_template('foo.html', debug=app.debug)
not that this would be too hard, but I'd rather just magically say in the template:
{% if debug %}
go crazzzzy
{% endif %}
Is there some default variable just lazing about waiting for me to pounce?
use context processors
To inject new variables automatically into the context of a template, context processors exist in Flask. Context processors run before the template is rendered and have the ability to inject new values into the template context. A context processor is a function that returns a dictionary. The keys and values of this dictionary are then merged with the template context, for all templates in the app:
#app.context_processor
def inject_debug():
return dict(debug=app.debug)
now debug variable accessible in templates.
When you run your flask application with app.run(debug=True), you can also just check the config object like so:
{% if config['DEBUG'] %}
<h1>My html here</h1>
{% endif %}

Queryset in view or in template

I am trying to speed up my code. In development, everything ran very smooth, but once I put it in production, and started adding more depth of data into the database, I realize that it is running very slow.
I noticed on django-toolbar that it is running THOUSANDS of queries, where it should only be maybe 10-20. I am wondering if it may be because of the way I have a lot of content being delivered.
For example, I have code that looks like this:
{% if user.profile.is_admin %}
...
{% endif %}
and
{% for stuff in user.profile.get_somestuff %}
...
{{ stuff.info }}
{{ stuff.other_info }}
...
{% endfor %}
Does each one of these execute a new query?
Should I run the query for get_somestuff in the view, pass it through context? I am asking from a performance perspective.
If profile.get_somestuff is an expensive operation and you call it multiple times in the template, and yes, you should call that in the view once and pass the result to the template via context.
def view(request):
...
stuff = request.user.profile.get_somestuff()
return render(request, 'page.html', {'stuff': stuff})
Alternatively, you can use {% with %} tag to create a scope containing the value in its own context:
{% with stuff=user.profile.get_somestuff %}
...
{{ stuff.info }}
{{ stuff.other_info }}
... do some other things with stuff
{% endwith %}
Personally, I would go with the first option because, with the help of django.db.connection.queries, it is relatively easier to monitor db queries you make in the view. Make sure you avoid sending template querysets, lazy expressions etc. as much as possible.
BTW, please note that DEBUG must be set to True for connection.queries to work.
If stuff.info or stuff.other_info are foreign keys to other models then yes, each time you hit each of those for a new stuff obj you could be doing another select query for each one.
select_related might help you here. It'll effectively join the relevant tables on the fk fields you specify in the sql query upfront. The sql query will be more complex than the queries you're running now but far less numerous.

django : get the current locale inside a templatetag

I've an internationalized django (1.4) app.
In this app, I have a templatetag which needs to know what is the current language.
I think I could use django.utils.translation.get_language but it seems that inside the templatetag, it returns the default language and not the current language.
I fixed it by reading request.LANGUAGE_CODE from context.
But I would like to know if there is a better way to get the language in a templatetag
There's a few default templatetags you can use
{% get_current_language %}
or
{% get_current_language as FOO %}
e.g.
{% render_bar request FOO %}

Django template check for empty when I have an if inside a for

I have the following code in my template:
{% for req in user.requests_made_set.all %}
{% if not req.is_published %}
{{ req }}
{% endif %}
{% empty %}
No requests
{% endfor %}
If there are some requests but none has the is_published = True then how could I output a message (like "No requests") ?? I'd only like to use Django templates and not do it in my view!
Thanks
Even if this might be possible to achieve in the template, I (and probably many other people) would advise against it. To achieve this, you basically need to find out whether there are any objects in the database matching some criteria. That is certainly not something that belongs into a template.
Templates are intended to be used to define how stuff is displayed. The task you're solving is determining what stuff to display. This definitely belongs in a view and not a template.
If you want to avoid placing it in a view just because you want the information to appear on each page, regardless of the view, consider using a context processor which would add the required information to your template context automatically, or writing a template tag that would solve this for you.

Getting Site_ID in template Django

I have build a web site for a client which has a number of applications. Now he has a new URL registered which he wants to point to the same site, but he wants the look and feel changed. That's basically he wants a new home.html and base.html for the new web site. I can easily add the new site to settings and then change the view for the home page, to display a new home2.html.
However how do I do something like this as expressed in psuedo code in base.html
{% if site_id equals 1 %}
{% include "base1.html" %}
{% endif %}
{% if site_id equals 2 %}
{% include "base2.html" %}
{% endif %}
Any ideas. There are 100s of views on the site and nearly 50 models. I cannot recreate models, and mess around. This needs to be a quick fix.
Thanks in advance
You can create a context processor to automatically add site_id to the context: http://docs.djangoproject.com/en/dev/ref/templates/api/#writing-your-own-context-processors
But I would opt for a different solution. You can simply add an extra template directory per site so Django will try the templates specifically for that site first and fall back to the normal templates if they're not available.
To extend the idea of WoLph with the context processor, I would maybe even add the switching of the template to the context processor which would clean up your templates, as otherwise you may have to repeat the if clause quite often:
from django.contrib.sites.models import Site
def base_template(request):
site = Site.objects.get_current()
template = "base%s.html" % str(site.pk)
return {'BASE_TEMPLATE': template}
And in your template: {% include BASE_TEMPLATE %}
Looks nicer to me than the switching in the templates!
Another solution would be writing a Middleware to set ´request.site´ the current site id.

Categories

Resources