This commit mostly abstracts away from the app refactor that happened in
Django 1.7. It is modelled after recommendations by @aaugustin.
It also introduces a new model_registered check, which can be used
during initialisation to safely register Oscar's models only if they
haven't been registered by a fork app before. This is necessary as
Django 1.7 enforces that models are only registered once; previously the
second declaration would just be ignored silently. That is why e.g.
"from oscar.apps.catalogue.models import *" would work even if one
declared an own catalogue model.
get_classes: Allow importing from any module depth
Previously, get_classes could only import from modules one level below
the INSTALLED_APPS entry. This wasn't a problem so far, but is needed to
when Oscar's Application instances switch to using get_class to load the
views.
This allows importing a class or attribute by the reasonably common
string notation, which is useful for e.g. settings. When using settings,
we should prefer absolute imports over using get_class, because that
limits us to modules in INSTALLED_APPS.
Rename and move Scales class, and make it overridable
Making it overridable allows altering the weighing method of the basket,
which I wanted to do in a project. As get_classes doesn't support
importing from top-level products, it had to be moved to a scales
module.
Other class names tend to use singular, so as the imports had to be
changed anyway, Scales was renamed to Scale.
I investigated for a few minutes if it's possible to get
get_class('shipping', 'Scales') working. oscar_module loads correctly,
but any overriding local_module was missing classes defined therein.
Just documenting this limitation explicitly instead of tripping at the
ValueError when trying to split.
Issue #1277 correctly pointed out that Oscar's latest released version
has an issue when trying to override dashboard apps. Hopefully that is
fixed by just backporting the changes to get_classes that have happened
since.
Remove difference between _import_oscar_module and _import_local_module
If an oscar module fails, it should equally be reported instead of being silenced.
For example, an the Oscar module could import an overwritten class that fails.
Before the fix in e97ec27c95, the new
get_classes threw an AppNotFoundError whereas get_classes prior to the
refactor did not.
The condition is that neither the local module nor the Oscar module
could be imported. Our reasoning is that this is an issue with circular
imports. Before the refactor of get_classes, not being able to import
the Oscar in the shortcut path (beginning of function) would raise an
ImportError; which is then masked by a wrapping get_classes call. Hence,
the circular import is hidden.
This fix highlights the condition explicitly by raising an error that is
intentionally not an ImportError.
Investigation following a mailing list thread
(https://groups.google.com/d/msgid/django-oscar/a1489359-cfa6-44dc-90d8-2958be9e88a1%40googlegroups.com)
highlighted that an overriding app can't be a root app, e.g. overriding
'oscar.dashboard' with an app called 'dashboard' did not work.
In the past, there's been a lot of subtle issues surrounding the
get_classes code. Hence, some effort was expended to make the logic more
readable and fix the issue. Docstrings have been expanded, comments have
been written and private methods have been renamed.
Fix for get_classes when used with non-Oscar modules
get_classes allows imported_oscar_module to be None.
imported_oscar_module ends up in modules in _pluck_classes, however the
code is not prepared to handle None at that place.
Fixes #1160.
Thanks to @mberthau for reporting and supplying fix.
Django's get_model returns None if the import fails, which causes subtle
bugs further down the line. This change introduces a wrapper around it
which raises an ImportError instead, hence catching the error as early
as possible.
The code assumed that an application instance and it's views have the
same parent module. This is not necessarily true, as one can customise
either just a view or just an app.
The fix consists of always using get_class to load the application
instance.
@mberthau pointed out the issue and provided a first fix in #993 and #1034.
Introduces the concept of a "feature" that can be hidden. A list of
features to hide can be supplied in OSCAR_HIDDEN_FEATURES.
Feature hiding is currently supported in two places.
Application instances can carry a "hidable_feature_name". If set and
found in OSCAR_HIDDEN_FEATURES, post_process_urls will return an empty
set of url patterns, hence disabling relevant views.
display_tags also has a new template block tag iffeature.
{% iffeature "feature" %}xxx{% endiffeature %} will hide everything
inside the block if the feature is hidden.