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 %-)
(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
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
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 %-)
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 %-)
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 %-)
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