=======================
How to customise an app
=======================

A core part of how oscar can be customised is to create a local version of one
of oscar's apps so that it can be modified and extended.  Creating a local
version of an app allows customisation of any of the classes within the
corresponding app in oscar.

The way this is done involves a few steps, which are detailed here.

Method
======

1. Create an app within your project with the same "app label" as an app in oscar.  Eg,
   to create a local version of ``oscar.apps.order``, create something like ``myproject.order``.

2. Ensure the ``models.py`` in your local app imports all the models from oscar's version::

   # models.py
   from oscar.apps.order.models import *

3. Replace oscar's version of the app with your new version in ``INSTALLED_APPS``.


Worked example
==============

Suppose you want to modify the homepage view class, which by default is defined in
``oscar.apps.promotions.views.HomeView``.  This view is bound to a URL within the 
``PromotionsApplication`` class in ``oscar.apps.promotions.app`` - hence we need to 
override this application class to be able to use a different view.

By default, your base ``urls.py`` should include oscar's URLs as so::

    # urls.py
    from oscar.app import application

    urlpatterns = patterns('',
        ...
        (r'', include(application.urls)),
    )

To get control over the mapping between URLs and views, you need to use a local
``application`` instance, that (optionally) subclasses oscar's.  Hence, create 
``myproject/app.py`` with contents::

    # myproject/app.py
    from oscar.app import Shop

    class BaseApplication(Shop):
        pass

    application = BaseApplication()

No customisation for now, that will come later, but you now have control over which
URLs and view functions are used.  

Now hook this up in your ``urls.py``::

    # urls.py
    from myproject.app import application

    urlpatterns = patterns('',
        ...
        (r'', include(application.urls)),
    )

The next step is to create a local app with the same name as the app you want to override::

    mkdir myproject/promotions
    touch myproject/promotions/__init__.py
    touch myproject/promotions/models.py

The ``models.py`` file should import all models from the oscar app being overridden::

    # myproject/promotions/models.py
    from oscar.apps.promotions.models import *

Now replace ``oscar.apps.promotions`` with ``myproject.promotions`` in the ``INSTALLED_APPS``
setting in your settings file.

Now create a new homepage view class in ``myproject.promotions.views`` - you can subclass
oscar's view if you like::

    from oscar.apps.promotions.views import HomeView as CoreHomeView

    class HomeView(CoreHomeView):
        template_name = 'promotions/new-homeview.html'

In this example, we set a new template location but it's possible to customise the view
in any imaginable way.

Next, create a new ``app.py`` for your local promotions app which maps your new ``HomeView``
class to the homepage URL::

    # myproject/promotions/app.py
    from oscar.apps.promotions import PromotionsApplication as CorePromotionsApplication

    from myproject.promotions.views import HomeView

    class PromotionsApplication(CorePromotionsApplication):
        home_view  = HomeView

    application = PromotionsApplication()

Finally, hook up the new view to the homepage URL::

    # myproject/app.py
    from oscar.app import Shop

    from myproject.promotions.app import application as promotions_app

    class BaseApplication(Shop):
        promotions_app = promotions_app

Quite long-winded, but once this step is done, you have lots of freedom to customise
the app in question.

Other points of note
--------------------

One pain point with replacing one of oscar's apps with a local one in ``INSTALLED_APPS`` is
that template tags are lost from the original app and need to be manually imported.  This can be
done by creating a local version of the template tags files::

    mkdir myproject/templatetags
    
and importing the tags from oscar's corresponding file::

    # myproject/promotions/templatetags/promotion_tags.py
    from oscar.apps.promotions.templatetags.promotion_tags import *

This isn't great but we haven't found a better way as of yet.