Generating a list of available templates

1,486 views
Skip to first unread message

Patrick Wellever

unread,
Feb 10, 2012, 9:40:50 AM2/10/12
to django...@googlegroups.com
Hi all,

An app I'm working on has a model that, similar to the Flatpages app model, has a "template" field to allow users to specify an alternate template for displaying the object detail view.

What I'd like to do though is to make this a choice field that pulls its options dynamically from a directory on the template path. So for example, I can create a directory like "templates/myapp/entry_detail/" and have the choice field simply look into that directory and list its contents as options. I could figure it out using an absolute filesystem path, but I'd like to make it work within the context of the "template_dirs" behavior, so it's more portable.

I guess the main question is, is there some function I can use to return a directory on the template path?

Thanks for any assistance!

Best,
Patrick

Micky Hulse

unread,
Feb 10, 2012, 12:42:03 PM2/10/12
to django...@googlegroups.com
On Fri, Feb 10, 2012 at 6:40 AM, Patrick Wellever <pwel...@gmail.com> wrote:
> I guess the main question is, is there some function I can use to return a
> directory on the template path?

I'm interested in this also.

I too have my own static pages app and have always thought it would be
easier for folks to choose from a list of files rather than have to
remember what's there on the file system.

I suppose one way to do it would be to create an FK to another model
what someone has entered the paths to available templates... But, I do
like the idea of reading the contents of a folder and having a
dropdown that updates dynamically (with code portability in mind).

Patrick, if you find a solution please post it back here to the list.
I will do the same.

Thanks!
M

Python_Junkie

unread,
Feb 10, 2012, 1:03:54 PM2/10/12
to Django users
The setttings file is a python module.

See page
https://docs.djangoproject.com/en/dev/topics/settings/

Since the path is set for the templates.
*******************
frrom the example

TEMPLATE_DIRS = ('/home/templates/mike')

in the view

templates=os.listdir(TEMPLATE_DIRS)
###template_count=len(templates)

create a dictionary of templates and pass it back to the rendered
template and you have it.





On Feb 10, 12:42 pm, Micky Hulse <rgmi...@gmail.com> wrote:

Patrick Wellever

unread,
Feb 10, 2012, 1:21:08 PM2/10/12
to django...@googlegroups.com
Thanks, I think that gets me most of the way there, but the part I'm having more trouble with is figuring out how to deal with the other template loaders, such as 'django.template.loaders.app_directories.Loader', that don't pull from the 'TEMPLATE_DIRS' setting. I'd like the solution to take those into account if possible.

Is there any way to iterate over whatever list of source directories Django builds when it wants to go looking for a template?

Thanks,
Patrick

Python_Junkie

unread,
Feb 10, 2012, 1:33:32 PM2/10/12
to Django users


https://docs.djangoproject.com/en/dev/ref/templates/api/

Search on template loaders and this url will explain it.





The following is from this url

django.template.loaders.app_directories.Loader

Loads templates from Django apps on the filesystem. For each app
in INSTALLED_APPS, the loader looks for a templates subdirectory. If
the directory exists, Django looks for templates in there.

This means you can store templates with your individual apps. This
also makes it easy to distribute Django apps with default templates.

For example, for this setting:

INSTALLED_APPS = ('myproject.polls', 'myproject.music')

...then get_template('foo.html') will look for templates in these
directories, in this order:

/path/to/myproject/polls/templates/foo.html
/path/to/myproject/music/templates/foo.html

Note that the loader performs an optimization when it is first
imported: It caches a list of which INSTALLED_APPS packages have a
templates subdirectory.

This loader is enabled by default.

Patrick Wellever

unread,
Feb 10, 2012, 1:45:46 PM2/10/12
to django...@googlegroups.com
Right, I understand the loader works this way… What I'm trying to do is generate a list of 'choices' for a model field that populates itself automatically with available templates in a given directory, that works regardless of whether the specified directory is on a template path specified in the TEMPLATE_DIRS setting or a template path loaded by some loader other than the filesystem loader.

I want to scan a directory on the template path and return a list of all the files in that directory, ideally by hooking into whatever functions Django uses to locate templates, so that my list will populate correctly whether the directory is on an app-specific template path or one explicitly defined in the TEMPLATE_DIRS setting. Does that make sense? Point is I want to be able to just create new template files in this particular directory and have them automatically show up as available choices for a "template" field on my model.

Python_Junkie

unread,
Feb 10, 2012, 2:00:36 PM2/10/12
to Django users
I am not sure that I follow you.

When you mention creating new templates (you would be creating these
dynamically?) and have them show up in your database (model?)

Please explain or provide an example

Patrick Wellever

unread,
Feb 10, 2012, 2:50:41 PM2/10/12
to django...@googlegroups.com
Sorry to be unclear... Basically the functionality I want is just like in contrib.flatpages -- the model has a field called 'template' that allows the user to specify an alternate template for the object detail page. But in the flatpages app, the field is just a charfield, so the user has to know which templates are available, then enter something like 'flatpages/some_template.hrml' in the field.

I want to make it a choice field that justs lists all the files in, for example, 'templates/flatpages/page_templates/', so the user can see what templates are available and just choose one from a select list.

Don't need to create templates dynamically or store them in the db.

Thanks for your patience.

Patrick




Demetrio Girardi

unread,
Feb 10, 2012, 3:01:44 PM2/10/12
to django...@googlegroups.com
On 10 Feb 2012 at 14:50, Patrick Wellever wrote:

> I want to make it a choice field that justs lists all the files in, for example,
> 'templates/flatpages/page_templates/', so the user can see what templates are available and just
> choose one from a select list.

you can read the filesystem in the form's (or form field's) __init__.

Python_Junkie

unread,
Feb 10, 2012, 3:25:04 PM2/10/12
to Django users
The internet is a wonderful invention, but sometimes the interchange
of the spoken word is more efficient.

Let's see if I have this straight.

You know which directory the templates are in.

'templates/flatpages/page_templates/ for example.
Is that correct?

Then when you pass from the url.py to the specific view that you are
directed to by the url you simply call the function
*********************************************************
files=os.listdir('templates/flatpages/page_templates/ ')

template_qty=len(files)

for xxx in range(template_qty):
#####perform the logic to add values to the dictionary
template_dictionary=( a:'a',b:'b') ### I forget the
exact syntax

render(form_template_listing.html,template_dictionary) - ## I
forget the exact syntax

******************************************************

Then in the form_template_listing.html , template unpack the template
dictionary in the drop down list.




I believe this is what you are asking.

If not I can send you my cell phone number and we can discuss it.





On Feb 10, 3:01 pm, "Demetrio Girardi" <demetrio.gira...@gmail.com>
wrote:

Patrick Wellever

unread,
Feb 10, 2012, 4:19:10 PM2/10/12
to django...@googlegroups.com
Sorry, this question is turning out to be much more difficult to articulate than I originally expected. Thanks for bearing with me. ;)

You definitely have the right idea of what I'm trying to do -- the only problem with this method is that Django looks for templates in multiple places, so I don't actually know the absolute path to the directory the templates are in. You quoted the critical bit from the docs in your earlier message: if 'django.template.loaders.app_directories.Loader' is included in your TEMPLATE_LOADERS setting, Django will search for a directory called 'templates' in each installed app, in addition to all the directories specified in TEMPLATE_DIRS, when trying to load a template.

So if I have an app called 'myapp' and my settings.py contains this:

TEMPLATE_DIRS = ('/home/django/templates',)

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

... then both "/home/django/templates/myapp/" and "/path/to/myapp/templates/myapp/" are legitimate places to stick the templates for the app. I'm trying to find a solution that would list the contents of my specified directory in either of these locations.

I need something like:

template_files = []
for dir in EVERY_DIRECTORY_DJANGO_LOOKS_FOR_TEMPLATES_IN:
    template_files.extend(os.listdir(dir))

I have to agree about the spoken word, though... Happy to discuss this by phone if you'd like, and thanks again for your help.

- Patrick

Python_Junkie

unread,
Feb 10, 2012, 4:33:45 PM2/10/12
to Django users
I think you have it with the syntax you just wrote down.

I will be available by phone until 4:45 if you want to call.

781-248-6557

On Feb 10, 4:19 pm, Patrick Wellever <pwelle...@gmail.com> wrote:
> Sorry, this question is turning out to be much more difficult to articulate
> than I originally expected. Thanks for bearing with me. ;)
>
> You definitely have the right idea of what I'm trying to do -- the only
> problem with this method is that Django looks for templates in multiple
> places, so I don't actually know the absolute path to the directory the
> templates are in. You quoted the critical bit from the docs in your earlier
> message: if 'django.template.loaders.app_directories.Loader' is included in
> your TEMPLATE_LOADERS setting, Django will search for a directory called
> 'templates' in each installed app, *in addition to all the directories
> specified in TEMPLATE_DIRS*, when trying to load a template.

Patrick Wellever

unread,
Feb 10, 2012, 4:51:18 PM2/10/12
to django...@googlegroups.com
Thanks -- you must be on your way out the door now so I won't call, but according to your area code it looks like we might be neighbors. ;) So following the syntax I wrote out, what I still need to know is how do I get to EVERY_DIRECTORY_DJANGO_LOOKS_FOR_TEMPLATES_IN. That's a variable that represents wishful thinking on my part -- I don't actually know how to get an iterable list of those directories.

There must be some way I can sort of hook into the logic Django uses to construct a list of template directories...

- P


On Fri, Feb 10, 2012 at 4:33 PM, Python_Junkie <software....@gmail.com> wrote:
I think you have it with the syntax you just wrote down.

I will be available by phone until 4:45 if you want to call.

781-...

Python_Junkie

unread,
Feb 10, 2012, 4:54:35 PM2/10/12
to Django users
I think I just figured out what you want to do.

You want one drop down list with all of the templates listed
regardless of which folder the template lives in.

So, as describe above you would create a dictionary of template_names
by walking through the directories where the templates live
and pass the dictionary back to the drop down list.

Once the template is selected then you have to render back to the
folder that the selected template lives in.

You can create a database table with 2 columns , assuming all of the
templates are named uniquely.

column a contains the nameof all of the templates from the drop down
list.
column b contains the path that the template lives in.

So, when the user selects the template, the template value is passed
to the view.

The view can then perform a sql query

select template_path from table where template ='template_name'

value=crsr.fetchone()

full_path=value+template_name

render(full_path, plus whatever values you want to pass that
template)


If you don't want to use a table you could have python walk through
all of the directories until it achieves a match on the template name

I hope this solves your question




On Feb 10, 4:33 pm, Python_Junkie <software.buy.des...@gmail.com>
wrote:

Patrick Wellever

unread,
Feb 10, 2012, 5:15:28 PM2/10/12
to django...@googlegroups.com
Sorry... I think that's a little more complicated than what I need, actually. Django already handles searching for the template in all the right locations, so I don't have any problem actually rendering the template, and I shouldn't need to worry about absolute paths. Here's a simplified bit of code from the flatpage view in contrib.flatpages:

DEFAULT_TEMPLATE = 'flatpages/default.html'

def flatpage_detail(request, url):
    # ...
    f = get_object_or_404(FlatPage, url__exact=url)
    
    if f.template_name:
        t = loader.select_template((f.template_name, DEFAULT_TEMPLATE))
    else:
        t = loader.get_template(DEFAULT_TEMPLATE)


So in the Django admin site, if you have a flatpage that you want to use a template other than the default one, you enter the path to the template into the "template_name" field as something like 'flatpages/custom_template.html'. I want the exact same functionality, but I just want the admin site to display a select list of all templates in a given directory instead of a blank text field. So within my templates directory I'd make a directory at "flatpages/templates/" that includes "default.html," "custom_template_a.html," "custom_template_b.html" and so on.

I'm just stuck on how to generate a list of those templates to use as choices in the admin site select menu. I want the list to populate based on the actual files that exist in the directory and update itself when new files are added. When you save the model instance, the template name you selected is saved in a column on the model itself and rendering the correct template is no problem.

Bill Beal

unread,
Feb 10, 2012, 5:05:41 PM2/10/12
to django...@googlegroups.com
I know nothing, but here's what I did:

python manage.py shell
import os
from settings import TEMPLATE_DIRS
for x in TEMPLATE_DIRS:
  print x
  os.listdir(x)

It gave me a list of the files in the (each) template directory.

It looks like you already have a list of template directories in settings.py.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Patrick Wellever

unread,
Feb 10, 2012, 8:34:35 PM2/10/12
to django...@googlegroups.com
Thanks for the reply. Yes, this wouldn't be too tough if the directories in TEMPLATE_DIRS were all I had to worry about, but the glitch I mentioned earlier is that Django can also look for templates in places other than those listed explicitly in that setting.

For example, the template loader 'django.template.loaders.app_directories.Loader', which is enabled by default, looks for a "templates" directory within each one of your "installed apps." So a solution that only considers paths in TEMPLATE_DIRS would not work in that situation.

- Patrick

bb6xt

unread,
Feb 11, 2012, 5:39:50 AM2/11/12
to Django users
Here's my two cents. Hope it helps some.

from proj.settings import TEMPLATE_DIRS, INSTALLED_APPS
import os

def recursive_search(path):
result = []
for i in os.listdir(path):
if not os.path.isdir(os.path.join(path, i)):
result.append(i)
else:
result.extend(recursive_search(os.path.join(path, i)))
return result

def myview(request):
templates = []
# the next 2 lines handles templates in explicit template
directories
for path in TEMPLATE_DIRS:
templates.extend(recursive_search(path))
# the next 2 will handle template dir generated py the
appdirectories loader excluding contrib apps
for app in INSTALLED_APPS:
if app.startswith('proj'):
if os.path.exists(os.path.join(ABSPATH_TO_PROJ,
app.split('.')[-1], 'templates')):

templates.extend(recursive_search(os.path.join(ABSPATH_TO_PROJ,
app.split('.')[-1], 'templates')))

well, you have a list of templates with which you can do as you
please. surely you'll want to keep track of the actual path django
uses to locate these templates, you can do so by tweaking the
recursive_search funct a little

Patrick Wellever

unread,
Feb 11, 2012, 10:30:50 AM2/11/12
to django...@googlegroups.com
Thanks for weighing in, that is helpful, and last night I was playing around with a somewhat similar approach. It's not exactly what I was hoping for, because it still isn't really integrated with the way Django uses template loaders.

This solution works for TEMPLATE_DIRS and INSTALLED_APPS, but there are other possible template loaders, or a user might have written a custom template loader. Or they may have disabled the app directories loader… Anyway, you get the idea.

If the built-in loader classes had some method that would just return the paths to the template directories that were loaded, this could work perfectly, but I think the loaders can only check to see if a supplied template name exists on one of the paths they serve. So it seems like my idea is not really feasible in a way that integrates cleanly with Django and results in a portable application. Thanks to everyone who replied with assistance.

- Patrick

William Stewart

unread,
Oct 28, 2014, 8:06:27 AM10/28/14
to django...@googlegroups.com
I know this thread has been dead for over 2 years, but I've been trying to solve this problem or a similar problem today for a library I'm working on.

I've only tested it with the filesystem and app_directories loaders, but the code I've got to so far looks like this:

for loader in settings.TEMPLATE_LOADERS:
    loader_module = importlib.import_module('.'.join(loader.split('.')[:-1]))
    print '\n'.join(source for source in loader_module.Loader().get_template_sources(''))

Just thought I'd share it in case anyone was interested.

Cheers,


William

Collin Anderson

unread,
Oct 29, 2014, 2:24:12 PM10/29/14
to django...@googlegroups.com
Hi William,
 
for loader in settings.TEMPLATE_LOADERS:
    loader_module = importlib.import_module('.'.join(loader.split('.')[:-1]))
    print '\n'.join(source for source in loader_module.Loader().get_template_sources(''))

You could also get the loader like this:

from django.template.loader import find_template_loader
for loader_name in settings.TEMPLATE_LOADERS:
    loader
= find_template_loader(loader_name)
    loader
.get_template_sources('')  # etc

Collin

Reply all
Reply to author
Forward
0 new messages