views:

53

answers:

3

Hi All,

I'm struggling to work out how best to do what I think I want to do - now it may be I don't have a correct understanding of what's best practice, so if not, feel free to howl at me etc.

Basically, my question is, how do I abstract application functionality correctly, said functionality being found in an application that is part of a project?

To put some context on this (this is not far away from what I'm trying to achieve):

Suppose, I am building a website. My website has a look and feel and I've defined a base.html piece of boilerplate and various css-items in a model-less django app.

Now, I'm also writing a django blog application. I know how to include it so that the models appear in my current application and I'm more than happy that I can use any method I like from the various packages and manipulate the models and do thing all python with it.

What I don't understand is how to deal with views. It seems counter-intuitive to have the models stored elsewhere but have to build the user interface in my current project (and by implication any subsequent project). So, I thought, fine, include urls from my app folder in my project, done.

Then of course I have to write views, which is fine. I can do that.

What I can't get my head around is how templates fit in: in my app, I want to serve a template like this:

<div class="entry">
<a href=""><h2>{{ entry.Title }}</h2></a>
<p>Published on: {{ entry.date_published|date:"j F Y" }}</p>
{{ entry.body_html }}
</div>
<a href="">X comments.</a>&nbsp;<a href=""">Add Comment</a>&nbsp;<a href="">Link</a>

<div id="commentdiv">
{% if comments %}
    <ul id="commentlist">
    {% for comment in Comments %}
        <li></li>
    {% endfor %}
    </ul>
{% else %}
    <p>There are no comments on this entry.</p>
{% endif %}

</div>

for viewing an entry, so on my site I go to sitename.com/blog/...someparameters.../entryname' where blog is theincludeinurls.py` of the root project in question. All fine and well, but how do I also attach the boilerplate from that project i.e. the look and feel? I could go all out and design a base template for the blog application, fair enough, but what about navigation etc? I might want to include the same header on every page even if the content is fairly diverse.

Now, I'm aware of the {% extends "template" %} directive and {% include template %} directive. Why these don't work as far as I understand it:

  • {% extends %} - how do I know/provide what I want to extend from the root project? If you can provide a mechanism for this I think it would solve the problem very well.
  • {% include %} - implies I have to provide views for the blog in a bootstrap application in the root project. I really don't want to do this - I might as well move the entire project into that if that's the case.

So my question is, how do I put it all together?

Edit: I think this question is quite similar to what I'm asking and have noted the answers. However, they're still don't satisfy.

+1  A: 

If I understand your problem correctly, then I would suggest doing a Custom Template Tag. In it you can do anything you want: use 0 or more args to the tag to get at and manipulate arbitrary objects, and then invoke the template engine "by hand" to get a snippet to return. E.g.

[...]
t = loader.get_template('sometemplate.html')
c = Context({
    'my_data': my_data, # or whatever
})
return t.render(c) # this gets inserted into the invoking template

If you don't want to have the various "main templates" of the site do a {% load ... %} then you can stick something like the following in the __init__.py of the app directory:

from django import template
template.add_to_builtins('myapp.templatetags.myapptags')

Which makes it available anywhere in the site, just like any other built-in.

Peter Rowell
Hi Peter, that looks like what I'm looking for after a fashion - I have to change strategy ever so slightly because I wanted to isolate the blog app from everything else, but that's not going to be possible it doesn't seem.
Ninefingers
It's hardly ever possible to completely isolate it because *something* has to reference your app *somewhere*. I tend to put all of the app-specific stuff into an include file and that minimizes the embedded knowledge in other files. The one thing you can do (but it definitely qualifies as a *hack*) is to handle the insertion of your app in middleware. I did this for an edit-this-object overlay on the admin code, but that was a very special case. Middleware is useful, but when used "too cleverly" it can drive a future maintainer crazy because it is a hidden man-in-the-middle technology.
Peter Rowell
A: 

If you want to use template inheritance with django in a way that makes sense, you do not only need to use the {% extends %} tag but also the {% block %}tag! Specify in your base template the blocks that your app needs to replace: E.g.:

<html>
    <body>
         <div id="content">
         {% block "content" %}
         {% endblock %}
         </div>
    </body>
</html>

You can then easily fill the content of the div with your app with template inheritance:

{% extends "base.html" %}
{% block "content" %}
    The content generated by your app.
{% endblock %}

You should keep in mind that also Django's template language tries to fulfill the concept of not repeating yourself, so try to avoid repeating block's with inheritance / inclusion, it will give you a project that is much nicer to maintain!

lazerscience
Hi lazerscience, yep, I already use extends and blocks quite a lot - what I'm trying to achieve though is total isolation of functionality so I can just include a blog via settings.py and it will work at url /blog BUT it will also inherit the default look and feel from somewhere i.e. a base html pattern. I can't extend across projects I don't think, or to put it a different way, if I "extend" base in the blog app and provide no base.html for that app and rely on it being supplied elsewhere, does it work? I don't think it will.
Ninefingers
A: 

The typical way to do this is to use a base template, extend it in your app and replace any blocks you like with your app's content, and use template tags to bring in content from other apps.

For example:

base.html (in your project's templates):

<html>
  <head>
    <title>{% block "title" %}Default title{% endblock %}</title>
  </head>
  <body>
    <div id="nav">{% block "nav" %}{% endblock %}</div>
    <div id="content">{% block "content" %}{% endblock %}</div>
  </body>
</html>

template_name.html (in the app's templates):

{% extends "base.html" %}

{% load other_app_tags %}

{% block "nav" %}
  {% other_app_nav_tag %}
{% endblock %}

{% block "content" %}
  lotsa good junk.
{% endblock %}

other_app_tags.py (in the other app's templatetags dir):

from django import template

register = template.Library()

@register.inclusion_tag('navigation.html')
def other_app_nav_tag():
    # You could give this tag arguments to change how it behaves
    return {
        'links': {
            'Home': '/home'
        }
    }

navigation.html (in the other app's templates):

<ul>
{% for text, target in links %}
  <li><a href="{{ target }}">{{ text }}</a></li>
{% endfor %}
</ul>

This uses inclusion tags to render from your other app's templates. It's a good way to contain templates to their own relevant apps and still make them available throughout the site.

Clueless