Git Product home page Git Product logo

django-overextends's Introduction

image

Created by Stephen McDonald

Introduction

A Django reusable app providing the overextends template tag, a drop-in replacement for Django's extends tag, which allows you to use circular template inheritance.

The primary use-case for overextends is to simultaneously override and extend templates from other reusable apps, in your own Django project.

Example

Consider the following settings module and templates, with the apps app1 and app2 bundled in the project, for example's sake:

# settings.py
INSTALLED_APPS = (
    "app1",
    "app2",
    "overextends",
)
TEMPLATE_LOADERS = (
    "django.template.loaders.filesystem.Loader",
    "django.template.loaders.app_directories.Loader",
)
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_DIRS = (os.path.join(PROJECT_ROOT, "templates"),)

<!-- myproject/app1/templates/pages/page.html -->
<h1>Title</h1>
{% block main %}
<p>A paragraph in app1</p>
{% enblock %}
<footer>Copyright 2012</footer>

<!-- myproject/app2/templates/pages/page.html -->
{% overextends "pages/page.html" %}
{% block main %}
<p>A paragraph in app2, that wants to be on top of app1's main block</p>
{{ block.super }}
{% enblock %}

<!-- myproject/templates/pages/page.html -->
{% overextends "pages/page.html" %}
{% block main %}
{{ block.super }}
<p>A paragraph in the project's template directory, under the other main blocks</p>
{% enblock %}

The resulting HTML rendered when pages/page.html was loaded would be:

<h1>Title</h1>
<p>A paragraph in app2, that wants to be on top of app1's main block</p>
<p>A paragraph in app1</p>
<p>A paragraph in the project's template directory, under the other main blocks</p>
<footer>Copyright 2012</footer>

For a detailed analysis of why you would use this approach, how it works, and alternative approaches, read my initial blog post: Circular Template Inheritance for Django

Installation

The easiest way to install django-overextends is directly from PyPi using pip by running the following command:

$ pip install -U django-overextends

Otherwise you can download django-overextends and install it directly from source:

$ python setup.py install

Project Configuration

Once installed you can configure your project to use django-overextends by adding the overextends app to the INSTALLED_APPS in your project's settings module:

INSTALLED_APPS = (
    # ... other apps here ...
    'overextends',
)

For Django 1.9+ you must add overextends to the builtins key of your TEMPLATES setting:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'APP_DIRS': True,
        'OPTIONS': {
            'builtins': ['overextends.templatetags.overextends_tags'],
        }
    },
]

Note that while the overextends tag is provided by the package overextends.templatetags.overextends_tags, it is unnecessary to use {% load overextends_tags %} in your templates. Like the extends tag, overextends must be the first tag in your template, so it is automatically added to Django's built-in template tags, removing the need to load its tag library in each template.

django-overextends's People

Contributors

aaugustin avatar al45tair avatar dwaynebailey avatar stefanw avatar stephenmcd avatar tomkins avatar vinnyrose 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  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  avatar  avatar  avatar  avatar

django-overextends's Issues

Custom inheritance order?

Not sure if this is an issue or I'm just not seeing it.

I've got 3 apps that inhert from each other in the database

BaseType #template basetype/details.html doesn't extend anything
Type1(BaseType) #template basetype/details.html overextends basetype/details.html
SubType1(Type1) #template basetype/details.html overextends basetype/details.html

Fails DB creation, works for overextends
INSTALLED_APPS (
SubType1,
Type1,
BaseType, )

Works for DB creation, fails for overextends. Will only see the template from BaseType
INSTALLED_APPS (
BaseType,
SubType1,
Type1, )

Perhaps this is an odd case since the URL I load is for BaseType. BaseType is constructed in such a way as to use model-utils.InhertianceManager and load the correct subclass model for the display.

Support Django 1.7 / 1.8

Test failing on 1.7 and 1.8:

ERROR: test_overextends (overextends.tests.Tests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./../overextends/tests.py", line 93, in test_overextends
    html = get_template(self.unique_id).render(Context())
  File "/Library/Python/2.7/site-packages/django/template/base.py", line 148, in render
    return self._render(context)
  File "/Library/Python/2.7/site-packages/django/test/utils.py", line 88, in instrumented_test_render
    return self.nodelist.render(context)
  File "/Library/Python/2.7/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/Library/Python/2.7/site-packages/django/template/base.py", line 858, in render_node
    return node.render(context)
  File "/Library/Python/2.7/site-packages/django/template/loader_tags.py", line 126, in render
    return compiled_parent._render(context)
  File "/Library/Python/2.7/site-packages/django/test/utils.py", line 88, in instrumented_test_render
    return self.nodelist.render(context)
  File "/Library/Python/2.7/site-packages/django/template/base.py", line 844, in render
    bit = self.render_node(node, context)
  File "/Library/Python/2.7/site-packages/django/template/base.py", line 858, in render_node
    return node.render(context)
  File "/Library/Python/2.7/site-packages/django/template/loader_tags.py", line 104, in render
    compiled_parent = self.get_parent(context)
  File "./../overextends/templatetags/overextends_tags.py", line 120, in get_parent
    template = self.find_template(parent, context)
  File "./../overextends/templatetags/overextends_tags.py", line 103, in find_template
    context[context_name][name].remove(remove_path)
ValueError: list.remove(x): x not in list

Overextends skips the template with different filename even when it would not create a cycle

I would like to use overextends everywhere instead of extends, so that I (and other contributors) do not have to think about which one to use where. But this is not really possible because if I have a template named user.html which uses {% overextends "base.html" %}, first base.html in the inheritance chain is unnecessary skipped. If I replace overextends with extends then things work correctly. But it would be great if overextends would simply behave like extends in this case.

AttributeError: 'unicode' object has no attribute 'resolve'

File "/production/environments/thenewstyle/lib/python2.6/site-packages/overextends/templatetags/overextends_tags.py", line 106, in get_parent
template.nodelist[0].parent_name.resolve(context) == parent):

AttributeError: 'unicode' object has no attribute 'resolve'

Django 1.3.2
Python 2.6

Merging with app-namespace-template-loader?

I just stumbled across this project, and noticed that it's quite similar to https://github.com/Fantomas42/django-app-namespace-template-loader, or at least to the pull request I've submitted recently to add support for over-extending (Fantomas42/django-app-namespace-template-loader#2).

Does it make sense to merge these and/or add namespace support to django-overextends, or should these features (explicit namespace addressing and over-extending) be kept separate?

overextending more than three templates not working

on my system overextend is not working when overextending more than three templates.
django version: 1.4

steps to reproduce:

  • in tests.py line 46, use range(3) instead of range(2)
  • run the tests

additional insights:

  • if i change range(3) to range(80) and add 'print html' in the test method, i can observe that every second app directory is skipped.

I have tried to fix the bug but i wasn't able to locate the problem.
As i needed a solution rather quickly, i have forked your repo and added another tag called superimpose that essentially has the same function as overextends. It assumes that the overextended template has the same name as the current template, which is sufficient for my use-case. To get it working, i implemented a custom template loader. I have not completely tested it, but it seems to be working so far. Maybe, we can work out a solution that would enable overextending another template and also allow to omit the argument to overextends which would then default to the template_name of the current template.

btw: i have called the tag 'superimpose' as it essentially enables FST superimposition on django templates.
If you are interested, you can find more info on that in this PDF: http://www.infosun.fim.uni-passau.de/cl/publications/docs/SC2008.pdf

Finally, i want to thank you for creating django-overextends! It really rocks!

There can be valid tags before ExtendsNode

In Django templates, text nodes can be legally before ExtendsNode. So checking just for ExtendsNode in the first array elements silently break things.

Text nodes can happen easily if you have comments before extends, like:

{# This template is for this and this. #}

{% extends "base.html" %}

It is true that putting such comment will give you some empty space before the content, but in some non-HTML templates or HTML fragment templates this might not be a problem.

Update readme to reflect that django 'extends' now natively supports this feature.

It appears that Django does now support circular inheritance. Not sure when it was introduced (believe 1.10?), but testing version 1.11 using the extends tag on a template of the same name now works correctly and allows me to selectively override+extend that template in the same way that overextends does.

It would probably be good to add a note to the readme and pypi docs to reflect that, and suggest a migration path so that when users migrate to Django 1.11 (which they need to soon because of 1.8 LTS ending early next year), they can stop using this library.

After of course testing that the expected functionality is now in Django, otherwise if this library still does anything usefully different it would be worth noting that, otherwise you can probably issue a deprecation warning and drop support for newer releases of Django after 1.11 LTS.

Now if only the Django docs themselves were actually clear about the ability to do circular inheritance with a section that looked like your readme...

That said, thank you for this library! This was an amazingly useful thing for ages!

Test fails

traceback:

======================================================================
ERROR: test_overextends (overextends.tests.Tests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/overextends/tests.py", line 93, in test_overextends
    html = get_template(self.unique_id).render(Context())
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/django/template/base.py", line 123, in render
    return self._render(context)
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/django/test/utils.py", line 60, in instrumented_test_render
    return self.nodelist.render(context)
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/django/template/base.py", line 744, in render
    bits.append(self.render_node(node, context))
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/django/template/debug.py", line 73, in render_node
    result = node.render(context)
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/django/template/loader_tags.py", line 105, in render
    compiled_parent = self.get_parent(context)
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/overextends/templatetags/overextends_tags.py", line 104, in get_parent
    template = self.find_template(parent, context)
  File "/var/lib/[...]PYTHON/Python2.7/env/local/lib/python2.7/site-packages/overextends/templatetags/overextends_tags.py", line 87, in find_template
    context[context_name][name].remove(remove_path)
TemplateSyntaxError: Caught ValueError while rendering: list.remove(x): x not in list

tested with overextends 3.0, Django 1.3.7 and Python 2.7.3

TemplateDoesNotExist in complex cases

I have found the issue in some complex cases when "extended-overriden" template contains "extended-overriden" includes. For example this template set causes TemplateDoesNotExist exception when looking for entry.txt template (while rendering index.txt):

project/app/templates/base.txt:

{% block header %}
Header.
{% endblock %}

{% block content %}
{% endblock %}

project/app/templates/index.txt:

{% extends 'base.txt' %}

{% block content %}
    {% for i in '123' %}
        {{ i }}
        {% include 'entry.txt' %}
    {% endfor %}
{% endblock %}

project/app/templates/entry.txt:

{% block name %}Entry{% endblock %} #{{ i }}

project/templates/entry.txt:

{% overextends 'base.txt' %}

{% block header %}MyEntry{% endblock %}

project/templates/entry.txt:

{% overextends 'entry.txt' %}

{% block name %}MyEntry{% endblock %}

Sometimes (depending on settings I suppose) instead of throwing an exception it renders but without "MyEntry" in third time.

If you change 123 to 12 in the loop, rendering will be successful. I don't know exactly why it fails at third iteration and not on second, but I have found a fix for this. Also it should address many other cases when such a glitch can happen. I will add a PR for this.

Is this behavior now part of Django?

I cannot find any reference to it (probably haven't looked hard enough), but in practice it looks like Django automatically finds the "overextended" template by default. At least on 1.10. I am sure that a few years ago it was not the case, I needed django-overexdends for that.

Are there any cases where this app still provides a benefit?

Django 1.9 support

Tests are currently failing:

Creating test database for alias 'default'...
./../overextends/tests.py:94: RemovedInDjango110Warning: render() must be called with a dict, not a Context.
  html = get_template(self.unique_id).render(Context())

F
======================================================================
FAIL: test_overextends (overextends.tests.Tests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./../overextends/tests.py", line 97, in test_overextends
    self.assertTrue(test_string in html)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 1 test in 0.038s

FAILED (failures=1)

Declare sphinx-me as an extra dependency

Hello,

Most users of django-overextends don't need sphinx-me because they won't build the documentation.

https://packaging.python.org/requirements/#install-requires states that install_requires "should be used to specify what a project minimally needs to run correctly".

https://github.com/pypa/sampleproject/blob/cec59671175bc2c86563ac80c90ba28b068a1335/setup.py#L78-L91 suggests that you could put sphinx-me in extras_require instead of install_requires.

Thanks!

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.