templates & elements

404 views
Skip to first unread message

patrick k

unread,
Dec 3, 2005, 7:46:18 AM12/3/05
to django-d...@googlegroups.com
i´ve been asking that question before. but now, i do have a somehow detailed
description at:
http://www.vonautomatisch.at/framework/dkit/description.php?id=1

basically, we´d like to split a site into a main template, into boxes
(div-structures) and elements (content). then, we´d like the administrator
(or editor) of a site to build new pages (and edit existing ones) on that
basis.

hope anyone can help.

patrick

Colin Howlett

unread,
Dec 4, 2005, 12:46:35 PM12/4/05
to Django developers
At risk of hijacking the thread, I've been struggling with something
similar (if I understand patrick correctly) Basically, I've been trying
to come up with a way of including re-usable 'pagelets' or
sub-templates into a parent template that are rendered with their own
local context generated by generic (i.e. re-usable) code. Essentially
I've come up with two ways of doing this, both of which have issues.

The first way I've tried involves coding the pagelet as if it were a
full blown page - i.e. creating a template suitable for use with a
generic view, and connecting that to the generic view using a urlconf
in the normal way, so I can browse to a url and see the template
rendered - the app_label and module_name are passed into the generic
view in a dict, and parameters can be passed from the url - all
standard stuff. Then I've written a custom template tag, which takes a
constructed url as a parameter, resolves that url and returns the
rendered template. The code is below:

from django.core import template
from django.utils.httpwrappers import HttpRequest
from django.utils.httpwrappers import HttpResponse
from django.utils.httpwrappers import HttpResponseNotFound
from django.utils.httpwrappers import HttpResponseForbidden
from django.utils.httpwrappers import HttpResponseGone
from django.utils.httpwrappers import HttpResponseServerError
from django.core.handlers.base import BaseHandler
from django.parts.auth.anonymoususers import AnonymousUser
from django.models.auth import User


#usage: {% include_url %}/url/with/{{ param }}/{% end_include_url %}

def do_include_url(parser, token):
nodelist = parser.parse(('end_include_url',))
parser.delete_first_token()
return IncludeUrlNode(nodelist)

class IncludeUrlNode(template.Node):

def __init__(self, nodelist):
self.nodelist = nodelist

def render(self, context):
try:
self.request = HttpRequest()
self.request.path = self.nodelist.render(context)
self.request.META['HTTP_HOST'] = ''
self.request.META['REQUEST_METHOD'] = ''
self.request.META['REMOTE_ADDR'] = ''

if (context.has_key('user') and
isinstance(context['user'], User)):
self.request.user = context['user']
else:
self.request.user = AnonymousUser()

self.handler = BaseHandler()
self.handler.load_middleware()
self.response =
self.handler.get_response(self.request.path, self.request)

if (isinstance(self.response, HttpResponse) and
not isinstance(self.response, HttpResponseNotFound) and

not isinstance(self.response, HttpResponseForbidden)
and
not isinstance(self.response, HttpResponseGone) and
not isinstance(self.response, HttpResponseServerError)
and
isinstance(self.response.content, basestring)):
return self.response.content
else:
return ""
except:
return ""

register = template.Library()
register.tag('include_url', do_include_url)

This is OK, it would be better if it had access to the request object,
then it could prevent recursive calls (i.e. if you pass it the url of
the template that it's located in, you will generate an infinitely long
response as the tag is called recursively). It has the advantage that
it can use generic views, but it doesn't really feel right to configure
these pagelets with their own urls.

The alternative is to use the {% include %} tag to include an
unrendered template, which then loads and uses a template tag to
populate the context it needs to be rendered. It would in theory be
possible to write 'generic' template tags, which like the generic views
take 'app_label' and 'module_name' as parameters alongside any other
parameters. These would be re-usable, but getting the app_label and
module_name to them at the point they're called in the template is
tricky - you could hard code them into the template, but then the
template isn't re-usable anymore. Alternatively, you could try to pass
them in the context of the parent template, or load them with a custom
template tag which reads some form of configuration, but both of those
seem messy.

I'd be very interested in any other suggestions for ways to include
reusable templates that are rendered with context from reusable
(generic) code. Or suggestions for ways to improve my template tag code
above.

Robert Wittams

unread,
Dec 4, 2005, 7:40:40 PM12/4/05
to django-d...@googlegroups.com
Sorry, I just can not work out what on earth you are actually trying to
achieve - too many instances of the word 'generic'... A concrete
example would be best.

Colin Howlett

unread,
Dec 4, 2005, 10:50:20 PM12/4/05
to Django developers

Robert,

Sorry, I guess I wasn't communicating very clearly.

What I want is something like the {% include %} tag but with batteries
included. With the {% include %} tag I still have to find some context
from somewhere to render the included template - either it's provided
by the main (custom) view or by custom template tags - I'd like to get
it from something like a generic view - the less coding the better.

An example:

Suppose I make a very simple site to show details of people - I have a
template to show name, date of birth etc. I want to visit e.g.
http://localhost/people/1/ to see the first person's details,
.../people/2/ to see the second etc., so I create a urlconf to connect
urls of the form /people/(\d+)/ to an object_detail generic view and
pass in a dictionary with app_label and module_name set appropriately.

Suppose I make a very similar simple site for cars instead of people.

So every page looks something like this in my people app:

Name: Bob Jones
Date of birth: 10/10/2010

or this in my car app:

Manufacturer: Ford
Model: Focus
Color: Red

Then I decide I would like to add, on each page, next to the detail of
each person or car, a list of all people or all cars, so I can click on
a person or car and it will take me to the detail page for that person
or car (which still has the list next to the detail). So every page
looks something like this in my people app:

Bob Name: Bob Jones
Mary Date of birth: 10/10/2010
John

or this in my car app:

Ford Focus Manufacturer: Ford
Hummer H2 Model: Focus
Fiat Panda Color: Red

If I was to make a list of all cars or people as a separate page I
could use an object_list generic view, but since I've already used an
object_detail generic view for my page I can't use another view - each
page has only one view. Obviously I could write a custom view, but I
like generic views - what would be nice would be if I could make a page
just for the list, using a generic view, configured with a url, and
then include that page, rendered by its generic view, into my original
page, just by including its url. That's what my template tag above
does.

In other words my {% include_url %} template tag is kind of like the {%
include %} tag, but it doesn't take the path to a template, it takes
the url of a page, and instead of being replaced by that un-rendered
template (which is subsequently rendered with the context of the
template it is included within), it is replaced by the rendered page.

--

An alternative would be to write template tags equivalent to each
generic view and use those along with an {% include %} tag.

--

I think each solution has their pros and cons - my include_url tag
requires that the pagelets (i.e. the pages showing just the lists) have
their own urls - which doesn't seem right since nobody should ever
really navigate to those urls (except perhaps for testing) - they're
just used for including in the main page.

On the other hand if I use some kind of new 'generic' template tags
(i.e. template tag equivalents of generic views) then I have two
choices:

- I could add the 'generic' template tag to the *included* template -
but then the included template has to hard code all the values to pass
to the 'generic' template tag (such as the app_label and the
module_name) and I can't re-use it.

- I could add the 'generic' template tag to the *including* template,
but then it pollutes the context. The generic template tag would have
to return something like 'object' or 'object_list' so I couldn't add
two in the same template.

I'm still not sure which is the best way, or if there's another way I
haven't thought of.

I hope that's at least slightly clearer.

Medium

unread,
Dec 5, 2005, 8:18:56 AM12/5/05
to django-d...@googlegroups.com
Are you guys looking for portal or portlet like functionality ? From
what I have seen, neither django or any other python web framework
currently supports such a component style of web programming. You would
need to handle the model repeatedly in your views or in some sort of
dynamic fashion, else use something like cheetah where you can load
models directly from the the template. Have a super template which can
dynamically include other templates whose names are passed in as part of
the model and then loaded dynamically. Sounds very messy and probably
not a great idea but that's all I can think of.

regards,

huy


Amit Upadhyay

unread,
Dec 5, 2005, 8:29:02 AM12/5/05
to django-d...@googlegroups.com
On 12/5/05, Medium <conta...@gmail.com> wrote:

Are you guys looking for portal or portlet like functionality ? From
what I have seen, neither django or any other python web framework
currently supports such a component style of web programming. You would
need to handle the model repeatedly in your views or in some sort of
dynamic fashion, else use something like cheetah where you can load
models directly from the the template. Have a super template which can
dynamically include other templates whose names are passed in as part of
the model and then loaded dynamically. Sounds very messy and probably
not a great idea but that's all I can think of.

It does not have to be this complicated. Proposal:

A tag called "render".

<% render partialURL %>

view: its partial URL enough to resolve to actual view following the URLConf.

This tag will following the whole django request processing, but without making a HTTP request, and return the content generated by the view. render tag can access request from the DjangoContext and view would be executed in the same context, so cookies, session data and everything else will be inherited by the view.

For completeness, render tag may take full urls and include external pages at the current location.

Repeatedly using the request object from different views may have some side effects, but just knowing that may be enough to stop it from hurting developers in any significant manner I guess.

--
Amit Upadhyay
Blog: http://www.rootshell.be/~upadhyay
+91-9867-359-701

patrick k

unread,
Dec 5, 2005, 9:02:36 AM12/5/05
to django-d...@googlegroups.com
> On 12/5/05, Medium <conta...@gmail.com> wrote:
>>
>> Are you guys looking for portal or portlet like functionality ? From
>> what I have seen, neither django or any other python web framework
>> currently supports such a component style of web programming. You would
>> need to handle the model repeatedly in your views or in some sort of
>> dynamic fashion, else use something like cheetah where you can load
>> models directly from the the template. Have a super template which can
>> dynamically include other templates whose names are passed in as part of
>> the model and then loaded dynamically. Sounds very messy and probably
>> not a great idea but that's all I can think of.

we are looking for a clean and easy way. nevertheless, i will check out
cheetah (been working with "kid" so far).

> It does not have to be this complicated. Proposal:
>
> A tag called "render".
>
> <% render partialURL %>

i like that.

with rails, you do have a render-tag. still, it´s not possible to have
something like <% render 'partial_film', filmobject, params:id=1237 %>

Colin Howlett

unread,
Dec 5, 2005, 11:38:48 AM12/5/05
to Django developers
What Amit is proposing with his

{% render partialURL%}

tag is extremely similar in concept to the

{% include_url %}/url/with/{{ param }}/{% end_include_url %}

tag that I proposed (and provided code for above). my reasoning for
using separate start and end tags was to ease the construction of a url
with variables. Otherwise they are identical in concept.

I'm not sure though if Amit envisaged resolving his partialURL using a
urlconf or some other mechanism.

Also note that my proof of concept code does actually construct a dummy
request, as contrary to what Amit implies, the request is not currently
available in DjangoContext (although it does take the user from
DjangoContext if it's available). This issue may be addressed in future
if the proposal for request processors is adopted (ticket #925).

The essence of my original post though was to solicit feedback as to
whether such an approach is a good one, or if there are better
alternatives for composing pages out of 'portlets' (or whatever you
want to call them).

Robert Wittams

unread,
Dec 5, 2005, 8:15:45 PM12/5/05
to django-d...@googlegroups.com
Ok,

I think you want to use inclusion_tag.

It is an easy way to create an template tag with custom logic.

Search back in the archives for examples of its use, or look at the
examples in the admin templatetags directories.

Colin Howlett

unread,
Dec 6, 2005, 7:19:11 AM12/6/05
to Django developers
Thanks Robert!

The @inclusion_tag decorator is really nice! Much nicer than what I
wrote above.

One thing I'm not too keen on with @inclusion_tag is that the template
is hard coded in the tag itself. (Did I understand that correctly?)
That means everytime I want to use a different template, even if the
logic for populating the context is the same, I need to write a new
tag. It would be nice if it were possible (optionally) to pass a
template name as a parameter to an inclusion_tag.

Looking at the code for inclusion_tag() it doesnt look that hard to
optionally pass in a template name in the same way you can optionally
pass in a context - i.e. make file_name an optional parameter and add
takes_template as an optional parameter defaulting to False. If
takes_template were set to True the function should take a template
file name as its first parameter (or second if takes_context is also
True).

If nobody objects (rather, if people think it's a good idea), I may try
to submit a patch for this.

Thanks.

Robert Wittams

unread,
Dec 6, 2005, 10:10:51 AM12/6/05
to django-d...@googlegroups.com
Colin Howlett wrote:
> Thanks Robert!
>
> The @inclusion_tag decorator is really nice! Much nicer than what I
> wrote above.
>
> One thing I'm not too keen on with @inclusion_tag is that the template
> is hard coded in the tag itself. (Did I understand that correctly?)
> That means everytime I want to use a different template, even if the
> logic for populating the context is the same, I need to write a new
> tag. It would be nice if it were possible (optionally) to pass a
> template name as a parameter to an inclusion_tag.
>
> Looking at the code for inclusion_tag() it doesnt look that hard to
> optionally pass in a template name in the same way you can optionally
> pass in a context - i.e. make file_name an optional parameter and add
> takes_template as an optional parameter defaulting to False. If
> takes_template were set to True the function should take a template
> file name as its first parameter (or second if takes_context is also
> True).
>
> If nobody objects (rather, if people think it's a good idea), I may try
> to submit a patch for this.
>

Ok, so I was thinking about something a little different for this issue
(flexibility in the template included).
The filenames would be a varargs, which would be interpolated with the
context you create. So an example would be

@register.inclusion_tag('admin/%(app_label)s/%(model_name)s/whatever',
'admin/%(app_label)s/whatever',
'admin/whatever')
def whatever(cl):
return {'app_label': cl.app_label,
'model_name': cl.model_name,
'something': cl.something }

So all the names would be interpolated with the context created at
render time, and passed to select_template. This allows similar
flexibility to the top level admin pages.

For now, you can just use @register.simple_tag,

@register.simple_tag
def something(template_name, whatever):
return render_to_string(template_name, {'something': something}

Also at some point I want to factor out the argument messing from the
rendering, so it would be something like

@register.include_tag('admin/whatever')
@register.takes_context
@register.takes_block
def whatever(context, block, cl):
return {}

So these things would just add function attribute(s) which would then be
used by gen_compile_func.

I'm not working on this right now, but I think it is worth looking at.

Colin Howlett

unread,
Dec 6, 2005, 10:56:38 AM12/6/05
to Django developers
Robert,

Thanks again. I can see how I can acheive what I want to acheive with
simple_tag.

I'm not sure that I understand your first example though.

Is the list of url patterns passed as parameters to inclusion_tag
supposed to be searched for a match to decide which template to use?

It seems I have to pass the values to complete the patterns in the
context. I'm not sure I understand how I'm supposed to get them there -
through the view of the parent page? from a separate template tag?

It all seems kind of complicated - which given the elegance of what
you've produced so far, suggests I've misunderstood something. In
short, I'm confused. Would you care to elaborate?

Thanks.

Robert Wittams

unread,
Dec 6, 2005, 11:21:53 AM12/6/05
to django-d...@googlegroups.com

I was suggesting future extensions, not something that works currently.
Basically the idea was that you get 'some' information in your
arguments, and use that information to generate a context which will be
used *both* to resolve the template, and to render it. Clearly, you
could just pass the whole template name as an argument - eg here is a
version of include with this hypothetical system (which doesn't pass
through the context from its caller).

@register.inclusion_tag('%(name)s'):
def include(context, name):
return {'name', name}

This is a future direction, not something that would work right now.

Colin Howlett

unread,
Dec 6, 2005, 12:19:44 PM12/6/05
to Django developers
Robert,

I understood it wasn't implemented yet. Thanks for the clarification -
it looks good!

Reply all
Reply to author
Forward
0 new messages