Multiple Template Engines > Updates - Funders - DEP - Campaign

Cette page n'existe qu'en anglais.

RSS

Multiple Template Engines for Django

25 janvier 2015 - Project complete!

Since I achieved all the goals laid out in my Indiegogo campaign, I'm considering this project complete. Once again I'd like to thank all the generous donors who made it possible!

This is the last post in this series. Of course, such a large refactoring creates opportunites for further improvements. If you're interested, watch for discussions on Django's usual communication channels, django-developers and django-updates.

Following the release of 1.8 alpha, I stepped back, mostly because I needed a break, but also because I wanted to get some perspective on what I done during the last four months and what the next steps should be. So here's a retrospective.

Many things went well:

A few things didn't go as planned:

I used to think that the Django Template Language was a decent template engine despite a few quirks and that it could merely use a slightly better implementation e.g. grow an actual lexer instead of a bunch of regexes. My opinion has changed. Some stories are better left untold ;-)

Don't worry, it's still fine, and it may even be slightly better than it used to. I've taken care of the pieces under the hood so you don't have to!

18 janvier 2015 - Week 15 - Wrapping up

In addition to polishing and merging changes I submitted in PR 3883 last week, I tied up some loose ends.

Throughout the project, I maintained a todo list of items to figure out later, once the bulk of the work would be done. Some got done along the way, others accumulated. After reviewing them, I pushed some relatively minor code cleanups and I filed a series of Trac tickets. Items that need changes by Django 1.8 final are tagged with the 1.8 keyword.

Collin Anderson and I already completed one of them: trimming the list of template context processors in the default TEMPLATES setting generated by the project template, according to the discussion on django-developers.

During the cleanup, I audited all uses of Engine.get_default(). As a reminder, the purpose of this method is to preserve the ability to instantiate a template with template = Template(template_code) without specifying an engine when exactly one DjangoTemplates engine is configured. Eventually it's called:

These tickets aren't critical. Some functionality isn't available when several DjangoTemplates engines are configured but I don't expect this to be a common scenario.

In the same vein, I audited all uses of engines['django']. It's used in tests to create short templates e.g. template = engines['django'].from_string("Hello"). This happens in Django's test suite, where it's fine because settings are under control, and in the tests for django.contrib.messages, where it's also fine because the TEMPLATES settings in overridden. No changes were required.

I could have used template = Template("Hello") but I prefer the more explicit version. In addition it's robust to the case where several DjangoTemplates engines are configured.

11 janvier 2015 - Week 14 - Preparing for Django 1.8 alpha

This week I focused on the items that were blocking the releasing of Django 1.8 alpha: writing the documentation and making the remaining API changes.

I completed my documentation changes and, thanks to Tim Graham's swift but thorough review, I merged PR 3845. The documentation for Django's development version now includes:

I decided that template backends must raise Django's standard TemplateSyntaxError when a template fails to compile, exactly like they raise the standard TemplateDoesNotExist when a template cannot be found. Django doesn't take advantage of this but it could be useful for third-party tools.

I deprecated passing a django.template.Context to django.template.backends.django.Template. See the previous update for details on this change.

I updated template responses to account for multiple template engines. That meant relying on the generic, backend-independent template APIs and deprecating direct use of the Django template language's APIs. There's a slightly convoluted deprecation path to change the return type of resolve_template and resolve_context because they may be overridden in subclasses.

I considered deprecating resolve_template and resolve_context. They don't seem very useful and even less so after my changes. However, I'm afraid that some developers could have subclassed TemplateResponse or SimpleTemplateResponse and overridden these methods, in an alternative — and debatable — take on class-based view. Since I'm short on time, I preserved them.

I just finished implementing all these changed. I submitted PR 3883 for review.

I'm on track with the plan I described on the django-developers mailing-list last weekend. As you can see there's still a lot of work left for making this feature nice and smooth in Django 1.8!

4 janvier 2015 - Week 13 - Writing the documentation

I put the finishing touches on PR 3770 and merged it last Sunday. It contained the bulk of the implementation of multiple template engines. After a quick self-congratulation I went back to adding the missing pieces.

The most important one is documentation. You can follow my progress in the multiple-template-engines-docs branch. Writing the documentation revealed a few mistakes; thankfully they aren't serious. The first commits of the branch fix them.

In parallel I worked on deprecating passing a django.template.Context to django.template.backends.django.Template — not to be confused with django.template.Template: the former is a wrapper around the latter. I had allowed it to make the refactoring path smoother and to avoid a hard backwards-incompatibility in the following use case:

from django.template import Context
from django.template.loader import get_template

template = get_template('hello.html')
template.render(Context({'name': 'world'}))

This get_template call returns a django.template.Template in Django 1.7 and a django.template.backends.django.Template in Django 1.8, creating a backwards-incompatibility if django.template.backends.django.Template.render doesn't accept a django.template.Context. This change is quite large because:

TemplateResponse.resolve_template and resolve_context seem rather useless after these changes but I'm going to keep them in order to minimize disruption. I've started a lot of deprecations and I'm wary of making too many changes at once.

The feature freeze for Django 1.8 is in one week. On the bright side, I landed this project two weeks before the deadline. On the negative side, I still have many side effects of the refactoring to deal with. I'm now priorizing what needs to be done by the alpha, what can wait until the beta, and what will be delayed until Django 1.9. I will most likely not have time to redesign the Origin API and certainly not to refactor internationalization in Django 1.8.

As long as you stick with the Django template system there won't be any loss of functionality in Django 1.8 compared to Django 1.7. However support for other template engines won't be completely up to par until Django 1.9.

I also updated my DEP to account for the new requirements added to DEP 1 a few weeks ago. Per the regular process, Carl Meyer submitted it to the technical board who was kind enough to approve it. Carl proceeded to merge it as DEP 182. It isn't final yet because the internationalization APIs may still change and because it's missing the "Reference Implementation" section.

28 décembre 2014 - Week 12 - Polishing the pull request

Last Sunday the multiple-template-engines branch reached a point where it could be reviewed meaningfully and I created PR 3770. During the week I integrated Tim Graham's, Carl Meyer's and Preston Timmons' feedback into that pull request.

Since I rewrite commits to keep a clean history, there's no easy way to see what changed. One change worth mentioning is the new docstring in django.template. It explains which parts of that package belong to "Multiple Template Engines" and which parts belong to the "Django Template Language".

As soon as I created the pull request, the CI server ran the test suite, revealing two problems with the tests for the Jinja2 backend:

I resolved the first problem by not even attempting to import jinja2 on Python 3.2. I thought catching ImportError and SyntaxError would suffice but it didn't work on the CI server (and I didn't try locally). Anyway, explicitly checking the Python version works. It's a bit verbose but it doesn't matter much since it only affects Django's test suite so I didn't spend more time investigating.

The second problem sent me deep into Django's autoescaping framework. I took this opportunity to improve Django's support for the __html__ convention, which provides interoperability between libraries that deal with HTML-escaped data. Since Django 1.7, objects marked as HTML-safe by Django were recognized by other libraries that support this convention. However the other way didn't work. I fixed that.

Then I wrote tests for the support of multiple template engines in django.template.loader. They can be found in the template_loader test application.

These tests revealed a bug in the implementation of select_template. When several template engines are configured and select_template is called with several template names, it will try every name with the first engine, then every name with the second engine, etc. until a match is found. In my opinion the expected behavior is to try the first name with every engine, then the second name with every engine, etc. The same bug also exists in render_to_string when it's called with a list of template names. For the time being I marked the two corresponding tests as expected failures.

It seems that fixing this bug will make the select_template function of template backends useless. Instead of calling select_template with all template names for each backend, Django will have, for each template name, to call get_template for each backend. I could make a special case when only one template engine is configured and delegate to the backend's select_template. That might be slightly more efficient on backends that optimize calls to select_template, typically with caching. It's a rather theoretical benefit and I'm not convinced it's worth having two code paths. I'm leaning towards dropping select_template from the backend API in the DEP. What do you think?

This hitch is delaying the pull request but I would still like to merge it by the end of the year.

21 décembre 2014 - Week 11 - Deprecating template-related settings

Well the title says it all. I deprecated ALLOWED_INCLUDE_ROOTS, TEMPLATE_CONTEXT_PROCESSORS, TEMPLATE_DIRS, TEMPLATE_LOADERS, and TEMPLATE_STRING_IF_INVALID.

I had already wiped them from Django except for building a backwards-compatible TEMPLATES during the deprecation period.

As explained two weeks ago I still had to update tests that overrode them. That's a lot of tests. The five commits that deprecate these five settings amount to 138 files changed, 1382 insertions, and 771 deletions. There are many more lines added than removed because overriding TEMPLATES is more verbose.

Besides I inserted some tests for template backends at the point in the branch where I added the backends.

Now I plan to add a few tests to ensure that django.template.loader dispatches to multiple template engines correctly. That's only 80 lines of straightforward code, more than half of which only provides backwards-compatibility and will be removed in Django 2.0. I'm not anticipating any problems.

Once this is done I'll try to have someone look at the branch. In my experience reviewing such a large refactoring is almost impossible. Assuming I get positive feedback I'll merge it.

Then, in order to complete this project, I'll still have to:

I jotted down some more or less related ideas while working on this project:

I can't say if they're good. We'll talk when multiple template engines are done.

14 décembre 2014 - Week 10 - Completing the basic implementation

I solved the problem I was describing last week by reordering commits and by temporarily commenting out the deprecation warning raised when a template engine is initialized based on the legacy TEMPLATE_* settings.

Then I read the DEP again and checked if I had made all the changes I planned, except for origin handling and internationalization which I'm purposefully leaving aside at this time. There were a few items left, mostly API cleanups and deprecations.

I moved built-in context processors from django.core to django.template. I updated the signature of django.shortcuts.render and django.shortcuts.render_to_response. I deprecated the current_app argument in these shortcuts and also in django.template.response.TemplateResponse.

I'm now going to focus on changes that are absolutely required to merge the multiple-template-engines branch:

  1. Review all references to the TEMPLATE_* settings — most of the remaining ones are in tests. Replace them with TEMPLATES or refactor to use an appropriate django.template.engine.Engine.
  2. Check all uses of django.template.engine.Engine.get_default(). Make changes if necessary.
  3. Write some basic tests for the multiple template engines functionality.

Since the patch is getting large — 146 changed files with 2,336 additions and 1,541 deletions at this time — I'd like to merge it as soon as possible, most likely by the end of the year.

I've documented deprecations with each commit. The DEP provides some background information for users who would like to try the new features. Therefore I think it's acceptable to write the documentation right after the feature is merged.

The current planning gives me about two weeks of headroom before the release of Django 1.8 alpha, according to the current schedule. I'll use this time to figure out a solution to problems I haven't dealt with yet.

7 décembre 2014 - Week 9 - Hitting a speed bump

This week I had to stay on top of two changes in Django's master branch that interfered with my branch:

I'm still struggling to figure out a refactoring path that allows reasonably small steps and avoids deprecation warnings. As a consequence I haven't made much progress this week. Hopefully I'll be back with better news next week!

30 novembre 2014 - Week 8 - Building the Multiple Template Engines machinery

I merged the refactor-template-engine-as-library branch last Sunday after addressing Carl Meyer's comments on PR 3605.

Then I rebased the multiple-template-engines branch on top of master. I had put it on hold to refactor the implementation of Django Templates when I was about to add the Django Templates backend. I resumed there.

It seemed easy enough but I hit surprising import errors on Python 2 after creating django/templates/backends/django.py. I had to add from __future__ import absolute_import to each submodule of django.templates.backends to prevent Python 2 from importing django.template.backends.django instead of django. Even though I've been using Python for years and I'm quite familiar with the differences between Python 2 and Python 3, it took me a bit of time to realize what was happening. If anyone needed confirmation that Python 3 gets this right, there it is.

At that point, all the template loading APIs in django.template.loader were still based on a single global instance of the Django template engine, django.template.engine.Engine.get_default(), configured with the usual settings (TEMPLATE_CONTEXT_PROCESSORS, TEMPLATE_DIRS, TEMPLATE_LOADERS, etc.) I introduced Engine.get_default() last week so as to make the refactoring incremental and auditable.

I changed Engine.get_default() to return the instance of the Django template engine if TEMPLATES defines exactly one and raise an error otherwise. I also changed several uses of the global loaders defined in django.template.loader that could be replaced by local APIs because the current engine is known at the point of invocation.

Finally I was ready to rewrite the functions in django.template.loaderget_template, select_template, and render_to_string — to stop calling Engine.get_default() and account for multiple template engines instead. Since I planned to simplify these functions' signatures, I had to deprecate some arguments at the same time. Otherwise I couldn't support both Django Templates and other backends cleanly.

If you're only using the Django Template Language, the functions in django.template.loader will still accept the same arguments in Django 1.8 and 1.9 but they'll raise a warning if you're using deprecated arguments. You have until Django 2.0 to upgrade your code. If you're using another template engine, only the new arguments are supported, although I might relax this rule later.

One change I couldn't avoid is the return type of get_template and select_template. They used to return a django.template.Template. Now they return a backend-specific Template class, depending on the backend that found and loaded the template. For instance, if it's a Django template, get_template returns a django.template.backends.django.Template which wraps the underlying django.template.Template. This matters if you're loading templates with get_template or select_template and accessing internals directly — essentially, doing anything other than calling render().

There's still one use of Engine.get_default() I can't get rid of in django.template.Template.__init__(). Let's look at a very simple example:

>>> from django.template import Template
>>> template = Template("{% extends 'base.html' %}")

In this example it appears that loading base.html will require properly configured template loaders. More generally a Template needs an Engine to be fully functional. This API doesn't provide any way to pass an engine. Since it has existed forever, I can't change it. I have no solution but to use an implicit global engine. It will be provided by Engine.get_default() according to the rules explained above.

Note how Jinja2's explicit environment avoid this problem:

>>> from jinja2 import Environment, FileSystemLoader
>>> env = Environment(loader=FileSystemLoader(TEMPLATE_DIRS))
>>> template = env.from_string("{% extends 'base.html' %}")

Finally, before someone asks — yes, I'm aware that I haven't written any documentation nor any tests yet, and yes, this stuff is less straightforward that I anticipated.

23 novembre 2014 - Week 7 - Refactoring Django templates

I merged the cleanup-template-loaders branch last Sunday after Marc Tamlyn and Tim Graham reviewed PR 3555.

In the wake of this refactoring, Preston Timmons documented the locmem template loader. It's a convenient API for defining small templates directly in Python code when writing tests.

Then I resumed working on the refactor-template-engine-as-library branch. I had to backtrack and push changes to master twice to clear the ground for this refactoring:

After getting these hindrances out of the way, I could complete the refactoring and I submitted PR 3605 which is now awaiting review. Since it's a refactoring, it doesn't add any new features. It tried to restrain myself when cleaning up code that shows its age. In some cases I added code to keep supporting undocumented behavior which some users were likely to depend on.

Overall that refactoring wasn't easy. I kept bumping into non-obvious test failures and design issues. The crux of theses issues was to pass a reference to the current Engine instance while preserving this API:

>>> from django.template import Context, Template
>>> template = Template("Hello {{ name }}!")
>>> context = Context({'name': "world"})
>>> template.render(context)
Hello world!

I added an optional keyword argument engine to the Template constructor. At this time, when engine isn't provided, a default engine is instanciated based on the settings you've always been using (TEMPLATE_DIRS, TEMPLATE_LOADERS, etc.). When I add support for multiple template engines, the default engine will be the first Django template engine configured in TEMPLATES. I may choose to raise an exception if there's more than one Django template engine configured in TEMPLATES.

The good news is that parsing never needs to access engine; only rendering does. The bad news is that there's no clean way to pass engine to nodes in the parsed tree. Node is a public API. Changing its constructor would break every custom template tag.

I settled for passing engine as an attribute of the context which is available everywhere. I don't know any good reasons for using something other than Django's standard Context and RequestContext. Even if someone subclassed them, their code should keep working. I wouldn't describe the implementation as elegant and I expect to regret it but that's the best idea I found.

I could further refactor the template engine but I think the current patch provides a good balance between cleaning the implementation and not making too many changes. Next steps might include:

16 novembre 2014 - Week 6 - Figuring out an implementation strategy

I started working on the implementation this week as planned. In terms of process, I'll rebase my work-in-progress branches on top of master and force-push them regularly. When a branch is ready, I'll make a pull request, take reviews into account, and rebase it just before merging it.

At first I tried to dive in head-first and build the infrastructure for supporting multiple template engines from the ground up. The code is in the multiple-template-engines branch.

I reached the point where I could run:

>>> from django.template import engines
>>> engine = engines['jinja2']
>>> template = engine.get_template("hello.txt")
>>> template.render({name: "world"})
Hello world!

Instant gratification! (Truth be said, that wasn't a lot of work.)

My enthusiasm faded when I started writing the backend for the Django Template Language and realized that it couldn't work until I had refactored the DTL as a standalone library not relying on global settings.

I could have ignored this problem and continued working on that branch until it had proper support for various template engines except Django's. I chose not to pursue this path because Multiple Template Engines (MTE) and the Django Template Language (DTL) both live in the django.template namespace. As a consequence it's more convenient to rework the implementation of the DTL before moving on with MTE.

I tried removing the DTL's dependency on django.conf.settings in the refactor-template-engine-as-library branch. That effort quickly hit a wall because of the rather sad state of django.template.base and django.template.loader. The implementation of the DTL was less-than-optimal by today's standards in ways that made the refactoring needlessly complex. Should I have done it anyway, it would have been very hard to review.

Up to that point I had avoided digging into the implementation of the DTL. I hoped I could simply work on the periphery. Since it was becoming clear that this strategy wouldn't work, I started a cleanup-template-loaders branch. It moves lots of code out of django.template.loader, freeing this namespace for the code loading templates from multiple engines, which is good.

That third branch is valuable regardless of the remainder of my project. I created PR 3555 and I believe it's ready for merging. Would someone be kind enough to review it?

Finally, writing code shed a new light on design decisions I made in the abstract. As a consequence I made some minor adjustments to the DEP and I'll most likely keep making more. I also received additional feedback that resulted in further improvements. None of these changes have a significant impact on the general design. They're more about the quality and the consistency of the implementation.

9 novembre 2014 - Week 5 - Getting consensus on the DEP

A healthy discussion has been taking place on the django-developers mailing-list since I announced the DEP was ready for review.

Half a dozen people have provided feedback. Considering that it can be hard to have ambitious proposals reviewed, this is excellent. I'm lucky to work with such support from the community!

I'm especially grateful to fellow core developer and technical board member Carl Meyer who wrote a thorough review and suggested almost twenty worthwhile improvements.

Feedback on the proposal has been positive overall. Comments have focused on requiring clarifications and suggesting API improvements.

I made three significant changes as a result:

I also made a few smaller adjustments and clarified many details.

I'm very happy with the result. The DEP looks much better now.

At this point I'm confident that I can start implementing it!

2 novembre 2014 - Week 4 - Finishing the DEP

Since I was on holidays this week, I could dedicate two full days to the project, which allowed me to complete the initial version of the DEP.

I listed all template APIs and reviewed all dependencies on django.template to make sure I hadn't missed something that could get in the way. (That's just as boring as it sounds.) I summarized the results in an appendix.

I realized that I hadn't dealt with template responses and template management commands. I added a paragraph about each of them.

Then I turned to the last major topic left: internationalization. It turned out to be a tough nut to crack because the current design is strongly tied to Django templates. I'll have to refactor makemessages heavily to provide appropriate extensibility.

Finally I reviewed the entire document. I corrected a few inconsistencies that I had introduced by gradually making up my mind and sometimes changing it. I hope that the current version is reasonably clear and consistent. Let me know if you spot problems.

Now my priorities for the next two weeks will be to improve the DEP according to the feedback I receive and to build consensus.

If I have time, I may start working on the implementation in parallel. At first I thought that it would be easier to refactor the Django template engine into a standalone library before building support for multiple template engines. By now I'm leaning towards implementing the infrastructure for template backends first and then fitting the Django template engine in this frame. It seems easier to start from the high-level design I just defined.

1 novembre 2014 - DEP ready for review!

Good news — the DEP is ready for public review!

I consider it to be complete and sufficient even though not every implementation detail is described. With twelve pages of content and as many of appendices, it's already long enough to test the persistence of reviewers :-)

It's complete because I've analyzed every source of relevant information I could find. Since I started working on it, I've been maintaining a list of topics to investigate. It peaked at fifteen items last week and reached zero yesterday.

It's sufficient because I've written down enough information to leave only small decisions for the implementation phase. To the best of my knowledge, I've answered every question that may impact the design significantly.

If you'd like to comment, please join the discussion on the django-developers mailing-list!

26 octobre 2014 - Week 3 - Shaping up the API

This week I worked on the API for multiple templates engines. The API includes:

I considered the following requirements, in order of decreasing importance:

  1. Security: the API must encourage secure design and implementation
  2. Simplicity: the API must be elegant, generic and straightforward
  3. Compatibility: the API must allow upgrading from the current public API

Since these requirements aren't entirely compatible with one another, I attempted to pick a reasonable and future-proof compromise. This turned out to be more difficult than I anticipated because of oddities in the design of Django templates.

One example is the existence of the Context and RequestContext classes. Most other template engines don't expose a dedicated class for rendering contexts; they accept a simple dict instead. RequestContext grew from the requirement to access common values in all templates. That requirement was fulfilled by template context processors, but that API only makes sense when handling HTTP requests.

A second example is the fact that django.shortcuts.render and django.template.response.TemplateResponse accept a current_app argument which is related to URL namespaces. This technique artificially avoids storing the current application in a global variable but sacrifices loose coupling. Unfortunately my work depends on loose coupling between the template engine and other parts of Django.

A third example is the implementation of the debug view. When TEMPLATE_DEBUG is enabled, Django displays relevant template lines in exception tracebacks. The implementation adds debug information for templates in an origin attribute and for exceptions in a django_template_source attribute. Sadly there are few comments about these attributes and the code isn't particularly straightforward.

Coupling is a recurring theme in these example. Django templates know a bit too much about HTTP requests and responses. Conversely, global state such as USE_L10N or USE_TZ is less of a concern because it's invisible in APIs. Of course, that's a problem in itself, but not one I have to deal with right now :-)

Otherwise I brought two small improvements to this website. Updates are available as a RSS feed. Code samples in the DEP gained syntax highlighting.

19 octobre 2014 - Week 2 - Reading and learning

This week I focused on learning as much as I could about Jinja2. I started by reading its documentation cover to cover. As I expected from Armin Ronacher, it's well written, precise and honest. It explains technical choices and tradeoffs in detail. I don't always agree with Armin's choices but his frankness makes it perfectly manageable. It's easy to figure out if I'm missing something or if I'm just working with different hypotheses.

To make sure I didn't miss any important idea discussed in the past, I reviewed more than 50 discussions on django-developers. Interesting ones include:

I also reviewed 40 Trac tickets or wiki pages that mention Jinja2. Here are the relevant ones:

As far as I can tell, the only serious discussion about switching to Jinja2 that ever took place was Christopher Medrela's GSoC proposal. Interestingly, in that thread, Russell Keith-Magee lays out the plan for the project I'm now tackling:

Work on the internals of Django to decouple the template engine, so that (a), Django's template language is a standalone in the same way that Jinja2 is, and/or (b) there's a clean interface so that it's possible to define a clean Jinja2 module that you can drop into your Django stack.

If anyone was under the impression I came up with innovative ideas, I'm sorry — I'm just implementing smart ideas of other people ;-)

In the same thread, Carl Meyer advocates for a more agressive approach than the one I have in mind:

I would immediately make Jinja2 the "blessed" option (i.e. the one used in the tutorial and featured in the documentation), with DTL maintained (in or out of core) only for backwards-compatibility for legacy projects.

That could be worthwhile. What do you think?

In other news, the Indiegogo campaign is over and I've completed the development of this website. Now that's out of the way, I can focus entirely on the technical aspects of the project.

Finally, I added a few paragraphs to the DEP draft and I still plan to submit it for review on week 3 or 4.

17 octobre 2014 - Campaign complete!

Thanks to your generosity, not only did the campaign reach its stretch goal, but it ended up raising twice the original target amount.

Depending on how the project goes, I will either work on a bonus feature or help managing the 1.8 release.

The credits page is live. If you funded the campaign, you can edit your funder profile.

If you're a silver, gold, platinum or diamond sponser, please follow that link to add extra information (logo, link, quote) on the credits page.

Thanks again!

12 octobre 2014 - Week 1 - Getting started with the project

While the Indiegogo campaign is still in progress, I started working on the project as soon as it was funded.

First, in order to fulfill my campaign's promises, I created the website where you're reading this first update. I'll publish a page to thank sponsors as soon as the campaign is over.

Second, I started working on the DEP. I tidied up notes I made while preparing the campaign. Here's the current status of the document:

The campaign mentioned "guidelines for third-party template engines". After working a bit on the DEP, I'm not sure there will be much content for such a section that isn't already covered in other sections, so I've removed it from the plan for now. I'll add it again if it seems useful.

To write a good technical text, I find it best to jot down a draft, wait a few days, come back with a fresh mind and work on a final version. Next week I'd like to finish the design decisions and draft the implementation plan. I hope to complete the implementation plan on week 3 or 4 and submit the DEP to django-developers then.

9 octobre 2014 - Stretch goal reached!

The full scope of the project is now funded! Thank you so much everyone!

In the mean time, I've been working on my DEP draft. I'll publish what's ready on Sunday, for my first update.

3 octobre 2014 - Project funded!

Funded in two days! Thanks everyone! You're a great community.

This secures the core of the project (steps 1 and 3). I'll start working on the DEP this week-end.

There's a stretch goal at €6000 for improving the architecture of Django templates (step 2). Let's see if we can reach it! :-)

1 octobre 2014 - Campaign launched

Head over to the project page on Indiegogo to learn more and contribute to the campaign!