An App Loading mechanism for Django
====================================

About Me
----------
Hi everyone,

My name is Nick Sandford, I'm an electrical engineering student at the
University of Western Australia.

Background
-----------

I haven't been a particularly active contributor to any open source project -
here is where I hope to change that. In my current work at Christ
Church Grammar School we use Django heavily for most of our internal
projects. I've
followed django-dev closely for a couple of years and I have poked around with
its internals on various occasions.

Plan
-----

Implement an improved application loading mechanism into Django.

Rationale
---------

Django current application loading code is inflexible in many cases. There
exists a need for a number of extra features to be added to the way
applications are handled such as:

  * The ability to internationalise application names.
  * The ability to customise application names similar to ``verbose_name`` in
    ``model._meta``
  * Deploy the same application multiple times in a single project.
  * Deploy two different applications with the same name.
  * Manage settings within applications.

Method
-------

I don't intend to solve the third dot point of the previous list - it seems
like a difficult problem, possibly to be tackled post-GSoC. What I do intend
to do is to move towards 'application classes' more akin to
``django.contrib.admin``.

New syntax in ``settings.INSTALLED_APPS`` will be introduced to address the
previous issues. Some examples of accepted syntax:

.. sourcecode:: python

    INSTALLED_APPS = (
        'django.contrib.auth',
        app('blog.BlogApplication', 'blog_1', 'My blog', 'app1_'),
        app('blog.BlogApplication', 'blog_2', 'Some other blog', 'app2_'),
        app(path='tagging.Tagging', verbose_name='My tagging application'),
        app('categories', db_prefix='cat_'),
        app({'path': 'django.contrib.admin.AdminApplication',
             'label': 'admin',
             'verbose_name': 'Secret Admin'}),
    )

The ``app`` function will take four arguments, three of which are optional.
These are ``path``, ``label``, ``verbose_name``, and ``db_prefix``. It will
return an instance of an ``Application`` object, which will contain all of an
installed application's information. ``path`` will be the dotted path to a
subclass of ``Application``. The downside is that ``settings.py`` requires
an import, which may be against style rules.

.. sourcecode:: python

    def app(path, label=None, verbose_name=None, db_prefix='')
        if not path or not isinstance(path, basestring):
            raise ImproperlyConfigured('Application path must be string.')
        application_class = import_module(path)
        return application_class(path, label, verbose_name, db_prefix)

``INSTALLED_APPS`` will then be a tuple containing strings or ``Application``
instances. The application loading code will iterate over ``INSTALLED_APPS``
and construct an internal cache of ``Application`` instances to be used with
``get_models``, etc. For backwards compatibility, if an element of the tuple is
a string, an instance of a base ``Application`` class will be created with sane
defaults (similar to ``app_label`` at the moment).

The ``Application`` class will be very similar to ``django.contrib.admin``'s
``AdminSite`` and will hopefully simplify application settings. If you write
views and urls directly on the ``Application`` class itself, instead of an
``from django.conf import settings`` and subsequent ``settings.API_KEY``, you
could just reference ``self.api_key`` for instance. This wouldn't be the
required, just an optional extra.

Model classes will get a ``_meta.app`` attribute, which will be an instance
of the model's ``Application`` class. Models should only be associated with one
application.

I agree with moving ``django.db.models.loading`` to ``core.apps``, since we'll
no longer require applications to have a ``models.py``. A reference to the
new functions in ``core.apps`` will still live in ``django.db.models.loading``
for backwards compatibility.

A subclass of ``Application`` might look like:

.. sourcecode:: python

    from django.views.simple import direct_to_template
    from djang.core.apps import Application
    from blog.models import Entry, Category

    class BlogApplication(Application):
        models = [Entry, Category]
        api_key = 'default'

        def entry_detail(self, slug, request,
template_name='blog/entry_detail.html'):
            entry = get_object_or_404(Entry, slug=slug)
            context = {
                'entry': entry,
                'api_key': self.api_key,
            }
            return direct_to_template(request, template_name, context)

Hurdles
--------

There are a list of possible technical issues:

 * Introducing the new ``app`` function requires an import in settings.py
   which might not be acceptable behaviour.
 * Two applications with the same label.
 * Worrying amounts of things that may affect backwards compatibility would
   probably need addressing.

Solutions
----------

We could alternatively introduce a new file into the project,
``applications.py`` which contains purely application-related setup. This might
be handy to manage application settings and to ensure ``settings.py`` doesn't
get too full of application-specific settings. This could allow us do something
like:

.. sourcecode:: python

    from django.core import apps
    from blog import BlogApplication

    class MyBlogApplication(BlogApplication):
        api_key = 'testing'

    another_blog = BlogApplication(label='blog2',
                                   verbose_name=_('Another blog'),
                                   api_key='anothertest')

    apps.register(MyBlogApplication, label='blog', verbose_name=_('My blog'))
    apps.register(another_blog)

depending on what people would like more. This could allow us not to touch
``settings.INSTALLED_APPS`` at all.

To solve the 'two applications named auth' problem, the ``AppCache`` keeps
track of application instances, not ``app_label``. For the case of two
applications with the same name, ``get_app`` should return a tuple containing
both application instances with that name. To ensure a single application
instance is returned with ``get_app``, another argument - ``path`` should be
added. ``get_models`` and ``get_model`` would take an application instance
and return models on that instance. This might affect the admin's handling of
applications.


Timeline
---------

1) Implement the ``Application`` class. -- 2 weeks
2) Move ``django.db.models.loading`` to ``django.core.apps`` and refactor
   ``get_app``, ``get_model``, etc. -- 1 week
3) Modify the admin, management, translation, templatetags, templateloaders
   to use ``app.path`` -- 1 week
4) Testing and documentation -- 3 weeks
5) Bug fixes and backwards incompatibility problems -- 1 week
6) Time permitting possibly tackle the multiple instances of the same app
   problem. (about 2 weeks).

Any feedback would be greatly appreciated, sorry for the late application and
good luck to all the other applicants :)

Thanks,
Nick

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

Reply via email to