LOGIN_URL, LOGOUT_URL, LOGIN_REDIRECT_URL and SCRIPT_NAME.

880 views
Skip to first unread message

Graham Dumpleton

unread,
Jul 7, 2010, 7:11:02 PM7/7/10
to Django developers
Can the following issue be revisited.

http://code.djangoproject.com/ticket/8906

Conversation about it at:

http://groups.google.com/group/django-users/browse_frm/thread/c457599caab6e87d/b70e1f56ad38f4cb

This is another of those issues where Django isn't being particular
friendly to people who want to have relocatable sites. That is, where
in in one environment it may be mounted at root of site (eg.
development server), but where it has to be mounted at a sub URL in
another (eg. production). It also relates to the whole problem where a
web application, to be a well behaved WSGI citizen, should honour
SCRIPT_NAME setting as supplied by the server, and ensure that ways
are provided such that everything in the users code, including
configuration, urls or settings files, can be expressed relative to
the URL that the application is mounted at, thereby avoiding as much
as possible any need for a user to modify their code base when
deploying to a new environment at a different location in URL
namespace.

Yes I know Jacob knocked it on the head last time, but this problem
keeps occasionally popping up because it seems not to be obvious that
people need to go and change stuff when mounting an application at a
sub URL. The solution may be the introduction of a new section in the
Django documentation outlining all the things that need to be done
differently when mounting site at a sub URL. At least then people cant
complain the requirements aren't documented even if some may think it
is a silly requirement to begin with. :-)

Graham

Russell Keith-Magee

unread,
Jul 8, 2010, 10:40:04 AM7/8/10
to django-d...@googlegroups.com
On Thu, Jul 8, 2010 at 7:11 AM, Graham Dumpleton
<graham.d...@gmail.com> wrote:
> Can the following issue be revisited.
>
>  http://code.djangoproject.com/ticket/8906
>
> Conversation about it at:
>
>  http://groups.google.com/group/django-users/browse_frm/thread/c457599caab6e87d/b70e1f56ad38f4cb
>
> This is another of those issues where Django isn't being particular
> friendly to people who want to have relocatable sites. That is, where
> in in one environment it may be mounted at root of site (eg.
> development server), but where it has to be mounted at a sub URL in
> another (eg. production).

While I can see the issue you're referring to, I'm not sure I agree
with your diagnosis. The site is perfectly relocatable. You just need
multiple settings files -- in development, settings will contain
LOGIN_URL='/login'; in production, settings will contain,
LOGIN_URL='/mount_point/login'.

When you move from development to production you almost always (in my
experience) have a separate settings file, if only to separate
database configurations, contact emails, etc. Adding a couple of extra
settings for some values that actually *are* deployment specific
doesn't seem especially onerous to me.

>It also relates to the whole problem where a
> web application, to be a well behaved WSGI citizen, should honour
> SCRIPT_NAME setting as supplied by the server, and ensure that ways
> are provided such that everything in the users code, including
> configuration, urls or settings files, can be expressed relative to
> the URL that the application is mounted at, thereby avoiding as much
> as possible any need for a user to modify their code base when
> deploying to a new environment at a different location in URL
> namespace.
>
> Yes I know Jacob knocked it on the head last time, but this problem
> keeps occasionally popping up because it seems not to be obvious that
> people need to go and change stuff when mounting an application at a
> sub URL.

Personally, I see this as a case of explicit vs implicit.

As currently defined, LOGIN_URL points to the login URL. Period.

Under the proposed patch, the onus is on every possible script to
ensure that the script prefix has been set correctly. WSGI will do
this by default, but WSGI scripts aren't the only consumers of Django
code. Personally, I spend almost as much time on background processing
scripts for sites I support as I do on pages served via HTTP.

So - is it more confusing to require that a settings file explcitly
define the full URL, or to expect every script to configure itself to
populate the magic SCRIPT_NAME variable? Jacob's position (and I find
myself agreeing with this position) is that it's less confusing to
require the settings file to be explicit.

> The solution may be the introduction of a new section in the
> Django documentation outlining all the things that need to be done
> differently when mounting site at a sub URL. At least then people cant
> complain the requirements aren't documented even if some may think it
> is a silly requirement to begin with. :-)

This is certainly worth doing. Submissions welcome.

Yours,
Russ Magee %-)

Tom Evans

unread,
Jul 9, 2010, 6:47:36 AM7/9/10
to django-d...@googlegroups.com
On Thu, Jul 8, 2010 at 3:40 PM, Russell Keith-Magee
<rus...@keith-magee.com> wrote:
> Personally, I see this as a case of explicit vs implicit.
>
> As currently defined, LOGIN_URL points to the login URL. Period.
>
> Under the proposed patch, the onus is on every possible script to
> ensure that the script prefix has been set correctly. WSGI will do
> this by default, but WSGI scripts aren't the only consumers of Django
> code. Personally, I spend almost as much time on background processing
> scripts for sites I support as I do on pages served via HTTP.
>
> So - is it more confusing to require that a settings file explcitly
> define the full URL, or to expect every script to configure itself to
> populate the magic SCRIPT_NAME variable? Jacob's position (and I find
> myself agreeing with this position) is that it's less confusing to
> require the settings file to be explicit.
>

(Apologies if I've trimmed some context, I hate untrimmed replies)

I can understand this position, but this could also be considered a
case of 'implicit/explicit' vs DRY. I have one way of specifying URLs:
they go into my urlpatterns.
The requirement to have to repeat the same URLs in my settings.py is
tedious, and requires me to change multiple places if I modify the
urlpatterns - the canonical definition of the URL.
My login/logout urlpatterns are also named 'login' and 'logout'.

I'm also glad you brought up background processing. One of the common
things we do in the background is to process new data and generate
emails for our users.
These emails should link directly to the website, and to do this
requires three additional bits of information that we can't get from
the urlconf - the protocol to access via, the host name and port, the
local path on the host.
These are deployment specific and (effectively) mean that all scripts
must populate some magic variables regardless.
Again, it would be nice if I didn't have to repeat the local path in
four places - LOGIN_URL, LOGOUT_URL, LOGIN_REDIRECT_URL,
HTTP_LOCAL_PATH

The LOCAL_PATH is taken care of by $MAGIC (get_script_prefix()) when
using wsgi/fcgi - so reverse('login') returns a different value when
used within the context of a request than it does from a background
script.

Ideally, we can solve both these issues in several backwardly compatible steps.

Define new settings:
HTTP_HOST
HTTP_PROTOCOL
HTTP_LOCAL_PATH:
These define the local part of the deployment location - could be
combined into one parameter.
AUTH_URLS_USE_NAMED_PATTERNS=False

Based upon the setting of AUTH_URLS_USE_NAMED_PATTERNS, the various
places that use the LOG*_URL settings would instead look for named
views called 'login', 'logout' and 'post_login'.

The reverse function would be changed to create a complete absolute
URI, regardless of the context it is called from. The logic here would
be something along the lines of prepending get_script_prefix() if set,
or HTTP_LOCAL_PATH otherwise.
The reverse function would also gain an additional parameter,
specifying whether to generate a fully qualified URL, defaulting to
the old behaviour. This could then be used in situations where we know
we want an fully qualified URL, eg generating links to be emailed.

In release+1, we can cause setting LOG*_URL to raise a
PendingDeprecationWarning, and turn AUTH_URLS_USE_NAMED_PATTERNS to
True by default.
In release+2, we drop support for LOG*_URL

If you're interested in seeing what a solution like this would look
like, I could make a patch.

Cheers

Tom

bur...@gmail.com

unread,
Jul 9, 2010, 12:19:25 PM7/9/10
to django-d...@googlegroups.com
Tom,

HTTP_HOST and other don't solve the multiple-host deployment, and it
is a solution you can do by yourself if you need.

I'd like to see better solution: ability to make reverse work for such URLs.
I think, currently the problem is in the binding time:

The load order is typically the following:
settings.py
*/urls.py
*/models.py
*/admin.py
*/views.py

This is not mentioned anywhere (or is it?), but if this order is
broken, "cycle in imports" errors will probably occur.

So you can't put reverse now in settings.py unless there is some
late-binding construct, like

LOGIN_URL = RevLink('accounts:login', account_type='user')

which can be resolved in views.py or admin.py or models.py into
reverse in some way (__unicode__ or __call__?).

Magic prefixes can be used as
LOGIN_URL = MAGIC + RevLink('accounts:login', account_type='user')
or even better more explicit
LOGIN_URL = RevLink('accounts:login', path=PREFIX, host=HOST,
protocol=PROTOCOL, {'account_type': 'user'})

I'd like django to have better reverse, and better inter-module bindings!

> --
> You received this message because you are subscribed to the Google Groups "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to django-develop...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
>
>

--
Best regards, Yuri V. Baburov, ICQ# 99934676, Skype: yuri.baburov,
MSN: bu...@live.com

Carl Meyer

unread,
Jul 9, 2010, 12:49:06 PM7/9/10
to Django developers
On Jul 7, 7:11 pm, Graham Dumpleton <graham.dumple...@gmail.com>
wrote:
[snip]
> web application, to be a well behaved WSGI citizen, should honour
> SCRIPT_NAME setting as supplied by the server, and ensure that ways
> are provided such that everything in the users code, including
> configuration, urls or settings files, can be expressed relative to
> the URL that the application is mounted at, thereby avoiding as much
> as possible any need for a user to modify their code base when
> deploying to a new environment at a different location in URL
> namespace.

IMO one root problem here (which has been discussed before) is that
settings.py conflates "deployment config" with "application config"
and thus tends to lead to fuzzy thinking about which is which (which
causes problems especially for larger organizations with separate
teams for development and ops). Application config should not have to
change between different deployments of the same codebase; deployment
config almost certainly will.

The status quo makes LOGIN_URL (and friends) themselves a mishmash of
deployment config and application config; i.e. the initial
"application mount point" portion is deployment config, and the rest
is clearly application config (it would only change if the URLconf
changes). This seems like a bad thing to me: the ops team shouldn't
have to understand the internal layout of the application's urls in
order to deploy it correctly.

I would rather see LOGIN_URL et al as purely application config, in
which case they should automatically respect the URL mount point. The
problem, as Russ points out, is that if this mount point is learned at
runtime from the WSGI request, it adds complication to code outside
the request cycle that needs to know where the application is mounted.
But that exact same problem already exists for the site hostname!
Django punts this issue to the developer and/or contrib.sites.
Wouldn't it be most sensible to treat the URL mount point similarly to
hostname, since they are really just two pieces of the same data: the
deployed root URL of the application? (Practically speaking, this
could mean a new field in contrib.sites.Site, or allowing/condoning
"example.com/something" as a value for the domain field, and extending
RequestSite to do the same by checking SCRIPT_NAME).

Carl

Russell Keith-Magee

unread,
Jul 10, 2010, 8:09:41 AM7/10/10
to django-d...@googlegroups.com
On Fri, Jul 9, 2010 at 6:47 PM, Tom Evans <teva...@googlemail.com> wrote:
> On Thu, Jul 8, 2010 at 3:40 PM, Russell Keith-Magee
> <rus...@keith-magee.com> wrote:
>> Personally, I see this as a case of explicit vs implicit.
>>
>> As currently defined, LOGIN_URL points to the login URL. Period.
>>
>> Under the proposed patch, the onus is on every possible script to
>> ensure that the script prefix has been set correctly. WSGI will do
>> this by default, but WSGI scripts aren't the only consumers of Django
>> code. Personally, I spend almost as much time on background processing
>> scripts for sites I support as I do on pages served via HTTP.
>>
>> So - is it more confusing to require that a settings file explcitly
>> define the full URL, or to expect every script to configure itself to
>> populate the magic SCRIPT_NAME variable? Jacob's position (and I find
>> myself agreeing with this position) is that it's less confusing to
>> require the settings file to be explicit.
>>
>
> (Apologies if I've trimmed some context, I hate untrimmed replies)
>
> I can understand this position, but this could also be considered a
> case of 'implicit/explicit' vs DRY. I have one way of specifying URLs:
> they go into my urlpatterns.
> The requirement to have to repeat the same URLs in my settings.py is
> tedious, and requires me to change multiple places if I modify the
> urlpatterns - the canonical definition of the URL.
> My login/logout urlpatterns are also named 'login' and 'logout'.

I very much like the idea of using reverse() as an approach to solving
this problem. The LOGIN_URL setting predates the introduction of named
URL patterns; with hindsight, using a named URL is exactly the right
solution to this problem.

If we move to named URLs, we can also set a clear requirement that a
call to set_script_prefix() is required before using reverse() in a
standalone script. To my mind, this is a lot more elegant than
introducing indirection into the interpretation of a setting (which is
what the "insert SCRIPT_PREFIX into LOGIN_URL" proposal essentially
requires).

However, this poses the issue of how to introduce the use of named
URLs as substitutes for LOGIN_URL in a backwards compatible way.

> Ideally, we can solve both these issues in several backwardly compatible steps.
>
> Define new settings:
> HTTP_HOST
> HTTP_PROTOCOL
> HTTP_LOCAL_PATH:

My issue with these settings is that it requires manual specification
of settings that are provided (arguably, canonically) as part of the
request object. For example, HTTP_LOCAL_PATH is essentially the same
as SCRIPT_NAME. So now we have a whole different flavor of DRY
problems.

Essentially, these settings would only exist for the benefit of
standalone scripts, and it would be very easy for standalone scripts
to get these settings wrong (or at least inconsistent with the
deployed settings).

>  These define the local part of the deployment location - could be
> combined into one parameter.
> AUTH_URLS_USE_NAMED_PATTERNS=False
>
> Based upon the setting of AUTH_URLS_USE_NAMED_PATTERNS, the various
> places that use the LOG*_URL settings would instead look for named
> views called 'login', 'logout' and 'post_login'.

I'm not wild about the idea of introducing settings to turn on/off
features. Using this approach makes it very difficult (or at least
risky) to share reusable applications, because an application becomes
reliant upon a specific setting value being available. For example, if
I write a blog app that expects AUTH_URLS_USE_NAMED_PATTERNS=True, but
your application expects =False, then hilarity will ensue. This means
I, as an app writer, either need to write my code to adapt to both
settings, release two copies of my app, or document extensively that a
particular setting is required, thereby reducing my potential audience
(and exposing my non-doc-reading audience to a world of problems).

> The reverse function would be changed to create a complete absolute
> URI, regardless of the context it is called from. The logic here would
> be something along the lines of prepending get_script_prefix() if set,
> or HTTP_LOCAL_PATH otherwise.
> The reverse function would also gain an additional parameter,
> specifying whether to generate a fully qualified URL, defaulting to
> the old behaviour. This could then be used in situations where we know
> we want an fully qualified URL, eg generating links to be emailed.
>
> In release+1, we can cause setting LOG*_URL to raise a
> PendingDeprecationWarning, and turn AUTH_URLS_USE_NAMED_PATTERNS to
> True by default.
> In release+2, we drop support for LOG*_URL

I'm not sure I understand how you see this transition working. For
example ,if I have a settings file that provides LOGIN_REDIRECT_URL,
how does the fallback process work for contrib.auth.views.login work?

Yours,
Russ Magee %-)

Russell Keith-Magee

unread,
Jul 10, 2010, 8:14:41 AM7/10/10
to django-d...@googlegroups.com
On Sat, Jul 10, 2010 at 12:19 AM, bur...@gmail.com <bur...@gmail.com> wrote:
> Tom,
>
> HTTP_HOST and other don't solve the multiple-host deployment, and it
> is a solution you can do by yourself if you need.
>
> I'd like to see better solution: ability to make reverse work for such URLs.
> I think, currently the problem is in the binding time:
>
> The load order is typically the following:
> settings.py
> */urls.py
> */models.py
> */admin.py
> */views.py
>
> This is not mentioned anywhere (or is it?), but if this order is
> broken, "cycle in imports" errors will probably occur.

This order isn't enforced at all. settings.py shouldn't be importing
anything -- it should just be simple settings, and maybe some light
logic to accommodate complex configurations. Other than that, you just
need to avoid circular imports -- but that's not a Django specific
issue. Circular imports are an issue in almost every language I can
think of.

> So you can't put reverse now in settings.py unless there is some
> late-binding construct, like
>
> LOGIN_URL = RevLink('accounts:login', account_type='user')

You shouldn't have to put reverse() calls into settings.py -- you
should only have to provide the name. That's the point of having named
URLs. Resolution should be happing at runtime, not at time of
definition.

Yours,
Russ Magee %-)

Russell Keith-Magee

unread,
Jul 10, 2010, 8:24:09 AM7/10/10
to django-d...@googlegroups.com

This is a certainly a criticism I would agree with. An improved app
config/deployment config distinction is a broad goal that is worth
striving for.

> Wouldn't it be most sensible to treat the URL mount point similarly to
> hostname, since they are really just two pieces of the same data: the
> deployed root URL of the application? (Practically speaking, this
> could mean a new field in contrib.sites.Site, or allowing/condoning
> "example.com/something" as a value for the domain field, and extending
> RequestSite to do the same by checking SCRIPT_NAME).

I'm not sure I understand exactly what you're describing here. Lets
say we modify Site, and I have the mount point data described in my
Site.objects.get_current() instance; are you proposing that anywhere
that you use LOGIN_URL, we should be prepending site.mount_point (or
whatever the bikeshed looks like)?

If so, how do we get from here to there? For better or worse, if
you're deploying a Django site at a non-root SCRIPT_NAME, you will
have a LOGIN_URL that includes that SCRIPT_NAME; how do you propose
that we transition to a mount-point based interpretation?

Yours,
Russ Magee %-)

Alex Gaynor

unread,
Jul 10, 2010, 7:23:57 PM7/10/10
to django-d...@googlegroups.com
> --
> You received this message because you are subscribed to the Google Groups "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to django-develop...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
>
>

It seems to me the easy answer is to always pass it through
"redirect", which will do the right thing on an absolute URL, but also
handle reverse.

Alex

--
"I disapprove of what you say, but I will defend to the death your
right to say it." -- Voltaire
"The people's good is the highest law." -- Cicero
"Code can always be simpler than you think, but never as simple as you
want" -- Me

SmileyChris

unread,
Jul 11, 2010, 4:46:47 AM7/11/10
to Django developers
On Jul 11, 12:14 am, Russell Keith-Magee <russ...@keith-magee.com>
wrote:
> > So you can't put reverse now in settings.py unless there is some
> > late-binding construct, like
>
> > LOGIN_URL = RevLink('accounts:login', account_type='user')
>
> You shouldn't have to put reverse() calls into settings.py -- you
> should only have to provide the name. That's the point of having named
> URLs. Resolution should be happing at runtime, not at time of
> definition.

Well, with a lazy_reverse[1] function, this resolution could still
happen at at runtime.

[1] http://code.djangoproject.com/ticket/5925

Carl Meyer

unread,
Jul 12, 2010, 12:36:18 PM7/12/10
to Django developers
Hi Russ,

On Jul 10, 8:24 am, Russell Keith-Magee <russ...@keith-magee.com>
wrote:
> On Sat, Jul 10, 2010 at 12:49 AM, Carl Meyer <carl.j.me...@gmail.com> wrote:
> > Wouldn't it be most sensible to treat the URL mount point similarly to
> > hostname, since they are really just two pieces of the same data: the
> > deployed root URL of the application? (Practically speaking, this
> > could mean a new field in contrib.sites.Site, or allowing/condoning
> > "example.com/something" as a value for the domain field, and extending
> > RequestSite to do the same by checking SCRIPT_NAME).
>
> I'm not sure I understand exactly what you're describing here. Lets
> say we modify Site, and I have the mount point data described in my
> Site.objects.get_current() instance; are you proposing that anywhere
> that you use LOGIN_URL, we should be prepending site.mount_point (or
> whatever the bikeshed looks like)?

Actually, on further reflection I retract this suggestion entirely.
Conceptually the host domain and the script prefix could be considered
part of the "same thing," but practically speaking they are quite
different, given the need for every internal link URL to include the
script prefix, but not the host domain.

Re-reading this thread, the named URL pattern solution seems eminently
reasonable:

- LOGIN_URL and friends can all be set to either the name of a URL
pattern, or to an absolute URL for backwards compatibility (this could
be documented as deprecated, and perhaps also raise a
PendingDeprecationWarning?).
- document that set_script_prefix is required for scripts outside the
request-response cycle that want to generate URLs, if they have a
script prefix that isn't / (since get_script_prefix returns / by
default).

As Alex points out, the needed code is already present in the
redirect() shortcut view, though if we want absolute URLs to raise
PendingDeprecationWarning we wouldn't be able to just reuse that view.

If this seems OK I'm willing to reopen #8906 and attach a patch that
does this.

Carl
Reply all
Reply to author
Forward
0 new messages