I've just tested django_notify, so far it seems plenty good enough to
replace all my pass-on-message hacks (not to mention being much more
elegant than any of 'em) expect for not being able to set a message
php-style in the GET, this way:
http://example.com/?msg=Shiver+me+timbers
Whether being able to do that at all is desirable is another question
entirely ;)
Big bonus in my view: it's completely independent of the
django.contrib.auth-system.
> What needs to be done to get this or something like it into contrib?
Yes, this inquiring mind also wants to know.
HM
Since you were evidently at my DjangoCon presentation, you know that I
(and the core in general) have a pretty high level of reticence to
adding things to contrib - especially when the package can live quite
happily outside of core. So far, everything I have seen about
django-notify/djangoflash makes it seem like a completely standalone
package that will gain nothing from being part of core, other than
increasing the size of the tarball, and increasing the maintenance
load of the core team. One of the questions that needs to be answered
is "why should this be shipped with Django?"
Packages also need to meet the basic definition of django.contrib in
order to be accepted. Part of this definition is that it must be the
defacto standard implementation. In order to be the defacto standard
implementation of anything, it needs to be relatively stable and
feature complete. When you make statements like "I just contributed a
patch for combo/fallback storage", it leads me to believe that this
may not be the case - at least, not yet.
You also mention that there are a number of other implementations, but
you haven't really given a compelling survey or analysis of the
alternatives - you've just blessed one in particular. Why?
Another piece of the puzzle that is missing from my perspective is any
discussion of how session-based messaging interacts with the existing
contrib.auth messaging framework. IMHO, Django shouldn't ship with two
messaging APIs, so what is the integration point? Do we need a
messaging backend API? I haven't thought about this problem anywhere
near enough to offer a proposal, but I haven't seen any discussion of
this point either. A good solution for this problem would go a long
way to greasing the wheels.
Yours,
Russ Magee %-)
Sorry - perhaps I need to be more clear on my intent here. I'm
convinced of the importance of session-based messages - just not of
when and how a particular implementation should be added to trunk.
I agree that they are a useful idea. I agree with Malcolm that session
based messages should be a standalone app. I don't think they should
be tightly bound to core.
What I'm talking about is an orthogonal set of modifications that
would allow for _any_ messaging system (database backed, session
backed, or magic pony backed) to be used. This doesn't couple a
particular implementation of session-based messages to the core, but
it would allow for end-users to choose a session-based message
framework if they found one that was appropriate to their needs. In
the fullness of time, we may end up adding django-notify or similar to
contrib, but if we add the plugable backend we can defer that decision
until much later when implementations have stabilized and a clear
implementation winner has emerged.
If such an abstraction API isn't possible (and the differences between
the capabilities of the two certainly makes this likely), then that
makes it even more important that we choose the right implementation
to add to contrib. As it stands, I'm aware of at _least_ two
implementations (django-notify and djangoflash), but I have no real
feel for the implementation maturity or general level of community
acceptance of either. I'm not going to advocate adding anything to
contrib until I can be convinced that one particular implementation
really is the defacto standard implementation of the pattern.
Yours,
Russ Magee %-)
Having some kind of defacto cross-request notification system makessense; it's a very common usage pattern.
Attaching these kind of messages to a User instance is wrong: there is
not an enforced one to one correlation between a user and a session
[1], and you can't notify anonymous sessions.
You also mention that there are a number of other implementations, but
you haven't really given a compelling survey or analysis of the
alternatives - you've just blessed one in particular. Why?
Ah... the wonderful American perspective of the internet. :-)
As a proud resident of the antipodes, allow me to assure you that
200ms is not a representative sample of the time required to load a
page for those of us that don't live in the continental USA.
Let's consider Rackspace as a representative sample of a US based
server with plenty of bandwidth. As the crow flies, I'm about 17000 km
from a server in Texas. By simple laws of physics, it takes 60ms for
the signal to get from me to the server, and 60ms for the signal to
get back. That's 120ms, and I'm not even taking into account:
* The time spent going through the 14 routing points between me and
Rackspace's servers
* The fact that the cable between me and Texas doesn't follow the
same route as a crow
In reality, I get a ping time closer to 300 ms. And that's to a
high-end data center under ideal conditions - it can be much larger if
I'm dealing with low end providers.
Now add the time required to actually compute a response.
To put this in practical perspective - during DjangoCon, I heard lots
of people complaining about the speed of the hotel wireless network. I
didn't notice a serious change from what I use at home every single
day.
In this particular case, I think you're correct - I'm not especially
concerned about parallel cookie requests. However, there are plenty of
arguments that get made on the assumption that the internet is a form
of instant communication (<cough> database connection lag </cough>). I
want to drive home the point that Earth is a very large place, and no
matter where you are located, most of the world's population isn't
anywhere near you - and no amount of technology will fix problems
caused by the laws of physics.
Yours,
Russ Magee %-)
In reality, I get a ping time closer to 300 ms. And that's to a
high-end data center under ideal conditions - it can be much larger if
I'm dealing with low end providers.
http://code.djangoproject.com/wiki/SessionMessages
Things that still need to be discussed/done:
* Coming to consensus on what 3rd party app we actually choose to
extend/modify to fit into Django
* What to do with the existing user message API (see
http://code.djangoproject.com/wiki/SessionMessages#IntegratingwiththeExistingAPI)
* Review/add to the TODO list on the wiki
(http://code.djangoproject.com/wiki/SessionMessages#TODOOnceaSolutionisChosen)
Cheers,
Tobias
--
Tobias McNulty
Caktus Consulting Group, LLC
P.O. Box 1454
Carrboro, NC 27510
(919) 951-0052
http://www.caktusgroup.com
I should have also added:
* Coming to consensus on a de facto standard API suitable for
inclusion in Django
I originally put it on the TODO list, but in retrospect the discussion
needn't wait till we pick a solution.
As a practical starting point, I copied the API for django-notify to
the wiki page. I like the general form of that API, but I'd be
slightly more happy with something like:
from django.contrib import messages
request.messages.add('message', messages.INFO)
# or
request.messages.add('message', classes=(messages.WARNING,))
# or
request.messages.error('message')
A la python logging, I think endorsing a specific set of message
classes or tags (but still allowing them to be extended) is A Good
Thing because it helps reusable apps provide more customized feedback
in a standard way.
For comparison I added django-notices to the Available Options:
http://code.djangoproject.com/wiki/SessionMessages#AvailableOptions
The suggested API: how hard will it be to use different message-types
in addition to the syslog-like ones? I use django-notify now and the
types I use is nothing (for info), "error", and "help" (for
help/tips). I've also made django-notify optional in my reusables,
with a dummy-object if it is not installed. Locking them down with
class.attributes will find typos of course, but right now I don't have
to import anything just to use session-messages, I just do a
hasattr(request, 'notifications') .
HM
I think it should definitely be easy, if not actively encouraged. IMO
those constants should just be strings, or integers which can easily
be mapped to strings (like in Python logging). This would give you
the flexibility of passing in anything you want. django-notices does
it the latter way:
http://github.com/dcramer/django-notices/blob/master/django_notices/constants.py
I clarified that criterion in the wiki:
http://code.djangoproject.com/wiki/SessionMessages#Criteria
AFAIK this will become a non-issue with the advent of HTML5, as each
window/tab will have its own session.
Tobias
I'm not exactly sure what problem you see as not being addressed, and
I don't think you were arguing against this, but it has been noted
elsewhere that it's more desirable to store the messages directly in a
cookie, if possible, to avoid the extra database or cache hit.
It seems well worth it to me and there are solutions out there that
try to store the messages in a cookie and then fall back to the
session for longer (>4kb) messages.
For me, the best solution would be a combination of django-notify and
django-notices. django-notify for their fallback backend and the
interchangeable backend support in general, and django-notices for the
logger-like API. I especially like the NOTICE_LEVEL setting which
could easily be controlled via your DEBUG setting. I agree that custom
levels should be easy and encouraged, and the strings used even for
the built-in levels should be configurable.
Paul
I'm with you 100%. The criteria on the wiki page say as much, but I
should probably clarify that django-notify does not actually provide
any default levels/classes/tags yet. Thanks for the feedback.
http://code.djangoproject.com/wiki/SessionMessages#PotentialAPI
It would be really nice if one could optionally iterate over the
message lists separately. For example:
{% if request.messages.error %}
<ul class="messages error">
{% for message in request.messages.error %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
And then get the rest normally...
{% if request.messages %}
<ul class="messages">
{% for message in request.messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif
%}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
This would allow for much more flexibility from a design perspective.
Paul
django-notify lets you plug in a pure session storage if that's what you want.
And if you do use cookies, they'll get removed 90% of the time on the
next request, and in most other cases removed on the subsequent
request (e.g., in the case of 2+ chained redirects).
The point is just to keep them around until they're actually
displayed, not indefinitely.
I have no particular issue with allowing messages to be iterated in
full and/or by type. We could also just sort the messages and let you
use {% regroup messages by tag %}, but I suppose you want the ability
to pull out a particular type of error and that could get tedious with
{% regroup %}.
I think regroup would be fine. My thought was that with iteration by
type you'd be able to easily highlight just one type if you so choose,
and even have any of the types displayed in a completely different
area of the markup if you needed. Having both abilities would be the
best-of-all-possible-worlds in my opinion, but this is starting to
smell like scope-creep. I'll just say that the backends and API are
the most important things, and having individually iterable lists is
just a "nice to have." I'd definitely be happy to help with the
implementation though if the group thinks it a worthy feature.
Paul
In Python logging the levels are integers, but you still get
convenience methods like logger.error('msg'), etc.
I don't see why a similar method to filter the messages by type would
be a bad thing.
For the record I have no personal need for this feature (and hence
strong feelings about it), but nor do I see why adding it would be a
bad thing IF the need is actually there. Right now it sounds like
regroup will work fine for its original proponent, so, in the interest
of keeping things simple, let's assume it will not be included unless
someone brings up a compelling case.
That's fair enough. Simple is good, and if someone really needs
different message retrieval methods, I'm sure a custom middleware
could provide anything they'd need.
Tobias: Are you planning on coding up a proposal app? It doesn't sound
like there's far to go from a branch or fork of django-notify. I
should have some time to contribute to the code or testing if you need
it.
Thanks for your work on this (and on django-notify),
Paul
Thanks for offering up your time. Once there seems to be a general
consensus about where we're going I plan to do that, yes. We're
getting closer but I'm not convinced we're there, yet.
Before we get too far, I'd appreciate hearing feedback from one or
more of the core devs (and from some of the folks who were involved in
this discussion back when it was happening on the ticket) on the wiki
page[1] and the general direction this project is going.
Tobias
[1] http://code.djangoproject.com/wiki/SessionMessages
Yes, that's a choice we'll have to make. Most folks seem to default to
calling them messages (or maybe that's just me).
That said, I see your point. What about {{ request.messages }}? Is
that still going to be confusing? Whatever the name becomes, I don't
really see the need for the extra context processor. You'll likely be
writing the name far more often in your views than in your templates.
This also raises the issue of how this app will live side by side with
User messages. I threw out a few ideas on the wiki (give User
messages a much narrower purpose than they have now and/or phase them
out altogether), but I'm sure there are other (probably better) ones.
My only real issue with "notifications" is that it's more letters to
type and fewer letters in the actual message (before the line hits 80
chars).
Tobias
On the whole, I'm comfortable with what I'm seeing. I haven't looked
into the codebase for django-notify, but the feature set certainly
makes it look like a reasonable starting point.
Notwithstanding a teardown of the actual code, I have three comments
on progress thus far:
Firstly, given the similarity between the APIs, it seems like we
should be trying to maintain parity with logging. That means:
request.messages.add(messages.INFO, 'message')
with:
request.messages.info('message')
as an alias. Note that this is a reversal of argument order from the
proposed API. I'm not absolutely tied to this suggestion - I'm just
noting that adding Django support for Python logging is also on the
cards for v1.2, and it seems weird that we would introduce two APIs
with similar external APIs, but not try to maintain parity.
Secondly, I share Chris' concern about overloading 'messages'. This
overlaps with the bikeshed issue of what to call the new contrib
package - whatever name is picked for the contrib package should
probably be reflected in the context variable, etc.
Lastly, one issue that seems unaddressed is the transition plan for
replacing user.message_set. The preamble to the wiki page makes a
compelling argument for ditching message_set, but remains silent on a
proposal for how Django (as a project) should be treating this new
alternative.
The solution might well be "use it if you want, ignore it if you
don't", but given the problems with user.message_set, it might be
worth giving some thought to a deprecation/transition plan.
Yours,
Russ Magee %-)
In fact, it would be very useful to both log and add a message at the
same time, iff there is an error. So that shouldn't deliberately be
made hard to do but maybe suitable for a lazy man's shortcut: one call
that does both.
HM
That is binding together two technologies that are still fairly
conceptual in terms of their integration with Django at this point.
I'm a little worried that we wouldn't get it right the first time
around. The extreme case would involve implementing messaging as a
python logging handler. Frankly I'm not sure it's worth it.
Furthermore, "error" message to the user may not always be, in fact
often probably isn't, an error for the web app, so it'd be important
to differentiate between the two. E.g., you might want to log an
ERROR message under the INFO python logging level.
Short of providing this do-all call in the implementation itself, I
see a few options:
1) write your own wrapper if you do a lot of that
2) add some logging calls to the messaging code so that you can see
messages that were shown to the user if you want to, under a static
logging level (e.g., INFO)
I like #2, provided that the default logging handler in Django is a
NullHandler (I think it is/will be).
Tobias
This is my main concern, and the thing keeping me from being
enthusiastic about the current state of the proposal. Many apps --
including, notably, django.contrib.admin -- rely on user.message_set.
We can't simply remove it; we need to follow a deprecation schedule.
But we also shouldn't just keep two parallel "messaging"
implementations; that's silly.
The best solution, I think, would be to implement user messages
(user.message_set, get_and_delete_messages, etc) in terms of the new
framework, and then gradually deprecate and remove the old APIs.
Jacob
I think y'all misunderstood what I tried to say. I think it'll be
common to "bundle" them like this (it's not as if we can prevent
anyone from doing it!) and that there might one day be a desire for an
official shortcut to do it.
> Short of providing this do-all call in the implementation itself, I
> see a few options:
>
> 1) write your own wrapper if you do a lot of that
Already have, for django-notify. Since I already need to test that a
request has a "notifications"-attribute I have a handy place to attach
other "messaging" in the wide sense too, like logging, debug-stuff,
assert False when manually checking things...
> 2) add some logging calls to the messaging code so that you can see
> messages that were shown to the user if you want to, under a static
> logging level (e.g., INFO)
... or under its own prefix so that it can be nulled.
getLogger('msg.'+__name__) or however it winds up looking.
HM
This may be adding undue complexity, but I wanted to throw it out and
see what you thought anyway. I think a possible solution to Russ' last
two points may indeed be overloading the "messages" template variable.
What I'm thinking is that we keep the message_set (of course) and add
the new "request.messages.*" API, but completely remove the messages
part of the auth context_processor and have the default backend for
the new system pull both for the first release (purely for backward
comparability). Messages from the old message_set system could get a
default level in the new system. This shouldn't be an issue since the
people using it will likely be existing projects where they won't be
using any level information in their templates. We couple this
combined backend with settings that will allow you to easily disable
the DB part of the backend (or make it clear how to and strongly
suggest switching backends), and provide a setting for the default
message level that defaults to INFO.
I think this could ease transition as the two systems could live
together for the first release. The subsequent releases could switch
the default backend to the existing fallback one, and then eventually
remove the message_set part of the auth app all together.
I do recognize that this may create more confusion than is necessary.
It may also be an unacceptable performance drain on the new system to
support the old at all. I just think as someone who has projects that
still use the message_set, and especially considering the 3rd party
apps out there which may use it as well, it might be a nice balance to
be able to mix in the new and better while having as little impact on
the templates as possible.
Technically, this may be more of a challenge than it's worth however.
The current auth context_processor is in core, and it just really
feels wrong to me to couple this new app to either core or
contrib.auth. We could just remove the "messages" part of the auth
context_processor and add a new processor from the new app to the list
of defaults that will provide all of the "messages". And actually,
we'll need to at least override that part of the auth
context_processor anyway, or else we'll still get a db hit for every
request with a logged in user due to the call to
user.get_and_delete_messages.
Sorry about throwing my brainstorm at you. Perhaps it'll spark someone
else's solution.
Thanks,
Paul
I updated the API on the wiki. The levels/classes will likely be
different, because logging and messaging are two very different
things, but keeping the argument order the same is probably a good
thing.
> Secondly, I share Chris' concern about overloading 'messages'. This
> overlaps with the bikeshed issue of what to call the new contrib
> package - whatever name is picked for the contrib package should
> probably be reflected in the context variable, etc.
Okay, let's pick something other than 'messages' and stick to it. I'm
fine with "notifications", "notices", and maybe even "notes", but the
last one is a little ambiguous. Other ideas?
> Lastly, one issue that seems unaddressed is the transition plan for
> replacing user.message_set. The preamble to the wiki page makes a
> compelling argument for ditching message_set, but remains silent on a
> proposal for how Django (as a project) should be treating this new
> alternative.
>
> The solution might well be "use it if you want, ignore it if you
> don't", but given the problems with user.message_set, it might be
> worth giving some thought to a deprecation/transition plan.
I think the deprecation plan would look something like this:
1.2: user.message_set is officially deprecated but continues to work unaffected
1.3: user.message_set is still usable, but raises a deprecation
warning when it is used
1.4: user.message_set is removed
Are we comfortable ditching the auth Message model altogether (several
releases from now), or is someone actually using it for a legitimate
purpose that won't be supplanted by the proposed app?
Cheers,
Tobias
I'll stay out of this particular bikeshed, other than to note that
Pinax ships with django-notifications, which has nothing to do with
session-based messages.
> I think the deprecation plan would look something like this:
>
> 1.2: user.message_set is officially deprecated but continues to work unaffected
> 1.3: user.message_set is still usable, but raises a deprecation
> warning when it is used
> 1.4: user.message_set is removed
I was hoping for a little more detail than this - this is just
Django's general deprecation plan, and I wouldn't expect that to
change. What exactly do these steps mean in practice? What changes are
going to be made to (for example) admin? Are there any other apps that
will be affected? What will happen when I update from 1.2-1.3?
1.3-1.4? Is there a need for a transition API (e.g., a 'message_set'
descriptor that actually sends messages to the new message backend)?
> Are we comfortable ditching the auth Message model altogether (several
> releases from now), or is someone actually using it for a legitimate
> purpose that won't be supplanted by the proposed app?
This is the sort of question we need to resolve for a deprecation
plan. Do we just delete auth.Message? Do we migrate it to an
contrib.old_messages app? I'm open to other suggestions.
Personally, I don't have a problem deprecating auth.Message, if for no
other reason than authentication has nothing to do with messaging.
Yours,
Russ Magee %-)
I think this is a large reason why the original attempt to get this
ticket fixed fell apart: User messages and session messages are two
completely different beasts and trying to fit them into the same API
is somewhat of a dead end.
The proposed app would function better for contrib.admin than what
exists now, so I'm confident that if we introduce/deprecate in a
gradual way the necessary changes can be made easily and on a
reasonable schedule. I don't envision updating the admin in this
release but someone correct me if there's an issue with not doing it
right away.
I'm not sure exactly what you mean by implement user messages in terms
of the new framework. It is completely possible to implement a
storage engine in several of the proposed solutions that would use
user.message_set to store data, though I'm not sure what the advantage
would be over any of the other options. Furthermore, the fact that
user.message_set is just a related manager on a model and not an
independent API per se makes it a difficult one to remove while
simultaneously keeping user message functionality.
Unless there is a compelling use case for user-based messages that
really sets it apart from the proposed app (and there may very well
be, I just haven't heard it yet), my vote is to deprecate the Message
model in contrib.auth in a gradual way and remove it altogether
several releases down the road.
Tobias
As a developer I'd rather make the conscious switch from one
technology to the other. IMHO I don't think the effort and potential
confusion of making everyone's templates "just work" is worth it.
Chris, I know you have some personal experience trying to get session-
and user-based messages to work together, so I think we'd benefit from
any wisdom you might be able to offer. :)
Toby
* The proposed messaging app will definitely leverage the cookie
signing utilities also in store for 1.2.
* This is a compelling core feature for me, at least, because a
standard needs to be set that reusable apps can take advantage of.
Currently everyone rolls his or her own solution or depends on (with
all due respect) some random 3rd party app (or at least that's how it
feels when one is trying to integrate two apps that use different
messaging backends).
* Developing a plan to integrate with user.message_set (and
potentially deprecate the old API) is a key priority and something we
DO need to hammer out more. That said I think we can get it done.
Tobias
I put up a skeleton of a plan (not much more than what I sent before)
on the wiki:
http://code.djangoproject.com/wiki/SessionMessages#DeprecationPlan
There are still a few things missing (like what apps actually need to
be updated) but hopefully that'll provide us a place to keep track of
the details.
On Tue, Oct 13, 2009 at 10:30 PM, Tobias McNulty <tob...@caktusgroup.com> wrote:
> I'm not sure exactly what you mean by implement user messages in terms
> of the new framework. It is completely possible to implement a
> storage engine in several of the proposed solutions that would use
> user.message_set to store data, though I'm not sure what the advantage
> would be over any of the other options. Furthermore, the fact that
> user.message_set is just a related manager on a model and not an
> independent API per se makes it a difficult one to remove while
> simultaneously keeping user message functionality.
I took what Jacob said to be the opposite. It sounded to me like he
was suggesting that we keep the existing user messaging api, but
change the internals of it to use the new notifications framework for
storage. In my head it goes something like:
1. Remove the relation from the User to the Message model.
2. Add a message_set property that mimics the related manager, but
which stores the messages via the new notifications app.
3. Modify the get_and_delete_messages method to retrieve messages from
the new app.
4. Gradually remove the messaging stuff from contrib.auth all together.
This has the advantage of removing the need to modify
core.context_managers.auth since that just uses
User.get_and_delete_messages.
I think this method is nice and clean, backward compatible as far as
the API goes, and moves all messaging into a single backend w/o
disrupting existing view or template code. It may break things if
people are directly querying the Message model, but that has to be
extremely rare.
Paul
Right, that's exactly what I meant; thanks.
I'm trying to avoid having two incompatible messaging systems in
Django. I agree that linking messages to sessions makes more sense
than linking them to users [1], but we can't just remove the user
messages API outright. I'd like a good transition strategy, so it
seems the best way would be to keep the user messages API, but switch
it to use session messages under the hood.
Or am I missing something?
Jacob
[1] Just as a historical note: the message API was never really
intended for public consumption; it was added as a lightweight way of
giving success messages in the admin. It was originally really just a
hardwired in part of the admin, and as such I'm not surprised it
doesn't work for a lot of other uses.
That's an option. It's short and doesn't conflict with anything that
I know of. I added a section to the wiki with potential app names:
http://code.djangoproject.com/wiki/SessionMessages#PotentialAppNames
I know a solution like this was attempted and then abandoned earlier
in the ticket history.
Chris, do you have any wisdom to share about this? I expect it may be
complicated because I can't think of a clean way to get the current
request (and hence the session messages variable) inside the User
model.
Maybe I'm missing something.
Just to make sure I understand this correctly, let me try to summarize
what this would mean:
* the proposed app controls {{ messages }} and is responsible for
retrieving anything set in the Message model
* old apps that call user.message_set.create will still get their
messages to the screen, assuming the project uses {{ messages }} in
the template
* old apps that call get_and_delete_messages or iterate through the
Message.objects.all() manually will NOT see any messages that were
stored in the session or a cookie, but they will continue to see
messages created the old way
* judging from your code, the new app will NOT store any messages in
the old Message model, it will only read them
* the standard template code:
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
will continue to work untouched after the update. For those that
choose to leverage it, a {{message.level}} (or otherwise named
attribute) will also be available.
* since we are tying ourselves to the {{ messages }} variable, this
probably means that the app should be called 'messages'. There has
been some hesitation about this. Are we in the clear now that we have
a potential upgrade path from the old API? Or is there still concern
about naming?
I've been catching up on this and watching this evolve, and this
proposal now looks solid IMO.
I think you should call this 'messages' consistently throughout, in
terms of the names of modules/variables/settings. If you need to
changed some documentation regarding the existing message
functionality, call that 'user messages' to distinguish it.
With regard to the deprecation schedule, normally we add a
PendingDeprecationWarning before the DeprecationWarning. If code in
core/contrib continues to use user messages for 1.2, it will emit a
PendingDeprecation, which is bad practice really - it's not fair to
users to emit warnings that aren't their fault and that they can't
fix, and it's not good for core code to be using something that we are
saying everyone else shouldn't.
I think this means that either the deprecation cycle would have to
pushed back one (i.e. pending deprecation warning in 1.3, deprecation
in 1.4, removed in 1.5), or core/contrib should be fixed by 1.2. I
would strongly prefer the latter, and this would affect my vote: I
don't want two messaging systems in Django, and if user messages are
not deprecated, then we do have two systems.
Luke
--
You meet a lot of smart guys with stupid wives, but you almost
never meet a smart woman with a stupid husband. (Erica Jong)
Luke Plant || http://lukeplant.me.uk/
I agree that using deprecated code in the core is setting a bad
example. A quick review of the code shows 8 calls to
message_set.create: 3 in auth, 2 in admin, and 3 in the
create/update/delete family of generic views. This definitely sounds
to me like a manageable update for 1.2.
Per feedback from Jacob, Chris, and Luke, I updated the notes on the
existing API, the transition plan, and the potential API on the wiki:
http://code.djangoproject.com/wiki/SessionMessages
Cheers,
Tobias
I'm working on migrating auth, admin, and the create_update generic
views to use messages in my bitbucket branch.
I updated them all to use request.messages.add() and things work great
as long as one has the middleware and context processor enabled for
django.contrib.messages. This obviously won't be the case, however,
when everyone initially updates to Django 1.2. There are a couple
ways around this that I see:
1) raise an error stating that the MessageMiddleware and 'messages'
context processor need to be installed, like the admin does for the
SessionMiddleware, e.g.:
assert hasattr(request, 'messages'), "The Django admin
requires message middleware to be installed. Edit your
MIDDLEWARE_CLASSES setting to insert
'django.contrib.messages.middleware.MessageMiddleware'."
2) add a compatibility method to the messages app that looks for
messages and falls back to user.message_set if the middleware hasn't
been enabled, e.g.:
def compat_add_message(request, level, message):
"""
Attempt to add a message to the request using the 'messages' app, falling
back to the user's message_set if MessageMiddleware hasn't been enabled.
"""
if hasattr(request, 'messages'):
request.messages.add(level, message)
elif hasattr(request, 'user') and request.user.is_authenticated():
request.user.message_set.create(message=message)
In sticking with the staged deprecation policy, my vote would be:
* use compat_add_message() internally in 1.2. There is no need to
document this function as request.message_set.create will continue to
work.
* in 1.3, remove compat_add_message and raise an error if 'messages'
isn't enabled when you try to use the admin or create_update generic
views, etc.
I could be convinced to skip the compatibility function and go
straight to raising an error, but that seems like a big jump.
I updated the branch to do basically this, but nothing is set in stone.
Thoughts?
> In sticking with the staged deprecation policy, my vote would be:
> * use compat_add_message() internally in 1.2. There is no need to
> document this function as request.message_set.create will continue
> to work.
> * in 1.3, remove compat_add_message and raise an error if
> 'messages' isn't enabled when you try to use the admin or
> create_update generic views, etc.
I think this is the right approach. The second branch of
compat_add_message should raise a DeprecationWarning that indicates
that the 'messages' app will be a dependency of the admin and generic
views, since the user's code is going to break if they update to 1.3
without fixing their settings. Obviously the release notes for 1.2
and 1.3 need to detail this - you can start to add things to
doc/releases/1.2-alpha.txt
I've started to look at the code, and have some other minor
suggestions, but not significant things to do with approach, so I'll
e-mail you personally with them at some point. On the basis of what
I've seen so far, I'm willing to change my vote on this to +1, which
at the moment would make me the 'champion' for this feature, as no-one
has voted -1. At the moment, the big areas that are lacking are:
- user docs
- code docs (I'm curious about things like EOFMessage, there
is nothing to explain why it exists).
- changes and complete tests for the auth context processor,
including the different configurations that are allowed
(with and without the messages app, for instance). I think
the auth context processor should call the messages one,
so that you get the same result whether you use one or
the other or both.
Luke
--
Carpe post meridian! (I'm not a morning person.)
Luke Plant || http://lukeplant.me.uk/
Thanks for your support and feedback. I'll get started on the docs
and other changes you mention. There's a small issue with the auth
context processor returning what the messages one does: it's currently
the responsibility of the middleware to create the Storage object that
is request.messages, so this won't exist if the middleware isn't
installed.
We could: (a) instantiate a new Storage instance for the {{ messages
}} variable if request.messages doesn't exist (in the context
processor), or (b) introduce another compat_* method that tries to
retrieve messages from request.messages, and falls back to
user.message_set if the middleware hasn't been enabled. I'd be more
comfortable with (b) since I'm not certain what all the ramifications
of (a) would be (and there wouldn't be much point anyway, since
there'd be no way to get a message into the cookie or session storages
without the middleware).
Now that I think about it, this is probably what you meant and just
wanted to make sure the messages context_processor will do the same
thing. I'll update the code.
I couldn't help but notice the docs suggest setting MESSAGE_STORAGE like so:
MESSAGE_STORAGE = 'cookie.CookieStorage'
Just yesterday, in reference to another upcoming feature, Jacob
commented that he would prefer all such settings to require full paths
only. In other words you would remove the attempt to import from a
default location first. So the correct setting would be:
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
Obviously, it's not my place to say for sure, but I got the impression
this should apply to all new features.
--
----
\X/ /-\ `/ |_ /-\ |\|
Waylan Limberg
Yes, please.
Special-casing built-in backends requires more code and leads to the
impression that external backends are somehow second-class citizens
This isn't -- or shouldn't be -- true.
Jacob
Yes, please.
Special-casing built-in backends requires more code and leads to the
impression that external backends are somehow second-class citizens
This isn't -- or shouldn't be -- true.
Hi Tobias,
These docs look like a pretty good start to me. Here's some feedback:
* In the discussion about message levels mentions tags before tags
have been introduced. The section on levels should introduce the
messaging levels without reference to the tagging process.
* The explanation of tags says they are a 'string representation',
but doesn't clarify what that string is useful for. Is it a verbose,
human readable name? An identifier? A name suitable for a CSS class?
* There also seems to be a lot of attention paid to the numerical
values for constants. Ordinarily, you don't really care about the
numerical values of system constants - you just use them. To my mind,
the numerical values of the constants are irrelevant until you get to
the 'custom level' section - where they are only relevant because you
need to know the numbers you can't use.
* The example custom level CRITICAL doesn't have a consistent value -
it's 50 in places, 60 in others.
* The explanation around minimum recorded message levels could do
with some elaboration. It isn't entirely clear to me what the
message.level setting does - is it a minimum level? maximum level?
inclusive or exclusive? The example here needs to be richer - showing
an example of a message that _won't_ be stored, for example.
* Similarly for extra tags - are the extra tags concatenated onto
message.tags? Are spaces added? will message.tags become a list?
* The description of LegacyFallbackStorage storage could do with some
elaboration. I'd almost go so far as to say that there should be a
whole section in the docs about this storage backend, and exactly how
it operates - the original list where the storage engines are
introduced should provide a link to a more detailed section. Questions
that need to be addressed:
* In what order are messages retrieved from cookie,session and database?
* Do I need to do anything to my existing user.message_set code?
* What level do old-style messages get?
* Following the lead of contrib.comments, you may want to consider
splitting the settings documentation into a separate documentation
module.
On a code level - I noticed that the messages middleware is in the
list of defaults in the settings.py template, but not in
global_settings.py. A minor inconsistency, but one I noticed while
trying to work out exactly what the transition requirements would be
for existing projects.
I had a quick poke around the rest of the code base, especially about
transition issues. However, I didn't want to start in on a big
teardown unless you thought the code was ready for it. When you're
ready for a the full latex-glove treatment, let me know. :-)
Yours,
Russ Magee %-)
* In the discussion about message levels mentions tags before tags
have been introduced. The section on levels should introduce the
messaging levels without reference to the tagging process.
* The explanation of tags says they are a 'string representation',
but doesn't clarify what that string is useful for. Is it a verbose,
human readable name? An identifier? A name suitable for a CSS class?
* There also seems to be a lot of attention paid to the numerical
values for constants. Ordinarily, you don't really care about the
numerical values of system constants - you just use them. To my mind,
the numerical values of the constants are irrelevant until you get to
the 'custom level' section - where they are only relevant because you
need to know the numbers you can't use.
* The example custom level CRITICAL doesn't have a consistent value -
it's 50 in places, 60 in others.
* The explanation around minimum recorded message levels could do
with some elaboration. It isn't entirely clear to me what the
message.level setting does - is it a minimum level? maximum level?
inclusive or exclusive? The example here needs to be richer - showing
an example of a message that _won't_ be stored, for example.
* Similarly for extra tags - are the extra tags concatenated onto
message.tags? Are spaces added? will message.tags become a list?
* The description of LegacyFallbackStorage storage could do with some
elaboration. I'd almost go so far as to say that there should be a
whole section in the docs about this storage backend, and exactly how
it operates - the original list where the storage engines are
introduced should provide a link to a more detailed section. Questions
that need to be addressed:
* In what order are messages retrieved from cookie,session and database?
* Do I need to do anything to my existing user.message_set code?
* What level do old-style messages get?
* Following the lead of contrib.comments, you may want to consider
splitting the settings documentation into a separate documentation
module.
On a code level - I noticed that the messages middleware is in the
list of defaults in the settings.py template, but not in
global_settings.py. A minor inconsistency, but one I noticed while
trying to work out exactly what the transition requirements would be
for existing projects.
I had a quick poke around the rest of the code base, especially about
transition issues. However, I didn't want to start in on a big
teardown unless you thought the code was ready for it. When you're
ready for a the full latex-glove treatment, let me know. :-)
I had a quick poke around the rest of the code base, especially about
transition issues. However, I didn't want to start in on a big
teardown unless you thought the code was ready for it. When you're
ready for a the full latex-glove treatment, let me know. :-)
* I think there's a circular reference problem in the middleware with
the setup for request.messages. The messages middleware actually
creates request.messages, but the auth middleware creates a lazy
evaluator that can return request.messages. This means that as a
request comes in, the request is always given a lazy evaluator for
user.messages_set, which is then overwritten by the messages
middleware. The consequence of this is that any middleware that is
executed between the auth and messages processors will get
user.message_set if they try to set a message.
* I have some concerns about the thread-safety (or, rather,
request-safety) of the various backends. If I'm reading the code
right, if you have two parallel requests, a message written in one
response will overwrite a message written in the second, which will
result in the first message never being displayed to the user. This
wasn't a problem with the old API because messages were written to the
database on response, and read on request; the first request would get
all the messages. If we're using cookies and sessions, there's all
sorts of clobbering problems.
* importcls: I can see that the aim here is to reduce code
duplication. However, I'm not convinced it's worth it - the code
you're replacing isn't that complex to start with, only occurs in two
places, and the price paid is a loss of fidelity in the error message.
On top of that, it doesn't satisfy the use cases for the other
pluggable backends (like cache and mail). In this case, I'm happy to
live with the code duplication rather than introduce a new utility
module.
* contrib.messages.compat: I'm not a huge fan of the deprecation
approach here. What you've got in code here requires that when v1.4
comes around, we don't just need to delete deprecated methods, we (and
anyone else writing pluggable apps that need to support messages) need
to do code updates to remove usage of deprecated methods.
The compat_add_message() and compat_get_messages() calls are
essentially just shortcuts - so why not formalize it as the official
API. i.e.,:
messages.add_message(request, level, msg)
from django.contrib import messages
When v1.4 comes around, we will need to tweak the implementation of
add_message() to remove the deprecated code path, but that's the
extent of the code changes required by us or any third party using
messages.
This has the additional benefit of separating the implementation from
the API by a degree - if we ever choose to replace request.messages
with something that isn't bound to the request itself, the
message.add_message() api can remain.
* Message._get_tags() can be simplified by using ' '.join([extra,label])
* Is there a use case for backends to use store_serialized=False?
There doesn't appear to be one in the shipped backends.
* Is there a use case for the 'fail_silently' flag on update? it
doesn't appear to be used.
* I might be missing something subtle, but it appears that the
update() method is calling _prepare() on messages that have already
been prepared - namely, the _loaded_messages in the added_new case.
Shouldn't loaded messages already by prepared?
* In the cookie backend, the import of django.utils.simplejson will
check for a locally available version of json - there's no need for
the import check.
* Also in the cookie backend - Is it just me, or is
MessageDecoder.process_messages much more complex than is required? It
seems to be able to handle decoding lists of lists of messages and
dicts of messages - but I'm not sure I understand why. You can delete
lines 38-41, and the tests still pass.
* The _store() method on the session backend appears to rely on
implicit return values.
* Shouldn't the LegacyFallbackStorage class be using
user._message_set, rather than the message_set wrapper? If the user is
using a known legacy wrapper, we don't need to raise
PendingDeprecation warnings every time they use the backend.
* The 1.2 release docs will need an entry describing this changeset,
both for the feature and the upgrade steps required.
* The deprecation notes should explicitly mention all the APIs that
will be deprecated - the current text of "and associated APIs" isn't
particularly specific about what will be deprecated.
* The updated documentation about the auth context processor
shouldn't mention the old-style user.message_set API. If it's
deprecated, it's deprecated. However, it's ok to mention the old API
in the versionchanged note. (i.e., Prior to v1.2, request.messages was
a lazy accessor for user.message_set)
* In the auth docs, you can use the ..deprecated sphinx tag as the
analog for ..versionadded and ..versionchanged.
* In the docs about LegacyFallbackStorage, you don't need to repeat
or explain Django's deprecation policy - just say that the policy will
be followed and provide a link to the original source.
I'll take another look at this patch next week to make sure I haven't
missed anything. Assuming I don't find anything new and interesting
(and assuming you're happy with my response on the API issues), this
should be able to land in trunk in the very near future.
--
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.
I just converted some code, and the API seems good to me. I had one
issue, which was the fact that I had some code which only had access
to the User object, and not the request object, which had to be re-
plumbed. The only place this change will be an issue is where it is
difficult to do that (e.g. if your code is being called from somewhere
else which you can't change). There is no reason this should hold us
back - storing messages on the User is really a hack, and if people
have a problem with not being able to do it any more (i.e. in Django
1.4), they can use other hacks like storing the request in threadlocal
storage.
The simplest solution is to catch the exception in the generic views.
Another option is to add a 'fail_silently' keyword argument,
defaulting to False, like the mail sending API. It would need to be
added to all the shortcut functions, and used in the generic views and
anywhere that you cannot rely on there being a current authenticated
User. Docs should include something about it being intended for re-
usable apps.
The simplest solution is to catch the exception in the generic views.
Another option is to add a 'fail_silently' keyword argument,
defaulting to False, like the mail sending API. It would need to be
added to all the shortcut functions, and used in the generic views and
anywhere that you cannot rely on there being a current authenticated
User. Docs should include something about it being intended for re-
usable apps.
Good catch. fail_silently sounds good to me. I'll add.
It's also a good thing, I realized last night, because it means one can use messages from a reusable app without requiring that all projects using the app enable messages.
Cheers,
Tobias
Sent from a mobile phone, please excuse any typos.
On Dec 3, 2009 6:25 AM, "Russell Keith-Magee" <freakb...@gmail.com> wrote:On Thu, Dec 3, 2009 at 2:39 PM, Alex Gaynor <alex....@gmail.com> wrote: > On Thu, Dec 3, 2009 at ...
-- You received this message because you are subscribed to the Google Groups "Django developers" g...
Looking though this patch I couldn't help but notice the newget_messages function. What if a project has neither the
MessageMiddleware nor the contrib.auth app enabled? Sure a reusable
app could catch the error, but that error will change once all support
for user.messages are removed in 1.4. Shouldn't the call to
user.get_and_delete_messages be wrapped in a check that request.user
exists like the new add_message function does?