#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.