Repository: encode/django-rest-framework

Issue:
RegexURLResolver no longer exists in Django 2.0(a1)
## 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.)

In testing an application against Django 2.0a1, I ran into a confusing error:

```python
  File ".tox/py36-django20/lib/python3.6/site-packages/rest_framework/serializers.py", line 30, in <module>
    from rest_framework.compat import JSONField as ModelJSONField
  File ".tox/py36-django20/lib/python3.6/site-packages/rest_framework/compat.py", line 26, in <module>
    from django.core.urlresolvers import (  # Will be removed in Django 2.0
ModuleNotFoundError: No module named 'django.core.urlresolvers'
```

After investigating, it is related to a shim for older Django versions which now breaks as of Django 2.0a1:

```python
try:
    from django.urls import (
        NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
    )
except ImportError:
    from django.core.urlresolvers import (  # Will be removed in Django 2.0
        NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
    )
```
https://github.com/encode/django-rest-framework/blob/607e4edca77441f057ce40cd90175b1b5700f316/rest_framework/compat.py#L28

It turns out, `RegexURLResolver` no longer exists. I would imagine this is related to the new simplified URL patterns.

I'm unsure what the exact fix is at this moment, as I haven't dug into the new url implementation(s) yet, but I do recall that the old regex functionality is being maintained.


Discussion:
Pretty sure it's `URLPattern` and `URLResolver` now. I'm trying to figure this one out.
Ok, so as far as I've been able to figure out, they're far from drop-in replacements. The entire way resolving paths used to work appears to have been refactored and you would need to do some work to work with this new api. You would need some compatibility shim, as some of the attributes have been renamed or layers of indirection have been added since Django 2.0.
Its now `django.urls.resolvers.RegexURLPattern`
`django.urls.resolvers.RegexURLPattern` doesn't exist anymore either, it was changed in https://github.com/django/django/commit/df41b5a05d4e00e80e73afe629072e37873e767a.
Also running Django 2.x alpha with the latest Rest Framework

When running python manage.py compress the following output is returned:
https://gist.github.com/diemuzi/e9049c96b703d87d061caa6d018be3fd

Just adding it here in case it helps with any debugging. Would be happy to do anything else to help too.
I just installed DRF 3.7, and it seems that there still is a compatibility issue with Django 2.0 (a1):
```bash
 File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
  File "~/myproject/urls.py", line 4, in <module>
    from django_rest_passwordreset.views import reset_password_request_token, reset_password_confirm
  File "~/myproject/views.py", line 8, in <module>
    from rest_framework import parsers, renderers, status
  File "~/myproject/venv/lib/python3.5/site-packages/rest_framework/parsers.py", line 23, in <module>
    from rest_framework import renderers
  File "~/myproject/venv/lib/python3.5/site-packages/rest_framework/renderers.py", line 24, in <module>
    from rest_framework import VERSION, exceptions, serializers, status
  File "~/myproject/venv/lib/python3.5/site-packages/rest_framework/exceptions.py", line 17, in <module>
    from rest_framework.utils.serializer_helpers import ReturnDict, ReturnList
  File "~/myproject/venv/lib/python3.5/site-packages/rest_framework/utils/serializer_helpers.py", line 8, in <module>
    from rest_framework.compat import unicode_to_repr
  File "~/myproject/venv/lib/python3.5/site-packages/rest_framework/compat.py", line 31, in <module>
    from django.core.urlresolvers import (  # Will be removed in Django 2.0
ImportError: No module named 'django.core.urlresolvers'
```

Could be fixed by PR #5485 


Resolving patch:
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -25,14 +25,35 @@
 
 try:
     from django.urls import (
-        NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
+        NoReverseMatch, URLPattern as RegexURLPattern, URLResolver as RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
     )
+
 except ImportError:
     from django.core.urlresolvers import (  # Will be removed in Django 2.0
         NoReverseMatch, RegexURLPattern, RegexURLResolver, ResolverMatch, Resolver404, get_script_prefix, reverse, reverse_lazy, resolve
     )
 
 
+def get_regex_pattern(urlpattern):
+    if hasattr(urlpattern, 'pattern'):
+        # Django 2.0
+        return urlpattern.pattern.regex.pattern
+    else:
+        # Django < 2.0
+        return urlpattern.regex.pattern
+
+
+def make_url_resolver(regex, urlpatterns):
+    try:
+        # Django 2.0
+        from django.urls.resolvers import RegexPattern
+        return RegexURLResolver(RegexPattern(regex), urlpatterns)
+
+    except ImportError:
+        # Django < 2.0
+        return RegexURLResolver(regex, urlpatterns)
+
+
 try:
     import urlparse  # Python 2.x
 except ImportError:
diff --git a/rest_framework/schemas/generators.py b/rest_framework/schemas/generators.py
--- a/rest_framework/schemas/generators.py
+++ b/rest_framework/schemas/generators.py
@@ -15,7 +15,7 @@
 
 from rest_framework import exceptions
 from rest_framework.compat import (
-    RegexURLPattern, RegexURLResolver, coreapi, coreschema
+    RegexURLPattern, RegexURLResolver, coreapi, coreschema, get_regex_pattern
 )
 from rest_framework.request import clone_request
 from rest_framework.settings import api_settings
@@ -135,7 +135,7 @@ def get_api_endpoints(self, patterns=None, prefix=''):
         api_endpoints = []
 
         for pattern in patterns:
-            path_regex = prefix + pattern.regex.pattern
+            path_regex = prefix + get_regex_pattern(pattern)
             if isinstance(pattern, RegexURLPattern):
                 path = self.get_path_from_regex(path_regex)
                 callback = pattern.callback
diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py
--- a/rest_framework/urlpatterns.py
+++ b/rest_framework/urlpatterns.py
@@ -2,7 +2,7 @@
 
 from django.conf.urls import include, url
 
-from rest_framework.compat import RegexURLResolver
+from rest_framework.compat import RegexURLResolver, get_regex_pattern
 from rest_framework.settings import api_settings
 
 
@@ -11,7 +11,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required):
     for urlpattern in urlpatterns:
         if isinstance(urlpattern, RegexURLResolver):
             # Set of included URL patterns
-            regex = urlpattern.regex.pattern
+            regex = get_regex_pattern(urlpattern)
             namespace = urlpattern.namespace
             app_name = urlpattern.app_name
             kwargs = urlpattern.default_kwargs
@@ -22,7 +22,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required):
             ret.append(url(regex, include((patterns, app_name), namespace), kwargs))
         else:
             # Regular URL pattern
-            regex = urlpattern.regex.pattern.rstrip('$').rstrip('/') + suffix_pattern
+            regex = get_regex_pattern(urlpattern).rstrip('$').rstrip('/') + suffix_pattern
             view = urlpattern.callback
             kwargs = urlpattern.default_args
             name = urlpattern.name


Test patch:
diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py
--- a/tests/test_urlpatterns.py
+++ b/tests/test_urlpatterns.py
@@ -5,7 +5,7 @@
 from django.conf.urls import include, url
 from django.test import TestCase
 
-from rest_framework.compat import RegexURLResolver, Resolver404
+from rest_framework.compat import Resolver404, make_url_resolver
 from rest_framework.test import APIRequestFactory
 from rest_framework.urlpatterns import format_suffix_patterns
 
@@ -28,7 +28,7 @@ def _resolve_urlpatterns(self, urlpatterns, test_paths):
             urlpatterns = format_suffix_patterns(urlpatterns)
         except Exception:
             self.fail("Failed to apply `format_suffix_patterns` on  the supplied urlpatterns")
-        resolver = RegexURLResolver(r'^/', urlpatterns)
+        resolver = make_url_resolver(r'^/', urlpatterns)
         for test_path in test_paths:
             request = factory.get(test_path.path)
             try:
@@ -43,7 +43,7 @@ def test_trailing_slash(self):
         urlpatterns = format_suffix_patterns([
             url(r'^test/$', dummy_view),
         ])
-        resolver = RegexURLResolver(r'^/', urlpatterns)
+        resolver = make_url_resolver(r'^/', urlpatterns)
 
         test_paths = [
             (URLTestPath('/test.api', (), {'format': 'api'}), True),