How to make a dynamic menu in base template using django - django

I'm about to start a new project and I think this time django is the way to go. I've been reading the documentation for the past two weeks and it looks promissing.
Ok, the thing is that I could not find anything about (in C# MVC called) Partial Rendering. For example if I want a dynamic menu where the menu-items comes from the database, then I would expect that the base template (or master page) renders the menu on each request (the partial renderer invokes another action or renders a template with some session data). So, the menu comes for free as long as my template inherits from this base template.
Honestly, I have no clue on how to achieve this.
What I would like is some code in the base template that uses data that is not contained in the child template. I don't want to include an extra variable (maybe 'menu_list_items') every time I call render_to_response('child_content.html',context). Is this possible?
Thanks!

You could either use a context processor, or a custom template tag to provide this functionality.
A context_processor is a simple function which can add objects to every RequestContext. A custom template tag can have its own template snippet and context which could render the menu for you.

For the template reusing: you should just create a base template for the generic layout, and use detailed templates for the individual pages. This is already covered in detail by the Django documentation.
What I tend to do for those generic parts (say for example, a menu highlighting the current part of the site the use is on), is to create my own render_to_response functions, akin to the following:
from django.shortcuts import render_to_response as django_render_to_response
def render_to_response(template, params, context_instance):
return django_render_to_response(template,
AppendStandardParams(request, params),
context_instance)
The ApplyStandardParams method then configures the menu based on the current path:
def AppendStandardParams(request, params):
if request.META['PATH_INFO'].startswith('/customer'):
params['category'] = 'customer'
params['title'] = 'Customer overview'
# and so on for all different parts
These category and title tags in this example are some values used to highlight the menu, configure titles, and so on. For example:
<!-- Customer menu entry: change class if this is the current category. -->
<li{% if category == "customer" %} class="selected"{% endif %}>Customers</li>
Finally, to use it in a view, instead of the normal render_to_response import, I just do something like from lib.views import *, which makes my custom version available in the view. This way the syntax of all code in the views stays the same, but I don't have to customize the menu every time I create a new view or app.

Related

Read variables from template in Django or Jinja2

Looking for a solution to read the complete list of variables that can be passed into a context and displayed within a template, using either Django or Jinja2.
For example, something like this in Django:
from django.template import Template
template = Template("<h1>{{title}}</h1>{%for v in values%}<p>{{v}}</p>{%endfor%}")
for node in template.nodelist:
#Get the variable name from the node
This seems like a real pain. All I want is a list of the keys that a context object could pass to the template, in this example, "title" and "values"
You could use {% debug %} tag
debug
Outputs a whole load of debugging information, including the current
context and imported modules.
Also if you want even more you could use Django debug toolbar

Conditional tags in templates

I'm using Django on top of an existing database/user framework so unfortunately it is not possible to me for use the django auth framework.
I have my custom auth library built, now I just need to figure out how to use it in templates. Certain links should only appear for users who have access to it.
In PHP I could do soemthing like this
<?php if auth('RestrictedLinkName') {?> <?php } ?>
What is the Django way? I have been playing with custom tags but haven't been able to get it working yet. I was thinking something like:
{% if check_permission('Restrictedarea') %} hjkfgdkhfg {% endif %}
If you need to evaluate check_permission('Restrictedarea') then do it in the view function and pass the evaluated result in the template.
The djangobook says (in Chapter 4: Templates, in Philosophies and Limitations section) -
Business logic should be separated from presentation logic. Django’s
developers see a template system as a tool that controls presentation
and presentation-related logic – and that’s it. The template system
shouldn’t support functionality that goes beyond this basic goal.
For that reason, it’s impossible to call Python code directly within
Django templates. All “programming” is fundamentally limited to the
scope of what template tags can do. It is possible to write custom
template tags that do arbitrary things, but the out-of-the-box Django
template tags intentionally do not allow for arbitrary Python code
execution.
And if need to write custom tags check these links:
Writing a Template Tag in Django
Custom template tags and filters
You can write your custom template tags and filters. But this may help you up to a point, for further, you must write your custom context_processors (and custom middlewares if needed.)
But doing authentication and permission check through custom function is quite hard, but possible. I have a system fully runs on custom authentication/authorization.
First of all, you can examine django context_processors to understand how they work. then you can write your custom context_processor. After you add your context processor in your settings, you can use those methods, and do your authentication/authorization as you wish.
One of my custom contect_processor function is like:
from django.utils.functional import lazy
def CustomProcessor(request):
cust_perms = {
'admin_perm_check': lazy(lambda: myCustomPermChecker(request), myCustomPermChecker)(),
'system_admin': aFunctionToReturnBoolValue(),
}
return custom_perms
class myCustomPermChecker(object):
def __init__(self, request):
self.request = request
def __getitem__(self, perm_name):
return True if (perm_name in user_perm_list()) else False
In your template
{%if admin_perm_check.perm_name%}...{%endif%}
{%if system_admin %} this is a bool check{%endif%}
You must define your permchecker as a class, which have __getitem__ method so, your template tag admin_perm_check.perm_name could work. This perm checker accepts only one additional paramater (perm_name), If you want to use django-style two parameter check then you must do:
class myCustomPermChecker(object):
def __init__(self):
pass
def __getitem__(self, module_name):
return SecondPermCheckerStep(module_name)
class SecondPermCheckerStep(object):
def __init__(self, module_name):
self.module_name = module_name
def __getitem__(self, perm_name)
return True if ('%s.%s' % (self.module_name,perm_name) in user_perm_list()) else False
{%if admin_perm_check.module_name.perm_name%}
You can use another class.__getitem__ to add one more key to tour tag chain in your template etc.
Since your context_processor CustomProcessor(request): accept http.request object as a parameter, you can pass your methods or functions any value (session id or user id etc.) that will be required in authenticaon or authorization. You can write a middleware to set custom values to your request object to use (like django set user instance and let you use request.user in your views.). You can also set your custom user model instance so you can use it in you template (if you set it in your request_context) and in your views (if you set it in your middleware)
Reading docs could not help here much, better is checking django code to see how django handle this.

Django's equivalence of ASP.NET UserControl

If anyone here is ASP.NET pro, you might know what I mean by user control. I wish to create a similar one in django instead.
So, my problem is that I have several pages in my website, but I need a search bar to appear in every pages. Since I require the views.py to operate this search bar, I cannot do a simple method of
{% include 'something.html' %}
Therefore, can anyone suggest how can I do it?
There are a couple of ways to accomplish what you're wanting to do:
Context Processors
Template Tags
Context Processors can augment the template context with values, regardless of which template is loaded. They are akin to filters in Rails.
Template Tags, like Context Processors, can accomplish anything you can do in Python, but are implemented at the template level.
If you need something to be present on every template, one of the simplest ways to accomplish this is with an inclusion tag, which can also accept values passed to it. An inclusion tag could be implemented at your highest level template, a.k.a your MasterPage, and as long as you don't put it in a block and override it, it would appear on every page that includes that template in its inheritance chain.
If it's just something you want to include on every page, and it doesn't need to do any processing, you should just be able to place the code you want in the top-most template and have subsequent templates inherit that.
I typically have a "base.html" template that all of my templates inherit from. If I need something to be in every page, I put it there. If it's something I want there by default, but want to be able to augment it in subsequent templates, I will place it into a block. That block will let you include or override its default content.
I know this post is kind of old but I just came across it and found a kind-of-solution that works. I call it kind-of-solution because it is a workaround.
I have a few different sites on which I want to display logging information. This display always looks the same (it has the same html) and has the same database table and model class behind it.
My solution/workaround uses the django filters:
in views.py I put the list of log-entries in the context
context = {'list_log': Log.objects.filter(condition = True) }
template = loader.get_template('my_html_file.html')
return HttpResponse(template.render(context, request))
in my_html_file.html I use a custom filter
{{ list_log|get_log_uc|safe }}
in the filters.py I load another html file with this custom filter
#register.filter
def get_log_uc(list_log):
template = loader.get_template('user_control_log.html')
context = { 'list_log' : log }
return template.render(context)
in user_control_log.html I have the user control equivalent html
{% for log in list_log %}
<p>log.something</p>
{% endfor %

Putting links in list_detail.object_list to list_detail.object_detail

I've started using Django and am going right to generic views. Great architecture! Well, the documents are great, but for the absolute beginner it is a bit like unix docs, where they make the most sense when you already know what you're doing. I've looked about and cannot find this specifically, which is, how do you set up an object_list template so that you can click on an entry in the rendered screen and get the object_detail?
The following is working. The reason I'm asking is to see if I am taking a reasonable route or is there some better, more Djangoish way to do this?
I've got a model which has a unicode defined so that I can identify my database entries in a human readable form. I want to click on a link in the object_list generated page to get to the object_detail page. I understand that a good way to do this is to create a system where the url for the detail looks like http://www.example.com/xxx/5/ which would call up the detail page for row 5 in the database. So, I just came up with the following, and my question is am I on the right track?
I made a template page for the list view that contains the following:
<ul>
{% for aninpatient in object_list %}
<li><a href='/inpatient-detail/{{ aninpatient.id }}/'>{{ aninpatient }}</a></li>
{% endfor %}
</ul>
Here, object_list comes from the list_detail.object_list generic view. The for loop steps through the object list object_list. In each line I create an anchor in html that references the desired href, "/inpatient-detail/nn/", where nn is the id field of each of the rows in the database table. The displayed link is the unicode string which is therefore a clickable link. I've set up templates and this works just fine.
So, am I going in the right direction? It looks like it will be straightforward to extend this to be able to put edit and delete links in the template as well.
Is there a generic view that takes advantage of the model to create the detail page? I used ModelForm helper from django.forms to make the form object, which was great for creating the input form (with automatic validation! wow that was cool!), so is there something like that for creating the detail view page?
Steve
If you're on django < 1.3 then what you are doing is basically perfect. Those generic views are quite good for quickly creating pages. If you're on django 1.3 you'll want to use the class based generic views. Once you get a handle on those they are are crazy good.
Only note I have is that you should use {% url %} tags in your templates instead of hardcoding urls. In your urls.conf file(s) define named urls like:
url('inpatient-detail/(?P<inpatient_id>\d+)/$', 'your_view', name='inpatient_detail')
and in your template (for django < 1.3):
...
In 1.3 a new url tag is available that improves life even more.

How to make a Template extend another one?

I've got some templates which I'm building from some data I've got stored in my DB:
my_template = Template(string_data)
Now I want that template to extend another one (also stored as text in the DB). How can I do that? Looking for something like:
my_template.base = other_template
Or whatever the syntax is. Can't find documentation on this.
I see template.nodelist in the source; can I maybe prepend some kind of extends node?
Added a bounty... will grant to first person who can tell me how to do this without hacking the core of django.
No need to hack the core of Django. Creating your own template loader, as Ignacio recommends, is pretty simple. You just need to subclass django.template.loader.BaseLoader, and implement the load_template_source method. This just takes a string for the template name, which you can simply use to look up the item in the database and return the string.
In your case, since you've got two template elements in the the single db row, you might want to do something to specify it in the extends tag:
{% extends "myinstance.html_version" %}
and simply split that in load_template_source:
def load_template_source(self, template_name, template_dirs=None):
name, field = template_name.split('.')
tpl = TemplateModel.objects.get(name=name)
return getattr(tpl, field)
Of course you'd want to put some error handling in there, but you get the idea. You just then specify this template loader in your settings TEMPLATE_LOADERS tuple.
Edit after comment I still don't really understand why, but if all you want to do is to choose the template to extend from dynamically, why not just insert that in the text template before processing?
tpl_string = get_template_from_wherever()
extender = '{% extends "%s" %}' % template_name_to_extend
tpl = Template(extender + tpl_string)
Still a hack, but probably much less of one than mucking about with the internals of template inheritance.
To restate your problem, you want to:
Take text that you've already got in a string (because you loaded it from a database),
Take more text that you're going to load into a string (because you know where to load it from),
Make a parent template out of the second string,
Make a child template out of the first string,
Set the parent of the child template (from the first string) to be the parent template (from the second string),
Evaluate the child template.
Most importantly, you want to avoid having to have anything like {% extends ...%} in the child template, because ... why?
And finally, you want to do this without hacking the core of Django.
Short answer:
Not possible. Out of the box, Django won't do what you're asking.
Long answer:
The entire concept of template inheritance in Django is implemented through the extends tag. More accurately, it's implemented through the ExtendsNode class of the django.template.loader_tags module, which is created when the extends tag is parsed. When you create a Template(), it parses its source string, and creates a list of Nodes (stored in the template's nodelist as you noted earlier). At a later date, you can render the template using whatever context you like, as many times as you like.
Roughly, rendering works by calling render() on each node in the nodelist. If the first node in a template's nodelist is an ExtendsNode (and it must be the first node), then template inheritance magic happens. When the ExtendsNode is created, it is given the template's nodelist, and a parent name (either as a string (parent_name) or expression (parent_name_expr). When the ExtendsNode is rendered, it will use its get_parent() method to call get_template(parent_name), which will invoke the template loader mechanism to load the parent template. Once it has the parent template, the ExtendsNode::render() method will work the magic of template inheritance.
Feel free to check out the code yourself.
In order to avoid using a template loader, you would have to do the following:
Create a class SpecialExtendsNode(ExtendsNode), overriding the __init__ method and the get_parent method.
After creating a template from the child string, create an instance of your subclass, initialized from the parent template.
Insert your instance of SpecialExtendsNode into the head of the child template's nodelist.
Pray that none of this is ever changed by the Django developers.
Just to make things easy for you, here's your class:
class SpecialExtendsNode(ExtendsNode):
def __init__( self, nodelist, parent, name ):
self.myparent = parent
ExtendsNode.__init__( self, nodelist, name, None, None )
def get_parent( self ):
return self.myparent
And the code to use it will look like this:
parent = Template( parent_string )
child = Template( child_string )
hack = SpecialExtendsNode( child.nodelist, parent, "parent name" )
child.nodelist.insert( 0, hack )
output = child.render( context )
Now that I've spent the time and effort to give you a gun, and load it with bullets, I'm going to try to convince you to not to pull the trigger, but instead to do things the way everyone else has recommended.
The code I've written here has no error checking, and was written against Django 1.2. While I haven't tested it, I'm 99% sure that it will work on Django 1.2. I have no idea if it will work on any other version of Django. On the other hand, aside from providing the source, the Django developers haven't documented the internals of their template processor, other than to provide documented interfaces for writing new template tags and filters, and a documented interface for writing template loaders (specifically mentioning the use case of loading a template from a database). That means that there is a reasonable case that someday the Django developers may choose to rewrite or heavily modify the template extension mechanism. If they do this, I will 100% guarantee that this code will break. Why? Because it's a hack. This is what hacking the core of Django looks like. It will work for a day, or a week, or even months at a time, but sooner or later, you (or the programmer that comes after you) will upgrade from Django 1.2 to a later version, and then it will break. And when it breaks, I won't be there to help you, and the Django developers will all ask, why didn't you just write a template loader?
Now tell me, which involves more hacking the core of Django -- writing a template loader, which is documented and supported by the developers, or what I just described?
Unfortunately you're going to need to write your own template loader to read the database. The {% extends %} template tag is usually used for this, but it defers the task of actually loading the templates to the loaders.
I think you'll need to subclass django.template.loader_tags.ExtendNode and override its get_parent method, so that you can use your own get_template in there! Then you should be able to add the ExtendNode to your template.nodelist, but also be aware that it has to be the first!
from app.models.misc import EmailTemplate
from django.template import TemplateDoesNotExist
from django.template.loader import BaseLoader
class Loader(BaseLoader):
is_usable = True
def load_template_source(self, template_name, template_dirs=None):
try:
key, ext = template_name.split('.')
attr = {
'html': 'html_content',
'txt': 'text_content',
}[ext]
tpl = EmailTemplate.objects.get(key=key)
return (getattr(tpl, attr), repr(tpl))
except:
raise TemplateDoesNotExist
Really hard to write when you can't find updated documentation :\