Repository: encode/django-rest-framework

Issue:
Importing rest_framework.compat may raise AppRegistryNotReady
This may just be a wontfix, but wanted to at least make the issue known. In short `rest_framework.compat` cannot be imported during the Django's app registration/initialization process when django-guardian is in the `INSTALLED_APPS`. `guardian.shortcuts` imports Django's auth models, which in turn raises an `AppRegistryNotReady` exception. 

Relevant portion of the stack trace:

``` py
  File "lib/python2.7/site-packages/django_filters/compat.py", line 19, in <module>
    from rest_framework.compat import coreapi
  File "lib/python2.7/site-packages/rest_framework/compat.py", line 210, in <module>
    import guardian.shortcuts  # Fixes #1624
  File "lib/python2.7/site-packages/guardian/shortcuts.py", line 6, in <module>
    from django.contrib.auth.models import Group, Permission
```

See https://github.com/carltongibson/django-filter/issues/525 for more context. 
## Checklist
- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)
## Steps to reproduce

As an example, add `'guardian'` and `'django_filters'` to `INSTALLED_APPS`.



Discussion:

Resolving patch:
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -207,7 +207,6 @@ def value_from_object(field, obj):
 try:
     if 'guardian' in settings.INSTALLED_APPS:
         import guardian
-        import guardian.shortcuts  # Fixes #1624
 except ImportError:
     pass
 
diff --git a/rest_framework/filters.py b/rest_framework/filters.py
--- a/rest_framework/filters.py
+++ b/rest_framework/filters.py
@@ -289,6 +289,11 @@ def __init__(self):
     perm_format = '%(app_label)s.view_%(model_name)s'
 
     def filter_queryset(self, request, queryset, view):
+        # We want to defer this import until run-time, rather than import-time.
+        # See https://github.com/tomchristie/django-rest-framework/issues/4608
+        # (Also see #1624 for why we need to make this import explicitly)
+        from guardian.shortcuts import get_objects_for_user
+
         extra = {}
         user = request.user
         model_cls = queryset.model
@@ -302,4 +307,4 @@ def filter_queryset(self, request, queryset, view):
             extra = {'accept_global_perms': False}
         else:
             extra = {}
-        return guardian.shortcuts.get_objects_for_user(user, permission, queryset, **extra)
+        return get_objects_for_user(user, permission, queryset, **extra)
diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py
--- a/rest_framework/schemas.py
+++ b/rest_framework/schemas.py
@@ -1,4 +1,3 @@
-import os
 import re
 from collections import OrderedDict
 from importlib import import_module
@@ -37,6 +36,18 @@
 })
 
 
+def common_path(paths):
+    split_paths = [path.strip('/').split('/') for path in paths]
+    s1 = min(split_paths)
+    s2 = max(split_paths)
+    common = s1
+    for i, c in enumerate(s1):
+        if c != s2[i]:
+            common = s1[:i]
+            break
+    return '/' + '/'.join(common)
+
+
 def get_pk_name(model):
     meta = model._meta.concrete_model._meta
     return _get_pk(meta).name
@@ -292,7 +303,7 @@ def determine_path_prefix(self, paths):
                 # one URL that doesn't have a path prefix.
                 return '/'
             prefixes.append('/' + prefix + '/')
-        return os.path.commonprefix(prefixes)
+        return common_path(prefixes)
 
     def create_view(self, callback, method, request=None):
         """


Test patch:
diff --git a/tests/test_schemas.py b/tests/test_schemas.py
--- a/tests/test_schemas.py
+++ b/tests/test_schemas.py
@@ -335,3 +335,14 @@ def test_schema_for_regular_views(self):
             }
         )
         self.assertEqual(schema, expected)
+
+
+@unittest.skipUnless(coreapi, 'coreapi is not installed')
+class Test4605Regression(TestCase):
+    def test_4605_regression(self):
+        generator = SchemaGenerator()
+        prefix = generator.determine_path_prefix([
+            '/api/v1/items/',
+            '/auth/convert-token/'
+        ])
+        assert prefix == '/'