Coder Social home page Coder Social logo

fantomas42 / django-app-namespace-template-loader Goto Github PK

View Code? Open in Web Editor NEW
59.0 4.0 18.0 93 KB

Template loader allowing you to both extend and override a template at the same time.

License: BSD 3-Clause "New" or "Revised" License

Python 92.73% HTML 7.27%

django-app-namespace-template-loader's Introduction

Django App Namespace Template Loader

Build Status - develop branch Coverage of the code

Provides a template loader that allows you to load a template from a specific application. This allows you to both extend and override a template at the same time.

The default Django loaders require you to copy the entire template you want to override, even if you only want to override one small block.

This is the issue that this package tries to resolve.

Examples:

You want to change the titles of the admin site, you would originally created this template:

$ cat my-project/templates/admin/base_site.html
{% extends "admin/base.html" %}
{% load i18n %}

{% block title %}{{ title }} | My Project{% endblock %}

{% block branding %}
<h1 id="site-name">My Project</h1>
{% endblock %}

{% block nav-global %}{% endblock %}

Extend and override version with a namespace:

$ cat my-project/templates/admin/base_site.html
{% extends "admin:admin/base_site.html" %}

{% block title %}{{ title }} - My Project{% endblock %}

{% block branding %}
<h1 id="site-name">My Project</h1>
{% endblock %}

Note that in this version the block nav-global does not have to be present because of the inheritance.

Shorter version without namespace:

$ cat my-project/templates/admin/base_site.html
{% extends ":admin/base_site.html" %}

{% block title %}{{ title }} - My Project{% endblock %}

{% block branding %}
<h1 id="site-name">My Project</h1>
{% endblock %}

If we do not specify the application namespace, the first matching template will be used. This is useful when several applications provide the same templates but with different features.

Example of multiple empty namespaces:

$ cat my-project/application/templates/application/template.html
{% block content %}
<p>Application</p>
{% endblock content %}

$ cat my-project/application_extension/templates/application/template.html
{% extends ":application/template.html" %}
{% block content %}
{{ block.super }}
<p>Application extension</p>
{% endblock content %}

$ cat my-project/templates/application/template.html
{% extends ":application/template.html" %}
{% block content %}
{{ block.super }}
<p>Application project</p>
{% endblock content %}

Will render:

<p>Application</p>
<p>Application extension</p>
<p>Application project</p>

Installation

First of all install django-app-namespace-template-loader with your favorite package manager. Example :

$ pip install django-app-namespace-template-loader

Once installed, add app_namespace.Loader to the TEMPLATE_LOADERS setting of your project.

TEMPLATE_LOADERS = [
  'app_namespace.Loader',
  ... # Other template loaders
]

With Django >= 1.8 app_namespace.Loader should be added to the 'loaders' section in the OPTIONS dict of the DjangoTemplates backend instead.

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'loaders': [
                'app_namespace.Loader',
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
            ],
        },
    },
]

Note: With Django 1.8, app_namespace.Loader should be first in the list of loaders.

Known limitations

app_namespace.Loader can not work properly if you use it in conjunction with django.template.loaders.cached.Loader and inheritance based on empty namespaces.

Notes

Based originally on: http://djangosnippets.org/snippets/1376/

Requires: Django >= 1.8

Tested with Python 2.7, 3.3, 3.4.

If you want to use this application for previous versions of Django, use the version 0.3.1 of the package.

If you want to use this application with Python 2.6, use the version 0.2 of the package.

django-app-namespace-template-loader's People

Contributors

bittner avatar blueyed avatar fantomas42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

django-app-namespace-template-loader's Issues

'Loader' object has no attribute 'make_origin'

After I had to upgrade my django version to 1.8.3, I ran into this issue with zinnia: http://dpaste.com/0DPVTA5

I opened this issue here because I figured this is a problem of the Loader class of this project, and not a problem of zinnia. I tried to update all the dependencies of zinnia and django-app-namespace-template-loader with pip to the newest version, but the problem persisted.

This looks like a newer version of django expects something differently of that template loader class. I'm not really into those django internals, can you have a look at this?

Support for extending the default template without recursion

I am often switching between admin and grappelli for admin templates when debugging some template related issues (to see if it is caused by grappelli).

Then I have to change e.g. {% extends "grappelli:admin/index.html" %} to {% extends "admin:admin/change_form.html" %}.

Since django-app-namespace-template-loader appears to skip any templates which do not contain a :, I wonder if a special syntax like :admin/change_form.html could be used to extend the default template, which means to use the first one which is not the current template itself?

This would allow me to only remove/comment grappelli in INSTALLED_APPS, without modifying any templates.

Exception on second render of template when `app_namespace.Loader` is not first.

With:

TEMPLATES_DJANGO = {
    ...
    'OPTIONS': {
        ...
        'loaders': [
            'django.template.loaders.filesystem.Loader',
            'django.template.loaders.app_directories.Loader',
            'app_namespace.Loader',
        ],
    },
}

I get the following on second and subsequent request for a page that is extended with a namespace:

Traceback (most recent call last):
  File "/Users/tailee/.pyenv/versions/2.7.10/lib/python2.7/wsgiref/handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__
    return self.application(environ, start_response)
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 189, in __call__
    response = self.get_response(request)
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/core/handlers/base.py", line 218, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/core/handlers/base.py", line 261, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/views/debug.py", line 97, in technical_500_response
    html = reporter.get_traceback_html()
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/views/debug.py", line 383, in get_traceback_html
    c = Context(self.get_traceback_data(), use_l10n=False)
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/views/debug.py", line 313, in get_traceback_data
    } for t in source_list_func(str(self.exc_value))]
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/site-packages/django/views/debug.py", line 270, in format_path_status
    if not os.path.exists(path):
  File "/Users/tailee/.virtualenvs/icekit-sprint/lib/python2.7/genericpath.py", line 26, in exists
    os.stat(path)
TypeError: coercing to Unicode: need string or buffer, NamespaceOrigin found

This shows up in stdout for manage.py runserver and the browser just says A server error occurred. Please contact the administrator. even thought DEBUG is True.

I assume this error is because on the first request, the template is found by app_namespace.Loader which creates a NamespaceOrigin which is somehow cached across requests. On second request, the NamespaceOrigin is being passed to Django's loaders which expect a simple string.

If it is required that app_namespace.Loader be first, this should be documented. Although it seems like this should not be a requirement. Perhaps NamespaceOrigin objects need a __unicode__() method or something, so they can be passed to other loaders, even if the unicode value is something that will never match another loader. Or perhaps the NamespaceOrigin object shouldn't be cached across requests?

Misleading error in namespace violation(?)

See ic-labs/django-icekit#162

I assume to reproduce the error, one would override a template that extends from a template in a different app namespace.

The error can't be rendered by the technical 500 view (see stacktrace below), and shows up as a TemplateDoesNotExist: icekit_plugins_file:icekit/plugins/contact_person/default.html in an error log.

A more friendly error than 'TemplateDoesNotExist' would be helpful, but I'm not sure enough about what's going on to be able to suggest one.

  File "/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/wsgiref/handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__
    return self.application(environ, start_response)
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 189, in __call__
    response = self.get_response(request)
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 218, in get_response
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/core/handlers/base.py", line 261, in handle_uncaught_exception
    return debug.technical_500_response(request, *exc_info)
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/views/debug.py", line 97, in technical_500_response
    html = reporter.get_traceback_html()
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/views/debug.py", line 383, in get_traceback_html
    c = Context(self.get_traceback_data(), use_l10n=False)
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/views/debug.py", line 313, in get_traceback_data
    } for t in source_list_func(str(self.exc_value))]
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/lib/python2.7/site-packages/django/views/debug.py", line 270, in format_path_status
    if not os.path.exists(path):
  File "/Users/gturner/Sites/acmi/var/go.sh-venv/bin/../lib/python2.7/genericpath.py", line 18, in exists
    os.stat(path)
TypeError: coercing to Unicode: need string or buffer, NamespaceOrigin found```

Doesn't work expectantly when use {% extends %} template tag.

I write these to code to cover the default tempate:

{% extends "zinnia_bootstrap:zinnia/skeleton.html" %}
{% load staticfiles %}
{% block link %}
<link rel="stylesheet" href="{% static "test.css" %}" />
{% endblock%}

but things doesn't work, <link rel="stylesheet" href="{% static "test.css" %}" /> doesn't populate on the link block. Nothing happened.

Django 1.9 compatibility

On Django 1.9, it seems django.template.base.TemplateDoesNotExist as been removed, which cause template rendering to fail with the following stack trace :

"""Template loader for app-namespace"""
   import os
   import sys
   from importlib import import_module
   from collections import OrderedDict

   import six

   from django.conf import settings
   from django.utils._os import safe_join
   from django.utils.functional import cached_property
>   from django.template.base import TemplateDoesNotExist
E   ImportError: cannot import name 'TemplateDoesNotExist'

.tox/py34/lib/python3.4/site-packages/app_namespace/loader.py:12: ImportError

I'll make a pull request to solve this.

I think it's a bug

I installed django-blog-zinnia. in the Djang Web project folder.

$tree -L 1
├── app_namespace
├── blog
├── db.sqlite3
├── manage.py
├── __pycache__
├── six.py
├── zinnia
├── zinnia_bootstrap

then $python3 manage.py runserver
open localhost:8000/weblog, there is this infomation

AttributeError at /weblog/
'module' object has no attribute '__file__'
Request Method: GET
Request URL:    http://localhost:8000/weblog/
Django Version: 1.8.4
Exception Type: AttributeError
Exception Value:    
'module' object has no attribute '__file__'
Exception Location: /tmp/zinnia/blog/app_namespace/loader.py in app_templates_dirs, line 59
Python Executable:  /usr/bin/python3
Python Version: 3.4.3
Python Path:    
['/tmp/zinnia/blog',
 '/usr/local/lib/python3.4/dist-packages/Pillow-2.9.0-py3.4-linux-x86_64.egg',
 '/usr/lib/python3.4',
 '/usr/lib/python3.4/plat-x86_64-linux-gnu',
 '/usr/lib/python3.4/lib-dynload',
 '/usr/local/lib/python3.4/dist-packages',
 '/usr/lib/python3/dist-packages']
Server time:    Wed, 21 Oct 2015 16:11:46 +0000

But I installed django-blog-zinnia. in /usr/local/lib/python3/dist-packages/,
then I test it, it is normal.
Could you fix it?

Unexpected behaviour when using an invalid namespace.

I had a template icekit/integration/reversion/templates/admin/fluent_pages/page/change_list.html that had {% extends "icekit:admin/fluent_pages/page/change_list.html" %} at the top and which defined {% block object-tools-items %} to add a "Recover deleted pages" button.

At some point, the icekit/templates/admin/fluent_pages/page/change_list.html file was removed, because its overrides were no longer needed.

Surprisingly, the pages change list still rendered the reversion integration template, but it duplicated the contents of the object-tools-items block, so there were two "Recover deleted pages" buttons.

I tried {% extends "icekit:admin/fluent_pages/page/change_list.html" %} and even {% extends "foo:admin/fluent_pages/page/change_list.html" %} (foo being an invalid app namespace) and {% extends "admin:admin/fluent_pages/page/change_list.html" %} (admin being a valid app namespace, but not one that contained the specified template). All these exhibited the same behaviour.

Only specifying a different valid app namespace (where the template exists) worked: {% extends "fluent_pages:admin/fluent_pages/page/change_list.html" %}.

I would expect an invalid app namespace to either raise an exception (if DEBUG=True) or fail to match any templates. I would expect a valid namespace that doesn't contain the specified template to fail to match. I would expect an empty namespace to match the first app namespace that contains the template according to INSTALLED_APPS order.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.