You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

class_loading_explained.rst 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. ===============================
  2. Dynamic class loading explained
  3. ===============================
  4. Dynamic class loading is the foundation for making Oscar extensively
  5. customisable. It is hence worth understanding how it works, because most
  6. customisation depends on it.
  7. It is achieved by :meth:`oscar.core.loading.get_classes` and its
  8. single-class cousin :meth:`~oscar.core.loading.get_class`. Wherever feasible,
  9. Oscar uses ``get_classes`` instead of a regular import statement:
  10. .. code-block:: python
  11. from oscar.apps.shipping.repository import Repository
  12. is replaced by:
  13. .. code-block:: python
  14. from oscar.core.loading import get_class
  15. Repository = get_class('shipping.repository', 'Repository')
  16. .. note:: This is done for almost all classes: views, models, etc. Every class
  17. imported by ``get_class`` can be overridden.
  18. Why?
  19. ----
  20. This structure enables a project to create a local ``shipping.repository``
  21. module, and optionally subclass the class from
  22. ``oscar.apps.shipping.repository``. When Oscar tries to load the
  23. ``Repository`` class, it will load the one from your local project.
  24. This way, most classes can be overridden with minimal duplication, as only
  25. the to-be-changed classes have to be altered. They can optionally inherit from
  26. Oscar's implementation, which often amounts to little more than a few lines of
  27. custom code for changes to core behaviour.
  28. Seen on a bigger scale, this structures enables Oscar to ship with classes with
  29. minimal assumptions about the domain, and make it easy to modify behaviour as
  30. needed.
  31. How it works
  32. ------------
  33. The ``get_class`` function looks through your ``INSTALLED_APPS`` for a matching
  34. app and will attempt to load the custom class from the specified module. If the
  35. app isn't overridden or the custom module doesn't define the class, it will
  36. fall back to the default Oscar class.
  37. In practice
  38. -----------
  39. For ``get_class`` to pick up the customised class, the Oscar apps need to be
  40. forked. The process is detailed and illustrated with examples in
  41. :doc:`/topics/customisation`. It is usually enough to call ``oscar_fork_app``
  42. and replace the app in ``INSTALLED_APPS``.
  43. Using ``get_class`` in your own code
  44. ------------------------------------
  45. Generally, there is no need for ``get_class`` in your own code as the location
  46. of the module for the class is known. Some Oscar developers nonetheless
  47. use ``get_class`` when importing classes from Oscar. This means that if someday
  48. the class is overridden, it will not require code changes. Care should be taken
  49. when doing this, as this is a tricky trade-off between maintainability and
  50. added complexity.
  51. Please note that we cannot recommend ever using ``get_model`` in your own code.
  52. Model initialisation is a tricky process and it's
  53. easy to run into circular import issues.
  54. Overriding dynamic class loading behaviour
  55. ------------------------------------
  56. In some cases it may be necessary to customise the logic used by Oscar to
  57. dynamically load classes. You can do this by supplying your own class loader
  58. function to the ``OSCAR_DYNAMIC_CLASS_LOADER`` setting:
  59. .. code-block:: python
  60. OSCAR_DYNAMIC_CLASS_LOADER = 'myproject.custom_class_loader'
  61. Supply a dotted Python path to a callable that takes
  62. the same arguments as :meth:`~oscar.core.loading.default_class_loader`.
  63. Testing
  64. -------
  65. You can test whether your overriding worked by trying to get a class from your
  66. module:
  67. .. code-block:: python
  68. >>> from oscar.core.loading import get_class
  69. >>> get_class('shipping.repository', 'Repository')
  70. yourproject.shipping.repository.Repository # it worked!