Repository: django-oscar/django-oscar

Issue:
Confusing output of `oscar_fork_app` management command
Sample output of `oscar_fork_app`:

```bash
> ./manage.py oscar_fork_app order apps
Creating package apps/order

Creating admin.py

Creating app config

Creating models.py

Creating migrations folder

Replace the entry 'oscar.apps.order.apps.OrderConfig' with 'apps.order.apps.OrderConfig' in INSTALLED_APPS
```

But there is no entry like `'oscar.apps.order.apps.OrderConfig'` in `INSTALLED_APPS`.
Based on [Oscar docs](https://django-oscar.readthedocs.io/en/2.0.0/internals/getting_started.html#django-settings) there is only `'oscar.apps.order'`.

Probably instead of `app_config_class_path` here:
https://github.com/django-oscar/django-oscar/blob/e347ad9ed052e82eb275d3bb32500f3f5f225007/src/oscar/core/customisation.py#L122-L129
we should use `app_config.name`.

I can prepare a PR to change the current behavior. Please comment. Thanks.


Discussion:
I would prefer that we update the documentation on setting up a project to use the explicit app configs in the settings (i.e., the `INSTALLED_APPS` should contain `oscar.apps.order.apps.OrderConfig`), which is the recommended approach by Django. This will then make the output of the command correct.
@solarissmoke Thanks!

I will create a PR to update the documentation then.

Resolving patch:
diff --git a/sandbox/settings.py b/sandbox/settings.py
--- a/sandbox/settings.py
+++ b/sandbox/settings.py
@@ -257,35 +257,35 @@
     'django.contrib.sites',
     'django.contrib.flatpages',
 
-    'oscar',
-    'oscar.apps.analytics',
-    'oscar.apps.checkout',
-    'oscar.apps.address',
-    'oscar.apps.shipping',
-    'oscar.apps.catalogue',
-    'oscar.apps.catalogue.reviews',
-    'oscar.apps.partner',
-    'oscar.apps.basket',
-    'oscar.apps.payment',
-    'oscar.apps.offer',
-    'oscar.apps.order',
-    'oscar.apps.customer',
-    'oscar.apps.search',
-    'oscar.apps.voucher',
-    'oscar.apps.wishlists',
-    'oscar.apps.dashboard',
-    'oscar.apps.dashboard.reports',
-    'oscar.apps.dashboard.users',
-    'oscar.apps.dashboard.orders',
-    'oscar.apps.dashboard.catalogue',
-    'oscar.apps.dashboard.offers',
-    'oscar.apps.dashboard.partners',
-    'oscar.apps.dashboard.pages',
-    'oscar.apps.dashboard.ranges',
-    'oscar.apps.dashboard.reviews',
-    'oscar.apps.dashboard.vouchers',
-    'oscar.apps.dashboard.communications',
-    'oscar.apps.dashboard.shipping',
+    'oscar.config.Shop',
+    'oscar.apps.analytics.apps.AnalyticsConfig',
+    'oscar.apps.checkout.apps.CheckoutConfig',
+    'oscar.apps.address.apps.AddressConfig',
+    'oscar.apps.shipping.apps.ShippingConfig',
+    'oscar.apps.catalogue.apps.CatalogueConfig',
+    'oscar.apps.catalogue.reviews.apps.CatalogueReviewsConfig',
+    'oscar.apps.partner.apps.PartnerConfig',
+    'oscar.apps.basket.apps.BasketConfig',
+    'oscar.apps.payment.apps.PaymentConfig',
+    'oscar.apps.offer.apps.OfferConfig',
+    'oscar.apps.order.apps.OrderConfig',
+    'oscar.apps.customer.apps.CustomerConfig',
+    'oscar.apps.search.apps.SearchConfig',
+    'oscar.apps.voucher.apps.VoucherConfig',
+    'oscar.apps.wishlists.apps.WishlistsConfig',
+    'oscar.apps.dashboard.apps.DashboardConfig',
+    'oscar.apps.dashboard.reports.apps.ReportsDashboardConfig',
+    'oscar.apps.dashboard.users.apps.UsersDashboardConfig',
+    'oscar.apps.dashboard.orders.apps.OrdersDashboardConfig',
+    'oscar.apps.dashboard.catalogue.apps.CatalogueDashboardConfig',
+    'oscar.apps.dashboard.offers.apps.OffersDashboardConfig',
+    'oscar.apps.dashboard.partners.apps.PartnersDashboardConfig',
+    'oscar.apps.dashboard.pages.apps.PagesDashboardConfig',
+    'oscar.apps.dashboard.ranges.apps.RangesDashboardConfig',
+    'oscar.apps.dashboard.reviews.apps.ReviewsDashboardConfig',
+    'oscar.apps.dashboard.vouchers.apps.VouchersDashboardConfig',
+    'oscar.apps.dashboard.communications.apps.CommunicationsDashboardConfig',
+    'oscar.apps.dashboard.shipping.apps.ShippingDashboardConfig',
 
     # 3rd-party apps that Oscar depends on
     'widget_tweaks',
diff --git a/src/oscar/__init__.py b/src/oscar/__init__.py
--- a/src/oscar/__init__.py
+++ b/src/oscar/__init__.py
@@ -29,35 +29,35 @@ def get_version():
     'django.contrib.sites',
     'django.contrib.flatpages',
 
-    'oscar',
-    'oscar.apps.analytics',
-    'oscar.apps.checkout',
-    'oscar.apps.address',
-    'oscar.apps.shipping',
-    'oscar.apps.catalogue',
-    'oscar.apps.catalogue.reviews',
-    'oscar.apps.partner',
-    'oscar.apps.basket',
-    'oscar.apps.payment',
-    'oscar.apps.offer',
-    'oscar.apps.order',
-    'oscar.apps.customer',
-    'oscar.apps.search',
-    'oscar.apps.voucher',
-    'oscar.apps.wishlists',
-    'oscar.apps.dashboard',
-    'oscar.apps.dashboard.reports',
-    'oscar.apps.dashboard.users',
-    'oscar.apps.dashboard.orders',
-    'oscar.apps.dashboard.catalogue',
-    'oscar.apps.dashboard.offers',
-    'oscar.apps.dashboard.partners',
-    'oscar.apps.dashboard.pages',
-    'oscar.apps.dashboard.ranges',
-    'oscar.apps.dashboard.reviews',
-    'oscar.apps.dashboard.vouchers',
-    'oscar.apps.dashboard.communications',
-    'oscar.apps.dashboard.shipping',
+    'oscar.config.Shop',
+    'oscar.apps.analytics.apps.AnalyticsConfig',
+    'oscar.apps.checkout.apps.CheckoutConfig',
+    'oscar.apps.address.apps.AddressConfig',
+    'oscar.apps.shipping.apps.ShippingConfig',
+    'oscar.apps.catalogue.apps.CatalogueConfig',
+    'oscar.apps.catalogue.reviews.apps.CatalogueReviewsConfig',
+    'oscar.apps.partner.apps.PartnerConfig',
+    'oscar.apps.basket.apps.BasketConfig',
+    'oscar.apps.payment.apps.PaymentConfig',
+    'oscar.apps.offer.apps.OfferConfig',
+    'oscar.apps.order.apps.OrderConfig',
+    'oscar.apps.customer.apps.CustomerConfig',
+    'oscar.apps.search.apps.SearchConfig',
+    'oscar.apps.voucher.apps.VoucherConfig',
+    'oscar.apps.wishlists.apps.WishlistsConfig',
+    'oscar.apps.dashboard.apps.DashboardConfig',
+    'oscar.apps.dashboard.reports.apps.ReportsDashboardConfig',
+    'oscar.apps.dashboard.users.apps.UsersDashboardConfig',
+    'oscar.apps.dashboard.orders.apps.OrdersDashboardConfig',
+    'oscar.apps.dashboard.catalogue.apps.CatalogueDashboardConfig',
+    'oscar.apps.dashboard.offers.apps.OffersDashboardConfig',
+    'oscar.apps.dashboard.partners.apps.PartnersDashboardConfig',
+    'oscar.apps.dashboard.pages.apps.PagesDashboardConfig',
+    'oscar.apps.dashboard.ranges.apps.RangesDashboardConfig',
+    'oscar.apps.dashboard.reviews.apps.ReviewsDashboardConfig',
+    'oscar.apps.dashboard.vouchers.apps.VouchersDashboardConfig',
+    'oscar.apps.dashboard.communications.apps.CommunicationsDashboardConfig',
+    'oscar.apps.dashboard.shipping.apps.ShippingDashboardConfig',
 
     # 3rd-party apps that oscar depends on
     'widget_tweaks',


Test patch:
diff --git a/tests/integration/core/test_customisation.py b/tests/integration/core/test_customisation.py
--- a/tests/integration/core/test_customisation.py
+++ b/tests/integration/core/test_customisation.py
@@ -105,7 +105,7 @@ def tearDown(self):
     def test_fork_third_party(self):
         tmpdir = tempfile.mkdtemp()
         installed_apps = list(settings.INSTALLED_APPS)
-        installed_apps.append('thirdparty_package.apps.myapp')
+        installed_apps.append('thirdparty_package.apps.myapp.apps.MyAppConfig')
         with override_settings(INSTALLED_APPS=installed_apps):
             customisation.fork_app('myapp', tmpdir, 'custom_myapp')
             forked_app_dir = join(tmpdir, 'custom_myapp')
diff --git a/tests/integration/core/test_loading.py b/tests/integration/core/test_loading.py
--- a/tests/integration/core/test_loading.py
+++ b/tests/integration/core/test_loading.py
@@ -50,7 +50,8 @@ class in the core app. We use this fictitious class because classes in
         triggered when we override the INSTALLED_APPS setting.
         """
         apps = list(settings.INSTALLED_APPS)
-        apps[apps.index('tests._site.apps.catalogue')] = 'tests._site.import_error_app.catalogue'
+        replaced_app_idx = apps.index('tests._site.apps.catalogue.apps.CatalogueConfig')
+        apps[replaced_app_idx] = 'tests._site.import_error_app.catalogue.apps.CatalogueConfig'
         with override_settings(INSTALLED_APPS=apps):
             with self.assertRaises(ImportError):
                 get_class('catalogue.import_error_module', 'ImportErrorClass')
@@ -60,7 +61,8 @@ class ClassLoadingWithLocalOverrideTests(TestCase):
 
     def setUp(self):
         self.installed_apps = list(settings.INSTALLED_APPS)
-        self.installed_apps[self.installed_apps.index('oscar.apps.shipping')] = 'tests._site.apps.shipping'
+        replaced_app_idx = self.installed_apps.index('oscar.apps.shipping.apps.ShippingConfig')
+        self.installed_apps[replaced_app_idx] = 'tests._site.apps.shipping.apps.ShippingConfig'
 
     def test_loading_class_defined_in_local_module(self):
         with override_settings(INSTALLED_APPS=self.installed_apps):
@@ -87,8 +89,8 @@ def test_loading_classes_with_root_app(self):
         import tests._site.shipping
         path = dirname(dirname(tests._site.shipping.__file__))
         with temporary_python_path([path]):
-            self.installed_apps[
-                self.installed_apps.index('tests._site.apps.shipping')] = 'shipping'
+            replaced_app_idx = self.installed_apps.index('tests._site.apps.shipping.apps.ShippingConfig')
+            self.installed_apps[replaced_app_idx] = 'shipping.apps.ShippingConfig'
             with override_settings(INSTALLED_APPS=self.installed_apps):
                 (Free,) = get_classes('shipping.methods', ('Free',))
                 self.assertEqual('shipping.methods', Free.__module__)
@@ -108,7 +110,8 @@ class ClassLoadingWithLocalOverrideWithMultipleSegmentsTests(TestCase):
 
     def setUp(self):
         self.installed_apps = list(settings.INSTALLED_APPS)
-        self.installed_apps[self.installed_apps.index('oscar.apps.shipping')] = 'tests._site.apps.shipping'
+        replaced_app_idx = self.installed_apps.index('oscar.apps.shipping.apps.ShippingConfig')
+        self.installed_apps[replaced_app_idx] = 'tests._site.apps.shipping.apps.ShippingConfig'
 
     def test_loading_class_defined_in_local_module(self):
         with override_settings(INSTALLED_APPS=self.installed_apps):
@@ -160,14 +163,14 @@ def tearDown(self):
         sys.path.remove('./tests/_site/')
 
     def test_load_core_3rd_party_class_correctly(self):
-        self.installed_apps.append('thirdparty_package.apps.myapp')
+        self.installed_apps.append('thirdparty_package.apps.myapp.apps.MyAppConfig')
         with override_settings(INSTALLED_APPS=self.installed_apps):
             Cow, Goat = get_classes('myapp.models', ('Cow', 'Goat'), self.core_app_prefix)
             self.assertEqual('thirdparty_package.apps.myapp.models', Cow.__module__)
             self.assertEqual('thirdparty_package.apps.myapp.models', Goat.__module__)
 
     def test_load_overriden_3rd_party_class_correctly(self):
-        self.installed_apps.append('tests._site.apps.myapp')
+        self.installed_apps.append('tests._site.apps.myapp.apps.TestConfig')
         with override_settings(INSTALLED_APPS=self.installed_apps):
             Cow, Goat = get_classes('myapp.models', ('Cow', 'Goat'), self.core_app_prefix)
             self.assertEqual('thirdparty_package.apps.myapp.models', Cow.__module__)
diff --git a/tests/integration/test_settings.py b/tests/integration/test_settings.py
--- a/tests/integration/test_settings.py
+++ b/tests/integration/test_settings.py
@@ -6,4 +6,4 @@ class TestOscarInstalledAppsList(TestCase):
 
     def test_includes_oscar_itself(self):
         installed_apps = settings.INSTALLED_APPS
-        self.assertTrue('oscar' in installed_apps)
+        self.assertTrue('oscar.config.Shop' in installed_apps)
diff --git a/tests/settings.py b/tests/settings.py
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -27,35 +27,35 @@
     'django.contrib.sites',
     'django.contrib.flatpages',
 
-    'oscar',
-    'oscar.apps.analytics',
-    'oscar.apps.checkout',
-    'oscar.apps.address',
-    'oscar.apps.shipping',
-    'oscar.apps.catalogue',
-    'oscar.apps.catalogue.reviews',
-    'oscar.apps.partner',
-    'oscar.apps.basket',
-    'oscar.apps.payment',
-    'oscar.apps.offer',
-    'oscar.apps.order',
-    'oscar.apps.customer',
-    'oscar.apps.search',
-    'oscar.apps.voucher',
-    'oscar.apps.wishlists',
-    'oscar.apps.dashboard',
-    'oscar.apps.dashboard.reports',
-    'oscar.apps.dashboard.users',
-    'oscar.apps.dashboard.orders',
-    'oscar.apps.dashboard.catalogue',
-    'oscar.apps.dashboard.offers',
-    'oscar.apps.dashboard.partners',
-    'oscar.apps.dashboard.pages',
-    'oscar.apps.dashboard.ranges',
-    'oscar.apps.dashboard.reviews',
-    'oscar.apps.dashboard.vouchers',
-    'oscar.apps.dashboard.communications',
-    'oscar.apps.dashboard.shipping',
+    'oscar.config.Shop',
+    'oscar.apps.analytics.apps.AnalyticsConfig',
+    'oscar.apps.checkout.apps.CheckoutConfig',
+    'oscar.apps.address.apps.AddressConfig',
+    'oscar.apps.shipping.apps.ShippingConfig',
+    'oscar.apps.catalogue.apps.CatalogueConfig',
+    'oscar.apps.catalogue.reviews.apps.CatalogueReviewsConfig',
+    'oscar.apps.partner.apps.PartnerConfig',
+    'oscar.apps.basket.apps.BasketConfig',
+    'oscar.apps.payment.apps.PaymentConfig',
+    'oscar.apps.offer.apps.OfferConfig',
+    'oscar.apps.order.apps.OrderConfig',
+    'oscar.apps.customer.apps.CustomerConfig',
+    'oscar.apps.search.apps.SearchConfig',
+    'oscar.apps.voucher.apps.VoucherConfig',
+    'oscar.apps.wishlists.apps.WishlistsConfig',
+    'oscar.apps.dashboard.apps.DashboardConfig',
+    'oscar.apps.dashboard.reports.apps.ReportsDashboardConfig',
+    'oscar.apps.dashboard.users.apps.UsersDashboardConfig',
+    'oscar.apps.dashboard.orders.apps.OrdersDashboardConfig',
+    'oscar.apps.dashboard.catalogue.apps.CatalogueDashboardConfig',
+    'oscar.apps.dashboard.offers.apps.OffersDashboardConfig',
+    'oscar.apps.dashboard.partners.apps.PartnersDashboardConfig',
+    'oscar.apps.dashboard.pages.apps.PagesDashboardConfig',
+    'oscar.apps.dashboard.ranges.apps.RangesDashboardConfig',
+    'oscar.apps.dashboard.reviews.apps.ReviewsDashboardConfig',
+    'oscar.apps.dashboard.vouchers.apps.VouchersDashboardConfig',
+    'oscar.apps.dashboard.communications.apps.CommunicationsDashboardConfig',
+    'oscar.apps.dashboard.shipping.apps.ShippingDashboardConfig',
 
     # 3rd-party apps that oscar depends on
     'widget_tweaks',
@@ -72,10 +72,14 @@
 
 # Use a custom partner app to test overriding models. I can't find a way of
 # doing this on a per-test basis, so I'm using a global change.
-INSTALLED_APPS[INSTALLED_APPS.index('oscar.apps.partner')] = 'tests._site.apps.partner'
-INSTALLED_APPS[INSTALLED_APPS.index('oscar.apps.customer')] = 'tests._site.apps.customer'
-INSTALLED_APPS[INSTALLED_APPS.index('oscar.apps.catalogue')] = 'tests._site.apps.catalogue'
-INSTALLED_APPS[INSTALLED_APPS.index('oscar.apps.dashboard')] = 'tests._site.apps.dashboard'
+partner_app_idx = INSTALLED_APPS.index('oscar.apps.partner.apps.PartnerConfig')
+INSTALLED_APPS[partner_app_idx] = 'tests._site.apps.partner.apps.PartnerConfig'
+customer_app_idx = INSTALLED_APPS.index('oscar.apps.customer.apps.CustomerConfig')
+INSTALLED_APPS[customer_app_idx] = 'tests._site.apps.customer.apps.CustomerConfig'
+catalogue_app_idx = INSTALLED_APPS.index('oscar.apps.catalogue.apps.CatalogueConfig')
+INSTALLED_APPS[catalogue_app_idx] = 'tests._site.apps.catalogue.apps.CatalogueConfig'
+dashboard_app_idx = INSTALLED_APPS.index('oscar.apps.dashboard.apps.DashboardConfig')
+INSTALLED_APPS[dashboard_app_idx] = 'tests._site.apps.dashboard.apps.DashboardConfig'
 
 AUTH_USER_MODEL = 'myauth.User'