Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django view - load template from calling app's dir first

I try to keep a somewhat consistent naming scheme on my HTML templates. I.e. index.html for main, delete.html for delete page and so forth. But the app_directories loader always seems to load the template from the app that's first alphabetically.

Is there any way to always check for a match in the calling app's templates directory first?

Relevant settings in my settings.py:

PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))  TEMPLATE_LOADERS = (     'django.template.loaders.app_directories.load_template_source',     'django.template.loaders.filesystem.load_template_source', ) TEMPLATE_DIRS = (     os.path.join(PROJECT_PATH, 'templates'), ) 

I've tried changing the order of TEMPLATE_LOADERS, without success.


Edit as requested by Ashok:

Dir structure of each app:

templates/     index.html     add.html     delete.html     create.html models.py test.py admin.py views.py 

In each app's views.py:

def index(request):     # code...     return render_to_response('index.html', locals())  def add(request):     # code...     return render_to_response('add.html', locals())  def delete(request):     # code...     return render_to_response('delete.html', locals())  def update(request):     # code...     return render_to_response('update.html', locals()) 
like image 211
jmagnusson Avatar asked Jun 22 '10 12:06

jmagnusson


People also ask

What does {% %} mean in Django?

{% extends variable %} uses the value of variable . If the variable evaluates to a string, Django will use that string as the name of the parent template. If the variable evaluates to a Template object, Django will use that object as the parent template.

Where are Django templates stored?

Generally, the templates folder is created and kept in the sample directory where manage.py lives. This templates folder contains all the templates you will create in different Django Apps. Alternatively, you can maintain a template folder for each app separately.

What is App_dirs in Django?

With APP_DIRS set to True , the template loader will look in the app's templates directory and find the templates.

What {% include %} does?

{% include %} Processes a partial template. Any variables in the parent template will be available in the partial template. Variables set from the partial template using the set or assign tags will be available in the parent template.


2 Answers

The reason for this is that the app_directories loader is essentially the same as adding each app's template folder to the TEMPLATE_DIRS setting, e.g. like

TEMPLATE_DIRS = (     os.path.join(PROJECT_PATH, 'app1', 'templates'),     os.path.join(PROJECT_PATH, 'app2', 'template'),     ...     os.path.join(PROJECT_PATH, 'templates'), ) 

The problem with this is that as you mentioned, the index.html will always be found in app1/templates/index.html instead of any other app. There is no easy solution to magically fix this behavior without modifying the app_directories loader and using introspection or passing along app information, which gets a bit complicated. An easier solution:

  • Keep your settings.py as-is
  • Add a subdirectory in each app's templates folder with the name of the app
  • Use the templates in views like 'app1/index.html' or 'app2/index.html'

For a more concrete example:

project     app1         templates             app1                 index.html                 add.html                 ...         models.py         views.py         ...     app2         ... 

Then in the views:

def index(request):     return render_to_response('app1/index.html', locals()) 

You could even write a wrapper to automate prepending the app name to all your views, and even that could be extended to use introspection, e.g.:

def render(template, data=None):     return render_to_response(__name__.split(".")[-2] + '/' + template, data)  def index(request):     return render('index.html', locals()) 

The _____name_____.split(".")[-2] assumes the file is within a package, so it will turn e.g. 'app1.views' into 'app1' to prepend to the template name. This also assumes a user will never rename your app without also renaming the folder in the templates directory, which may not be a safe assumption to make and in that case just hard-code the name of the folder in the templates directory.

like image 78
Daniel Avatar answered Sep 19 '22 04:09

Daniel


I know this is an old thread, but I made something reusable, that allows for simpler namespacing. You could load the following as a Template Loader. It will find appname/index.html in appname/templates/index.html.

Gist available here: https://gist.github.com/871567

""" Wrapper for loading templates from "templates" directories in INSTALLED_APPS packages, prefixed by the appname for namespacing.  This loader finds `appname/templates/index.html` when looking for something of the form `appname/index.html`. """  from django.template import TemplateDoesNotExist from django.template.loaders.app_directories import app_template_dirs, Loader as BaseAppLoader  class Loader(BaseAppLoader):     '''     Modified AppDirecotry Template Loader that allows namespacing templates     with the name of their app, without requiring an extra subdirectory     in the form of `appname/templates/appname`.     '''     def load_template_source(self, template_name, template_dirs=None):         try:             app_name, template_path = template_name.split('/', 1)         except ValueError:             raise TemplateDoesNotExist(template_name)          if not template_dirs:             template_dirs = (d for d in app_template_dirs if                     d.endswith('/%s/templates' % app_name))          return iter(super(Loader, self).load_template_source(template_path,                 template_dirs)) 
like image 33
hidde-jan Avatar answered Sep 17 '22 04:09

hidde-jan