Repository: encode/django-rest-framework

Issue:
SchemaGenerator is not using the SCRIPT_NAME in the url
## 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.
- [X] 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

Deploy your django project on a subpath with a proxy and make sure to set the SCRIPT_NAME header.
## Expected behavior

Urls in the schema to be using the URLs with the SCRIPT_NAME prepended.
## Actual behavior

URLs without the SCRIPT_NAME making the Django REST Swagger UI unusable, since its calling to the wrong URLs.

Also see this ticket at the Django REST Swagger repo: https://github.com/marcgibbons/django-rest-swagger/issues/494#issuecomment-234598025



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
@@ -23,6 +23,12 @@
     from django.utils import importlib  # Will be removed in Django 1.9
 
 
+try:
+    import urlparse  # Python 2.x
+except ImportError:
+    import urllib.parse as urlparse
+
+
 def unicode_repr(instance):
     # Get the repr of an instance, but ensure it is a unicode string
     # on both python 3 (already the case) and 2 (not the case).
diff --git a/rest_framework/routers.py b/rest_framework/routers.py
--- a/rest_framework/routers.py
+++ b/rest_framework/routers.py
@@ -278,11 +278,14 @@ class DefaultRouter(SimpleRouter):
     def __init__(self, *args, **kwargs):
         if 'schema_renderers' in kwargs:
             assert 'schema_title' in kwargs, 'Missing "schema_title" argument.'
+        if 'schema_url' in kwargs:
+            assert 'schema_title' in kwargs, 'Missing "schema_title" argument.'
         self.schema_title = kwargs.pop('schema_title', None)
+        self.schema_url = kwargs.pop('schema_url', None)
         self.schema_renderers = kwargs.pop('schema_renderers', self.default_schema_renderers)
         super(DefaultRouter, self).__init__(*args, **kwargs)
 
-    def get_api_root_view(self, schema_urls=None):
+    def get_api_root_view(self, api_urls=None):
         """
         Return a view to use as the API root.
         """
@@ -294,11 +297,12 @@ def get_api_root_view(self, schema_urls=None):
         view_renderers = list(api_settings.DEFAULT_RENDERER_CLASSES)
         schema_media_types = []
 
-        if schema_urls and self.schema_title:
+        if api_urls and self.schema_title:
             view_renderers += list(self.schema_renderers)
             schema_generator = SchemaGenerator(
                 title=self.schema_title,
-                patterns=schema_urls
+                url=self.schema_url,
+                patterns=api_urls
             )
             schema_media_types = [
                 renderer.media_type
@@ -347,7 +351,7 @@ def get_urls(self):
         urls = super(DefaultRouter, self).get_urls()
 
         if self.include_root_view:
-            view = self.get_api_root_view(schema_urls=urls)
+            view = self.get_api_root_view(api_urls=urls)
             root_url = url(r'^$', view, name=self.root_view_name)
             urls.append(root_url)
 
diff --git a/rest_framework/schemas.py b/rest_framework/schemas.py
--- a/rest_framework/schemas.py
+++ b/rest_framework/schemas.py
@@ -6,7 +6,7 @@
 from django.utils import six
 
 from rest_framework import exceptions, serializers
-from rest_framework.compat import coreapi, uritemplate
+from rest_framework.compat import coreapi, uritemplate, urlparse
 from rest_framework.request import clone_request
 from rest_framework.views import APIView
 
@@ -57,7 +57,7 @@ class SchemaGenerator(object):
         'delete': 'destroy',
     }
 
-    def __init__(self, title=None, patterns=None, urlconf=None):
+    def __init__(self, title=None, url=None, patterns=None, urlconf=None):
         assert coreapi, '`coreapi` must be installed for schema support.'
 
         if patterns is None and urlconf is not None:
@@ -70,7 +70,11 @@ def __init__(self, title=None, patterns=None, urlconf=None):
             urls = import_module(settings.ROOT_URLCONF)
             patterns = urls.urlpatterns
 
+        if url and not url.endswith('/'):
+            url += '/'
+
         self.title = title
+        self.url = url
         self.endpoints = self.get_api_endpoints(patterns)
 
     def get_schema(self, request=None):
@@ -102,7 +106,7 @@ def get_schema(self, request=None):
             insert_into(content, key, link)
 
         # Return the schema document.
-        return coreapi.Document(title=self.title, content=content)
+        return coreapi.Document(title=self.title, content=content, url=self.url)
 
     def get_api_endpoints(self, patterns, prefix=''):
         """
@@ -203,7 +207,7 @@ def get_link(self, path, method, callback):
             encoding = None
 
         return coreapi.Link(
-            url=path,
+            url=urlparse.urljoin(self.url, path),
             action=method.lower(),
             encoding=encoding,
             fields=fields


Test patch: