#31527: Admindocs' View index assumes settings.ROOT_URLCONF is an import string
-----------------------------------+--------------------------------------
     Reporter:  Keryn Knight       |                    Owner:  nobody
         Type:  Uncategorized      |                   Status:  new
    Component:  contrib.admindocs  |                  Version:  master
     Severity:  Normal             |               Resolution:
     Keywords:                     |             Triage Stage:  Unreviewed
    Has patch:  0                  |      Needs documentation:  0
  Needs tests:  0                  |  Patch needs improvement:  0
Easy pickings:  0                  |                    UI/UX:  0
-----------------------------------+--------------------------------------

Comment (by Keryn Knight):

 > Docs have it thus [...]
 > So I'm kind of inclined towards `wontfix`. `ViewIndexView` is making a
 reasonable assumption.

 It is making a generally reasonable assumption, but it's an ''assumption''
 which is ''sort of'' wrong.
 The documentation is also misleading anyway, because it says **Not
 defined** is the default which is ... true (being that it's not in
 `global_settings`), but at least on 2.2, not having a `ROOT_URLCONF` is
 your project settings is an `AttributeError`:
 {{{
 /path/to/python3.7/site-packages/django/core/handlers/base.py", line 74,
 in get_response
     set_urlconf(settings.ROOT_URLCONF)
 AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF'
 }}}
 So `ROOT_URLCONF` must be set, fine. It is on any given `startproject`
 driven package anyway. Let's set it to something which might be
 ''considered'' "not defined" and do  `ROOT_URLCONF = None` because we know
 [https://docs.djangoproject.com/en/3.0/topics/http/urls/#how-django-
 processes-a-request how Django processes a request must allow for some
 variation upon it...] and use a middleware instead:
 {{{
 class URLPatternsMW(MiddlewareMixin):
     def process_request(self, request):
         request.urlconf = urlpatterns()
 }}}

 That works fine, and lo we can navigate to the admin and all around it as
 you might expect (and also most of admindocs, because in my original
 ticket I stupidly omitted that route from the `urlpatterns()` definition).
 But if we try to go to the same URL as before (`/admin/doc/views/`), we
 get the same principle error, but on a different type:
 {{{
 'NoneType' object has no attribute 'startswith'
 }}}

 Perhaps we should've set `ROOT_URLCONF = ""` and tried that instead:
 {{{
 Exception Type: ValueError at /admin/doc/views/
 Exception Value: Empty module name
 }}}

 It also cannot be a `"myproject.urls"` where there's no `urlpatterns`
 attribute, but it **can** if `urlpatterns = []` is within that module -
 that's at least dealt with by the checks framework. So I guess that's the
 ''actual intended'' way of having no root urlconf to speak of... Ten years
 in, and I didn't actually know that :)
 `ROOT_URLCONF` can be `()` and it's also seemingly fine (because
 `set_urlconf` does the weaker ''falsy'' test), again except for this
 import expectation. Mostly because these things are hashable.

 > Can I ask what the use-case here is? (Also: How does it go with tests
 and override_settings and such? Is this literally the only place it blows
 up?)

 It's actually specifically within tests where I found it (or remembered
 it, I do think I encountered it before but just moved on with life), by
 doing:
 {{{
 if __name__ == "__main__":
     def urlpatterns(...):
         return ()
     settings.configure(ROOT_URLCONF=SimpleLazyObject(urlpatterns), ...)
     django.setup()
     run the tests etc.
 }}}
 I happen to "know" (hah!) using the deferred object mostly seems to work
 from other weird hacks and things I've played around with over the years
 (I think for example I may've been doing something similar in #26287), but
 I cannot say with certainty that it's the **only** place it poses an error
 (and certainly a bunch of downstream packages would make the same
 assumption).
 I can say that it's the only place in Django where I **know** the error
 can happen, being that it's the only place which directly tries to
 **import** the value of `ROOT_URLCONF`, where most everything else handles
 it further down the resolver stack.

 A cursory glancing guess (as is always my way, I'm afraid) at a fix is
 something more like:
 {{{
         urlconf = get_resolver(settings.ROOT_URLCONF)
         try:
             view_functions =
 extract_views_from_urlpatterns(urlconf.url_patterns)
         except ImproperlyConfigured:
             view_functions = []
 }}}
 which is more like what everything else looks to do (using the threadlocal
 resolver). I don't offer that up as the full solution, but a possible
 direction in which a correction might be found.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/31527#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/067.822c34eb2343629a850180ff5a540395%40djangoproject.com.

Reply via email to