Include view in base template - django

I'm making a simple blog app and i want to have blog categories in the navbar which is in every page of the website.
do i have to pass a categories = Category.objects.all() object to EVERY view or is there any simple way to achieve this?

Yes, look at context processors:
myapp/context_processors.py
def categories(request):
return {'categories': Category.objects.all()}
And then add this to the TEMPLATE_CONTEXT_PROCESSORS setting:
TEMPLATE_CONTEXT_PROCESSORS = (
...
"myapp.context_processors.categories",
)

There is simpler solution, you can use template context processor. Simply create in your app file named context_processors (name of that file doesn't matter, but good practice is to name it that way) and inside that file create any function that will take request argument and return dictionary with additional context variables. Example:
from .models import Category
def categories(request):
categories = Category.objects.all()
return {
'categories': categories,
}
Now simply add your context processor into settings:
TEMPLATE_CONTEXT_PROCESSORS += (
'yourapp.context_processors.categories',
)

Related

Render a Django CMS Placeholder to variable in view

I try to render a Django CMS Placeholder from a page to a variable, to return the rendered code as JSON.
So what I do is:
from cms.models.placeholdermodel import Placeholder
from cms.models.pagemodel import Page
def render(self, page_id, placeholder_slot, request):
page = Page.objects.get(id=page_id)
placeholder = page.placeholders.get(slot=placeholder_slot)
Now I want to render the placeholder to a variable. Which function do I have to call in which way to get this?
The Placeholder can be called manually and rendered like that, a few examples:
from cms import models
from django import template
def render(request):
placeholder = models.Placeholder.objects.get_or_create(slot='some_slot')
context = template.RequestContext(request)
return placeholder.render(request, width=None)
Or for json/javascript/etc. where you don't want full html but just the value:
def render_basic(request):
placeholder = models.Placeholder.objects.get_or_create(slot='some_slot')
context = template.RequestContext(request)
return placeholder.render(request, width=None, editable=False)
It's also possible (and even easier) to use the StaticPlaceholder:
from cms import models
def render(request):
placeholder = models.StaticPlaceholder.objects.get_or_create(name='name of the placeholder')
return placeholder.code
You can render it using Placeholder.render. Note that the context must contain a valid HttpRequest object under the 'request' key. width may be None. See the {% render_placeholder %} implementation for an example.

using two templates from one view

I am trying to present content from a view in two ways: html and csv download. The only way I was able to do it was to use 2 different views, one for html presentation and one for csv. This duplicates my code and I am looking for a more elegant solution.
Any suggestions?
Here is the sample code:
# views.py
[...]
def member_list(request):
member_list = Member.objects.all()
return render_to_response("member_list.html",
{'member_list':member_list)
def member_csv_list(request):
member_list = Member.objects.all()
csv_list = HttpResponse(content_type='text/csv')
csv_list['Content-Disposition'] = 'attachment; filename="member_list.csv"'
writer = csv.writer(csv_list)
writer.writerow(['Name', 'Member Type', 'Rooms'])
for member in member_list:
fields = [member.name, member.member_type, member.room]
writer.writerow(fields)
return member_list
You can use a parameter in your url and implement a view like
def myview(request) :
type = request.GET.get('type', 'html')
# do processing
if type == 'html':
# return html
else if type == 'csv':
# return csv
If you access a url like http://yourserver/myview?type=csv it will render the csv part of the view. When the url http://yourserver/myview is accessed it will return the html part of the view.
Rohan's answer is absolutely the right paradigm. For an excellent tutorial-style introduction to this topic, cf. Multiple Templates in Django.
Here are a few quotes (all credit goes to Scott Newman).
To serve a printable version of an article, for example, we can add ?printable to the end of the URL.
To make it work, we'll add an extra step in our view to check the URL for this variable. If it exists, we'll load up a printer-friendly template file. If it doesn't exist, we'll load the normal template file.
def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)
if request.GET.has_key('printable'):
template_file = 'press/detail_printable.html'
else:
template_file = 'press/detail.html'
t = loader.get_template(template_file)
c = Context({'press': p})
return HttpResponse(t.render(c))
He continues with template overrides and different templates by domain names. All this is excellent.

Django - Access Context Dictionary Before Template

I'm hoping to use a context processor or middleware to modify the values of the dictionary passed to render_to_response prior to the actual rendering. I have a messaging schema I'm trying to implement that would populate a message list based on the existence of a type of user that I'd like to search the context for prior to the rendering of the template.
Example:
def myview(...):
...
return render_to_response('template.html',
{'variable': variable},
)
and I'd like to be able to add additional information to the context on the existence of 'variable'.
How can I access 'variable' after my view defines it but before it gets to the template so that I can further modify the context?
use TemplateResponse:
from django.template.response import TemplateResponse
def myview(...):
...
return TemplateResponse(request, 'template.html',
{'variable': variable},
)
def my_view_wrapper(...):
response = my_view(...)
variable = response.context_data['variable']
if variable == 'foo':
response.context_data['variable_is_foo'] = True
return response
This is easy. If you have supplied just a little bit more code in your example the answer might have bit you.
# first build your context, including all of the context_processors in your settings.py
context = RequestContext(request, <some dict values>)
# do something with your Context here
return render_to_response('template.html', context)
Update to comment:
The result of a render_to_response() is an HTTPResponse object containing a template rendered against a Context. That object does not (to my knowledge) have a context associated with it. I suppose you could save the result of render_to_response() in a variable and then access the Context you passed it, but I'm not sure what problem you are trying to solve.
Did you modify the Context during rendering? If so you may find that the information is not there any longer because the Context has a scope stack which is pushed/popped during template processing.
You can create a dictonary for the context:
def myview(...):
c = dict()
c["variable"] = value
...
do some stuff
...
return render_to_response('template.html',c)
Maybe the RequestContext is the thing you are looking for.

How to write a request filter / preprocessor in Django

I am writing an application in Django, which uses [year]/[month]/[title-text] in the url to identitfy news items. To manage the items I have defined a number of urls, each starting with the above prefix.
urlpatterns = patterns('msite.views',
(r'^(?P<year>[\d]{4})/(?P<month>[\d]{1,2})/(?P<slug>[\w]+)/edit/$', 'edit'),
(r'^(?P<year>[\d]{4})/(?P<month>[\d]{1,2})/(?P<slug>[\w]+)/$', 'show'),
(r'^(?P<year>[\d]{4})/(?P<month>[\d]{1,2})/(?P<slug>[\w]+)/save$', 'save'),
)
I was wondering, if there is a mechanism in Django, which allows me to preprocess a given request to the views edit, show and save. It could parse the parameters e.g. year=2010, month=11, slug='this-is-a-title' and extract a model object out of them.
The benefit would be, that I could define my views as
def show(news_item):
'''does some stuff with the news item, doesn't have to care
about how to extract the item from request data'''
...
instead of
def show(year, month, slug):
'''extract the model instance manually inside this method'''
...
What is the Django way of solving this?
Or in a more generic way, is there some mechanism to implement request filters / preprocessors such as in JavaEE and Ruby on Rails?
You need date based generic views and create/update/delete generic views maybe?
One way of doing this is to write a custom decorator. I tested this in one of my projects and it worked.
First, a custom decorator. This one will have to accept other arguments beside the function, so we declare another decorator to make it so.
decorator_with_arguments = lambda decorator: lambda * args, **kwargs: lambda func: decorator(func, *args, **kwargs)
Now the actual decorator:
#decorator_with_arguments
def parse_args_and_create_instance(function, klass, attr_names):
def _function(request, *args, **kwargs):
model_attributes_and_values = dict()
for name in attr_names:
value = kwargs.get(name, None)
if value: model_attributes_and_values[name] = value
model_instance = klass.objects.get(**model_attributes_and_values)
return function(model_instance)
return _function
This decorator expects two additional arguments besides the function it is decorating. These are respectively the model class for which the instance is to be prepared and injected and the names of the attributes to be used to prepare the instance. In this case the decorator uses the attributes to get the instance from the database.
And now, a "generic" view making use of a show function.
def show(model_instance):
return HttpResponse(model_instance.some_attribute)
show_order = parse_args_and_create_instance(Order, ['order_id'])(show)
And another:
show_customer = parse_args_and_create_instance(Customer, ['id'])(show)
In order for this to work the URL configuration parameters must contain the same key words as the attributes. Of course you can customize this by tweaking the decorator.
# urls.py
...
url(r'^order/(?P<order_id>\d+)/$', 'show_order', {}, name = 'show_order'),
url(r'^customer/(?P<id>\d+)/$', 'show_customer', {}, name = 'show_customer'),
...
Update
As #rebus correctly pointed out you also need to investigate Django's generic views.
Django is python after all, so you can easily do this:
def get_item(*args, **kwargs):
year = kwargs['year']
month = kwargs['month']
slug = kwargs['slug']
# return item based on year, month, slug...
def show(request, *args, **kwargs):
item = get_item(request, *args, **kwargs)
# rest of your logic using item
# return HttpResponse...

Django inclusion tag with configurable template

I've created an inclusion tag, however I'd like to be able to make the template optionally configurable. There doesn't seem to be support for this out of the box, so I'd like to see how people did this - maybe a method search the templates directory first for a specific template name and then falling back to the default template.
#register.inclusion_tag('foo.html', takes_context=True)
I use simple_tag when i need to do that:
from django.template import Library, loader, Context
#register.simple_tag(takes_context=True)
def my_tag(context, template_name):
var1 = ...
t = loader.get_template(template_name)
return t.render(Context({
'var1': var1,
...
}))
This post saved my life: http://djangosnippets.org/snippets/1329/
The key is to add to a "dummy template":
{% extends template %}
The inclusion_tag decorator is just a shortcut - it's meant as a simple way of rendering a specific template with a specific context. As soon as you want to move outside of that, it can no longer help you. But that just means you'll have to write the tag the long way, as explained in the documentation, and pass the template you want as a parameter.
I had to do something like this for a project and since we needed more than one of this kind of inclusion tag I made a decorator based on django inclusion_tag decorator. This is the code:
# -*- coding: utf-8 -*-
from django import template
from inspect import getargspec
from django.template.context import Context
from django.template import Node, generic_tag_compiler, Variable
from django.utils.functional import curry
def inclusion_tag(register, context_class=Context, takes_context=False):
def dec(func):
params, xx, xxx, defaults = getargspec(func)
if takes_context:
if params[0] == 'context':
params = params[1:]
else:
raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")
class InclusionNode(Node):
def __init__(self, vars_to_resolve):
self.vars_to_resolve = map(Variable, vars_to_resolve)
def render(self, context):
resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
if takes_context:
args = [context] + resolved_vars
else:
args = resolved_vars
file_name, extra_context = func(*args)
from django.template.loader import get_template, select_template
if not isinstance(file_name, basestring) and is_iterable(file_name):
t = select_template(file_name)
else:
t = get_template(file_name)
self.nodelist = t.nodelist
new_context = context_class(extra_context, autoescape=context.autoescape)
# Copy across the CSRF token, if present, because inclusion
# tags are often used for forms, and we need instructions
# for using CSRF protection to be as simple as possible.
csrf_token = context.get('csrf_token', None)
if csrf_token is not None:
new_context['csrf_token'] = csrf_token
return self.nodelist.render(new_context)
compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
compile_func.__doc__ = func.__doc__
register.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
return func
return dec
You have to return a tuple with the template (or template list) and the context dict. Note that you have to pass the register (Library instance) in the decorator call:
from somewhere import inclusion_tag
#inclusion_tag(register)
def display_formset(formset):
template_name = FORMSET_TEMPLATES.get(formset.model,
'includes/inline_formset.html')
return (template_name, {'formset': formset})
Hope this helps
A solution could be a regular inclusion_tag which pass dynamic template name to context.
Like this :
# templatetags/tags.py
#register.inclusion_tag('include_tag.html', takes_context=True)
def tag_manager(context):
context.update({
'dynamic_template': resolve_template(context),
})
return context
And the template:
<!-- include_tag.html -->
{% include dynamic_template %}
The tricks here is, when I call {% tag_manager %}, it includes include_tag.html which in turn includes the template returned by resolve_template() (not included for brevity).
Hope this helps...