I'm working on an Open Source Django app and created some design for it. Now a customer wants to use it's own, copyrighted, design. After reading the Django docs I created a separate, private, GIT repository that I want to use for the new design. I got it almost working by adding 2 items to the settings; an extra entry to look for templates in a folder "funder_custom_templates" and an extra entry to look for static files in the same location. This is how I configured TEMPLATES and STATICFILES_DIR:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(PROJECT_DIR, '..', '..', 'funder_custom_templates'),
os.path.join(PROJECT_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'fundraiser.context_processors.cart',
],
},
},
]
STATICFILES_DIRS = [
os.path.join(PROJECT_DIR, '..', '..', 'funder_custom_templates','static'),
os.path.join(PROJECT_DIR, 'static'),
]
This works for overriding the base design located in the PROJECT_DIR/templates/base.html, when I create funder_customer_templates/base.html and for all the static files as expected. But I also want to override app specific template files like blog/templates/blog/blog_index_page.html
I tried to put these files in the root of funder_custom_templates and I tried to mimic the app folders structure in funder_custom_templates but that doesn't load the app specific templates. Is there a way to solve this?
Example project files, with the folder structure, located at: https://github.com/acidjunk/funder/tree/develop/
Since you are using the app_directories.Loader class to load templates (specified by setting 'APP_DIRS': True,), then for app specfic templates Django will iterate over your INSTALLED_APPS setting looking for a specific template file. The important thing here is that it does so in order.
So if you want to override blog/templates/blog/blog_index_page.html then you will need to have a custom_blog/templates/blog/blog_index_page.html inside an application that comes before blog.
I recommend wrapping up all custom static resources in their own django application and python package. This way you can simply install the package from its private repo and add it to the list of installed apps to override any static content and templates.
See Django's docs for more details on template loading: https://docs.djangoproject.com/en/1.9/ref/templates/api/#django.template.loaders.app_directories.Loader
Related
I am using Django honeypot (https://github.com/jamesturk/django-honeypot) and was wondering how to customize the honeypot_error.html page (https://github.com/jamesturk/django-honeypot/blob/master/honeypot/templates/honeypot/honeypot_error.html), which is executed when honeypot detection gets tripped. Thanks!!
if you had honeypot_error.html with the same structured path
templates/honeypot/honeypot_error.html in your project it will take your page as default. (see the key point to note: here )
example:
package x define a template named toto.html in templates/myapp/toto.html
in your application, add a customized toto.html in templates/myapp/toto.html
when calling
python manage.py collectstatic
it will get your template and not the one provided by default by myapp
I can't comment on MSR974's answer so:
From Django's documentation on overriding templates:
from pathlib import Path
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
INSTALLED_APPS = [
...,
'blog',
...,
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
...
},
]
Notice: "If you have app and project templates directories that both contain overrides, the default Django template loader will try to load the template from the project-level directory first. In other words, DIRS is searched before APP_DIRS."
As you read through the examples, keep in mind the distinction between APP and PROJECT templates and make sure you've configured your SETTINGS according to the chosen strategy.
I'm trying to create a custom widget in Django admin. I created a class:
class FroalaWYSIWYGTextareaWidget(django.forms.widgets.Textarea):
template_name = 'froala_wysiwyg.html'
Then a simple model form:
class ArticleForm(django.forms.ModelForm):
class Meta:
fields = '__all__'
model = Article
widgets = {
'content': FroalaWYSIWYGTextareaWidget(),
}
Here are my settings:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_PATH, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.i18n',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
Usualy everything works fine and Django can find templates in my /templates/ directory but in case of this widget I have a 500 Error:
TemplateDoesNotExist at /admin/article/article/1/change/
froala_wysiwyg.html
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/article/article/1/change/
Django Version: 1.11.4
Exception Type: TemplateDoesNotExist
Exception Value: froala_wysiwyg.html
Exception Location: /home/username/.virtualenvs/sitename/lib/python3.5/site-packages/django/template/engine.py in find_template, line 148
Python Executable: /home/username/.virtualenvs/sitename/bin/python
Python Version: 3.5.2
I debugged django.filesystem.loader and found out that usually Loader.engine.dirs is a list:
['/home/username/python/sitename/templates']
so Loader.get_template_sources() works great
but in case of this custom widget this loader.engine.dirs contains only:
['/home/username/.virtualenvs/sitename/lib/python3.5/site-packages/django/forms/templates']
So it just ignores DIRS option from the settings and uses forms/templates instead. Is it a bug of Django or I have to change something in settings?
I don't understand where does this django/forms/templates path come from?
Thanks.
If you want to use a custom widget template stored somewhere under your "TEMPLATES" directory of your project then follow these steps:
a) Use the TEMPLATES settings that you have provided in your question
b) Set the FORM_RENDERER as following in the settings.py
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
c) Add the app "django.forms" to the 'INSTALLED_APPS' list in settings.py
Also, be sure to assign the correct path of the custom widget template relative to your "TEMPLATES" directory to "template_name" attribute of your custom widget.
Its certainly not a bug
I don't understand where does this django/forms/templates path come from?
You can view the source code where you can see the line
[docs]class Textarea(Widget):
template_name = 'django/forms/widgets/textarea.html'
This was the source of your first question. Now second one
This renderer uses a standalone DjangoTemplates engine (unconnected to what you might have configured in the TEMPLATES setting). It loads templates first from the built-in form templates directory in django/forms/templates and then from the installed apps’ templates directories using the app_directories loader.
This is true for your form widget classes also. To make things work for you custom widget template you have to specify the path with same terminology like app_name/forms/widget/textarea.html
I am trying to set up a new blog. I want to keep all my project templates folder in the same folder as where my settings.py is. To do this I did the following...
[...]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "templates/")],
'APP_DIRS': False,
'OPTIONS': {
'context_processors': [
[...]
But now, my admin panel layout doesn't work. How can I circumvent the above solution when using the admin panel? I get the error
Exception Type: TemplateDoesNotExist
Exception Value: admin/login.html
By disabling APP_DIRS you're forcing Django to look for your templates in the templates folder of your basedir regardless of where the app specifies them. This will break any plugins and also prevents you from namespacing templates. Its generally a bad idea.
DIRS is a list, so you can specify multiple locations if desperately want to hold the templates in a different locationand maintain access to teh admin
I have the following directory structure in my django 10 project:
/my-project/ # project dir
+app1
+templates
+admin
base.html
404.html
500.html
My templates attribute looks like this in settings:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
'templates/',
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'common.context_processors.site_info',
],
},
},
]
My custom base.html displays on my local machine. When I upload this to my production server it no longer overrides and uses the base.html file in project folder.
I have changed around the order of apps suggested here and tried printing the dirs element of the templates attribute which prints "templates/" like here.
Does anyone know how I can get this to work on my production environment?
You must use absolute path in your settings to avoid issues. For example:
import os
PROJECT_ROOT = os.path.join(os.path.dirname(__file__), '..', '..')
# depending where your settings.py live
...
'DIRS': [
os.path.join(PROJECT_ROOT, 'templates'),
],
Here is my TEMPLATES section in settings.py:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
],
},
},
]
I'm using template inside application directory and it works. But whenever I'm trying to extend it with layout from global templates directory (os.path.join(BASE_DIR, 'templates'), which exists, I tested), django raises TemplateDoesNotExist error. Here is Template-loader postmortem, which does not includes my DIRS directory:
Django tried loading these templates, in this order:
Using loader django.template.loaders.filesystem.Loader:
Using loader django.template.loaders.app_directories.Loader:
/usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/layouts/default.html (File does not exist)
/usr/local/lib/python2.7/dist-packages/django/contrib/auth/templates/layouts/default.html (File does not exist)
/home/foundation/public/foundation/foundation/event_request/templates/layouts/default.html (File does not exist)
**UPDATE: ** seems like apache loads django 1.5 instead of django 1.8. This might be a problem.
After log entry
Using loader django.template.loaders.filesystem.Loader:
loader should print all possible locations that it tried to find your template, like it was after similar log from app_directories loader. If there is no paths here, double check if your TEMPLATES setting is defined correctly, is not overwritten somewhere later in your settings or other files and if Django is loading that settings file correctly.
You can throw some exception to get 500 server error when DEBUG is set to True, on error page you will get handy list of all settings that Django sees.
Also check if your WSGI server is loading proper version of django, older versions doesn't have TEMPLATE setting (it was splitted between multiple settings).
Problem has to do with wrong virtualenv and actual use of Django 1.5.1 instead of 1.8. I'll save this post, maybe it will help someone with similar problem in future.