.. spelling::

    Sorl

=======================
Oscar 2.0 release notes
=======================

:release: 2019-07-04

Welcome to Oscar 2.0. This is a significant release which includes a number of
new features and backwards incompatible changes. In particular the way Oscar
configures apps has been refactored and projects that have forked apps will
need to follow the section on `Migrating forked apps`_.

.. contents::
    :local:
    :depth: 1

.. _compatibility_of_2.0:

Compatibility
-------------

Oscar 2.0 is compatible with Django 1.11, Django 2.1 and Django 2.2
as well as Python 3.5, 3.6 and 3.7.

Support for Python 2.7 and Python 3.4 has been dropped.

.. _new_in_2.0:

What's new in Oscar 2.0?
------------------------

- Added an ``is_public`` field to the ``Product`` model that is used to exclude
  products from the ``browsable`` queryset. A ``browsable_dashboard`` queryset
  is provided for use in the dashboard, which includes non-public products. This
  is a model change, so you will need to run the supplied migrations.

- Added an ``order.OrderStatusChange`` model that is used to log order status
  changes applied by ``Order.set_status()``. This is a new model, so you will
  need to run the supplied migrations.

- Added an ``OSCAR_OFFERS_INCL_TAX`` setting which can be used to configure
  whether offer discounts are applied on the tax-inclusive amount. This
  defaults to ``False``, to preserve the original behaviour of discount
  application.

- Added database index definitions for commonly queried fields in a range of
  models. See `#2875`_.  This will require projects that have forked Oscar apps
  to generate corresponding migrations.

- Added a ``filter_by_attributes`` method on the ``ProductQuerySet``, to allow
  database-level filtering of products by attribute.

- Added support for re-ordering product images in the dashboard, and for adding
  an arbitrary number of additional images into the formset.

- Added an ``OSCAR_THUMBNAILER`` setting can be used to customise thumbnail
  generation. Support is included for Sorl and Easy Thumbnails. The default is
  ``'oscar.core.thumbnails.SorlThumbnail'``.

- Added ``oscar_thumbnail`` template tag (to ``image_tags``) to generate
  thumbnails in templates.  All uses of Sorl's ``thumbnail`` template tag in
  the shipped templates have been replaced with ``oscar_thumbnail``.

- ``sorl-thumbnail`` has been dropped as a dependency. Use
  ``pip install django-oscar[sorl-thumbnail]`` or
  ``pip install django-oscar[easy-thumbnails]`` to explicitly install the
  dependencies for either of the two supported thumbnailers.

- Added the ability to manage ``catalogue.Option`` objects from the dashboard.

.. _`#2875`: https://github.com/django-oscar/django-oscar/pull/2875

Removal of deprecated features
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

- Product alert emails are now sent as Communication Events and the deprecated
  product alert email templates have been removed. The templates for these
  emails have been replaced as follows:

    - ``customer/alerts/emails/confirmation_subject.txt`` to
      ``customer/emails/commtype_product_alert_confirmation_subject.txt``
    - ``customer/alerts/emails/confirmation_body.txt`` to
      ``customer/emails/commtype_product_alert_confirmation_body.txt``
    - ``customer/alerts/emails/alert_subject.txt`` to
      ``customer/emails/commtype_product_alert_subject.txt``
    - ``customer/alerts/emails/alert_body.txt`` to
      ``customer/emails/commtype_product_alert_body.txt``

- Support for category URLs without a primary key has been removed.

  Enforcement of unique slugs for categories has also been removed, as enforcing
  this was inefficient and not thread safe. Since a primary key is now required
  for category URLs, there is no need for slugs to be unique.

- ``customer.forms.SetPasswordForm`` and ``customer.forms.PasswordChangeForm``
  have been removed. Use ``django.contrib.auth.forms.SetPasswordForm`` and
  ``django.contrib.auth.forms.PasswordChangeForm`` instead.

- The ``views.decorators.staff_member_required`` decorator has been removed. Use
  ``permissions_required(['is_staff']`` instead.

- The ``UserAddress.num_orders`` property has been removed. Use
  ``num_orders_as_shipping_address`` and ``num_orders_as_billing_address``
  instead.

- Support has been removed for the dynamic loading of formset classes that were
  moved in previous releases. Projects must update their ``get_class`` calls to
  use the new paths. The paths that have changed are as follows:

  ================================  ==================================  ================
  Old path                          New path                            Affected classes
  ================================  ==================================  ================
  ``basket.forms``                  ``basket.formsets``                 ``BaseBasketLineFormSet``

                                                                        ``BasketLineFormSet``

                                                                        ``BaseSavedLineFormSet``

                                                                        ``SavedLineFormSet``
  ``dashboard.catalogue.forms``     ``dashboard.catalogue.formsets``    ``BaseStockRecordFormSet``

                                                                        ``StockRecordFormSet``

                                                                        ``BaseProductCategoryFormSet``

                                                                        ``ProductCategoryFormSet``

                                                                        ``BaseProductImageFormSet``

                                                                        ``ProductImageFormSet``

                                                                        ``BaseProductRecommendationFormSet``

                                                                        ``ProductRecommendationFormSet``

                                                                        ``ProductAttributesFormSet``

  ``wishlists.forms``               ``wishlists.formsets``              ``LineFormset``
  ================================  ==================================  ================

Minor changes
~~~~~~~~~~~~~
- Dropped ``action=""`` and ``action="."`` attributes, following the lead of
  Django and as per the HTML5 specification.

- Replaced use of Django's procedural authentication views with the corresponding
  class-based views.

- ``OrderPlacementMixin.get_message_context()`` is now passed a ``code``
  argument specifying the communication event type code for the message being
  sent.

- We've dropped the dependency on `Unidecode <https://pypi.org/project/Unidecode/>`_ due to license incompatibilities,
  ``oscar.core.utils.default_slugifier`` now uses
  ``oscar.core.utils.cautious_slugify`` to handle Unicode characters in slugs
  when ``OSCAR_SLUG_ALLOW_UNICODE`` is ``False``.

- Fixed input validation for ``dashboard.offers.forms.BenefitForm`` when a
  ``range`` was specified but other fields were empty.

- Fixed calculation of weight-based shipping charges in cases where the basket
  weight is an exact multiple of a weight band's upper limit.

- The ``sort_by`` field on ``catalogue.reviews.SortReviewsForm`` was made
  optional and the logic in ``ProductReviewList`` adjusted so that the form
  fields don't have to be rendered manually because of form errors.

- Added a ``datetime_filters`` tag library that provides a ``timedelta``
  template filter for rendering time deltas in human readable format.

- ``OSCAR_OFFER_ROUNDING_FUNCTION`` previously accepted a function as its
  value. It now only accepts a dotted path to a function as its value

- Fixed the logic of ``offers.Range.all_products()`` to make it consistent with
  ``Range.contains_product()`` in excluding products specified in
  ``excluded_product_ids``.

- Added a ``COMPARISON_FIELDS`` constant to ``catalogue.Category`` to restrict
  which fields are fetched from the database when performing category
  comparison queries.

- Significantly improved the database efficiency of the ``category_tree``
  template tag.

- Order confirmation emails now include an order status link for authenticated
  users, as well as guest users, and order status is displayed consistently in
  both logged-in and anonymous order detail views.

- Fixed display of styled HTML emails in account email detail views, wrapping
  them in an iframe to avoid leakage of styles into the page.

- Bootstrap datetime picker JS/CSS assets removed from base layout, see
  :issue:`2584`.

- Oscar's 500 error template no longer inherits other templates and does not
  use any template template tags and styling to avoid potential errors caused
  by the template itself (see :issue:`2971`).

- Line discounts are now capped to a minimum of zero - i.e., negative discounts
  will not be reported.

.. _incompatible_in_2.0:

Backwards incompatible changes in Oscar 2.0
-------------------------------------------

- Redirection to the parent detail view for child products is disabled by
  default.  Child products now have their own detail view, which allows
  displaying their price and images independently from the parent product. To
  revert to the previous behaviour of redirecting to the parent product, set
  ``oscar.apps.catalogue.views.ProductDetailView.enforce_parent`` to ``False``.

- Renamed the modules containing the Django app config classes for Oscar apps
  (apart from the ``oscar`` app), from ``config`` to ``apps``.

- Removed the ``app`` modules of Oscar apps, moving the configuration (related to
  permissions, URLconfs, and feature hiding) they contained into the apps'
  Django app config classes. They include the following attributes: ``name``
  (since renamed ``namespace``), ``login_url``, ``hidable_feature_name``,
  ``permissions_map``, and ``default_permissions``; methods:
  :meth:``get_urls``, :meth:``post_process_urls``, :meth:``get_permissions``,
  :meth:``get_url_decorator``, and :meth:``urls``; and their respective view
  classes. The composite config classes for normal Oscar apps are subclasses of
  ``oscar.core.application.OscarConfig`` (previously
  ``oscar.core.application.Application``), and for Oscar Dashboard apps
  ``oscar.core.application.OscarDashboardConfig`` (previously
  ``oscar.core.application.DashboardApplication``).

- Removed the ``application`` variable, which previously held an Oscar app
  config instance, from the Oscar app config module. A single Django/Oscar app
  config instance is now registered in the Django app registry, for each app
  label. It should be obtained by looking it up in the Django app registry.

- Changed the values returned by the Oscar app config ``urls`` property. It now
  returns a tuple containing the list of URL patterns, the app namespace (which
  could previously be None, but not any more), and the instance namespace
  (which would previously be overridden by the app namespace, if left blank, but
  must now be explicitly set). To include URLs with an instance namespace, use
  the form ``app_config.urls``, and to include URLs without an instance
  namespace, use the form ``django.conf.urls.include(app_config.urls[0])``.

- Removed ``oscar.get_core_apps``. Overriding apps is now done by
  replacing the Oscar app entry in the ``INSTALLED_APPS`` setting with that of
  the forked app.

- Changed the calling signature for the ``oscar_fork_app`` management command.
  The ``app_label`` argument is the Django app label of the app to be forked.
  ``target_path`` is the directory into which the new app shall be copied.
  ``new_app_subpackage`` is the optional dotted path to the package of the
  new app, from which, together with the ``target_path``, the full Python path
  to the app will be derived. If ``new_app_subpackage`` is omitted, then the
  package of the app being forked will be used instead.

- Removed the ``promotions`` app. The app is now available in a separate package
  - `django_oscar_promotions`_.

- ``OSCAR_MAIN_TEMPLATE_DIR`` setting has been removed and existing templates
  updated with the full path. See :issue:`1378`, :issue:`2250`. Please update
  your templates accordingly.

- ``OSCAR_SLUG_FUNCTION`` previously accepted a function as its value. It now
  only accepts a dotted path to a function as its value. Such functions must
  also now take a ``allow_unicode`` kwarg.

.. _`django_oscar_promotions`: https://github.com/django-oscar/django-oscar-promotions

.. _migrating_forked_apps:

Migrating forked apps
~~~~~~~~~~~~~~~~~~~~~

In release 2.0 the way apps are configured has been substantially refactored to
merge Oscar's ``Application`` class with Django's ``AppConfig`` class. For
each app that you have forked, you will need to:

- Rename the ``config.py`` module to ``apps.py``.

- Change the ``default_app_config`` variable in ``__.init__.py`` to point
  to ``apps`` instead of ``config``.

- Update your ``AppConfig`` subclass in the ``apps.py`` module to either
  inherit from the parent app's ``AppConfig`` or use either
  ``oscar.core.application.OscarConfig`` or
  ``oscar.core.application.OscarDashboardConfig``.

- Move any changes you've made to the Oscar ``Application`` subclass in the ``app.py``
  module, to the ``AppConfig`` subclass in the ``apps.py`` module. When moving
  the ``name`` attribute over, rename it to ``namespace``. Your merged app
  config class should end up having a ``namespace`` (for declaring the URL
  application namespace) as well as ``name`` (for configuring the
  ``django.apps.config.AppConfig.name``) attribute.

- In your project URLconf and in `get_urls` methods replace the ``application``
  import by finding the app in the Django app registry.

.. code-block:: python

    from django.apps import apps
    application = apps.get_app_config('your_app_name')

- If the urls you're including don't define an instance namespace then use
  ``include(application.urls[0])``, which only passes in the list of URL
  patterns.

  .. note::

    Because of the way dynamic class loading now works, when forking dashboard
    apps, the ``oscar.apps.dashboard`` app also needs to be forked; and the
    forked dashboard app's code must live inside the forked
    ``oscar.apps.dashboard`` app's directory.

    Similarly, when forking ``oscar.apps.catalogue.reviews``,
    ``oscar.apps.catalogue`` needs to be forked as well; and the forked
    ``oscar.apps.catalogue.reviews`` app's code must live inside the forked
    ``oscar.apps.catalogue`` app's directory.

Dependency changes
------------------

- Dropped ``mock`` as a dependency in favour of ``unittests.mock``.

- Upgraded ``bootstrap`` to version 3.4.1.

- Upgraded ``jquery`` to 3.4.1.

- Dropped ``jquery.inputmask`` in favour of ``inputmask`` and upgraded to 4.0.2.

- Upgraded ``less`` to 3.8.1.

- Upgraded ``tinymce`` to version 4.8.3.

.. _deprecated_features_in_2.0:

Deprecated features
-------------------

- ``offer.Range.contains()`` is deprecated. Use ``contains_product()`` instead.

- ``catalogue.managers.ProductManager`` is deprecated.  Use
  ``catalogue.managers.ProductQuerySet.as_manager()`` instead.

- ``catalogue.managers.BrowsableProductManager`` is deprecated.  Use
  ``Product.objects.browsable()`` instead.

- ``catalogue.Product.browsable`` is deprecated. Use
  ``Product.objects.browsable()`` instead.
