POST to view loses POST data

54 views
Skip to first unread message

jacob

unread,
Jun 17, 2006, 7:37:37 PM6/17/06
to Django users
Yes, I've read http://code.djangoproject.com/wiki/NewbieMistakes

This is my URL list:

urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls')),
(r'^settings/$','file.app.views.settings'),
(r'^inbox/$','file.app.views.inbox'),
(r'^sent/$','file.app.views.sent'),
(r'^deletemessage/$','file.app.views.deletemessage'),
(r'^compose/$','file.app.views.createmessage'),
(r'^login/$','file.app.views.login'),
(r'^formtest/$','file.app.views.formtest'),
(r'^logout/$','file.app.views.logout'),
(r'^msg/(?P<msg_id>\d+)/$','file.app.views.msg'),
(r'^autologin/(?P<user_id>\d+)/$','file.app.views.autologin'),
)


I have an inbox template that generates a list of links to messages
with the necessary trailing slash like this:

<a class='msglink' href='/msg/34/'>blah blah</a>

That link takes you to the message view which has a form that looks
like this:

<form action='.' method='post'>
<input type='submit' value='Delete'>
</form>

I'm using the combined get/post approach in file.app.views.msg() but
when you click the 'Delete' button the request object passed to my
msg() method returns false when you evaluate request.POST, so the GET
view just gets rendered again.

I tried updating the GET template to hardcode the action part of the
form with a trailing slash instead of using "." like this:

<form action='/msg/{{msg.id}}/' method='post'>
<input type='submit' value='Delete'>
</form>

which generates HTML like this:

<form action='/msg/34/' method='post'>
<input type='submit' value='Delete'>
</form>

But that still results in request.POST being false when my msg() method
is called. I put some debugging code in my msg() view like this:

def msg(request,msg_id):
if request.POST:
raise ValueError, 'It posted.'

But that exception never gets raised.

Am I missing something obvious, or is there more info I should provide?

I'm using Django-0.95-py2.3.egg.

Thanks in advance!

jacob

Luke Plant

unread,
Jun 17, 2006, 8:21:00 PM6/17/06
to django...@googlegroups.com
On Sunday 18 June 2006 00:37, jacob wrote:

> I have an inbox template that generates a list of links to messages
> with the necessary trailing slash like this:
>
> <a class='msglink' href='/msg/34/'>blah blah</a>
>
> That link takes you to the message view which has a form that looks
> like this:
>
> <form action='.' method='post'>
> <input type='submit' value='Delete'>
> </form>

Short version:
<input type='submit' value='Delete' name="delete">
^^^^^^^^^^^^^

Long version:
request.POST is (essentially) a dictionary of post variables. As such,
if it is empty, it evaluates to False, even if the request method is
'POST'. In your form, you don't have a single 'successful' field --
the only field is an <input>, and since it doesn't have a 'name' it
can't be successful.

Luke

--
"Agony: Not all pain is gain." (despair.com)

Luke Plant || L.Plant.98 (at) cantab.net || http://lukeplant.me.uk/

Jeremy Dunck

unread,
Jun 17, 2006, 8:44:13 PM6/17/06
to django...@googlegroups.com
On 6/17/06, Luke Plant <L.Pla...@cantab.net> wrote:
> Long version:
> request.POST is (essentially) a dictionary of post variables. As such,
> if it is empty, it evaluates to False, even if the request method is
> 'POST'. In your form, you don't have a single 'successful' field --
> the only field is an <input>, and since it doesn't have a 'name' it
> can't be successful.

A post is a post w/ or w/o successful controls. How about putting a
dummy into the dictionary to force true?

Yeah, this is kludgy, but the alternative is to put in a attribute on
the request object to say "is this a post?" or force people to fix
"bugs" in their valid HTML. :)

Malcolm Tredinnick

unread,
Jun 17, 2006, 8:57:21 PM6/17/06
to django...@googlegroups.com

There is already a way to test for posts via the request object:
request.META['REQUEST_METHOD']. But your suggestion is not unreasonable,
so best to file a ticket so that it can be considered without being
forgotten.

The implementation side is easy, if we decided to go this route: make
sure that __nonzero__() on the MultiValueDict class returns the right
thing in these cases.

Thanks,
Malcolm

Max Battcher

unread,
Jun 17, 2006, 8:59:37 PM6/17/06
to django...@googlegroups.com

That's what request.META["REQUEST_METHOD"] is for.

http://www.djangoproject.com/documentation/request_response/

--
--Max Battcher--
http://www.worldmaker.net/
"I'm gonna win, trust in me / I have come to save this world / and in
the end I'll get the grrrl!" --Machinae Supremacy, Hero (Promo Track)

Jeremy Dunck

unread,
Jun 17, 2006, 8:59:52 PM6/17/06
to django...@googlegroups.com
On 6/17/06, Malcolm Tredinnick <mal...@pointy-stick.com> wrote:
> The implementation side is easy, if we decided to go this route: make
> sure that __nonzero__() on the MultiValueDict class returns the right
> thing in these cases.

Sorry, I dug around in python introspection docs a bit trying to find
what forces in bool() conversion, but came up empty. My response
wasn't as useful as it could have been. :)

Jeremy Dunck

unread,
Jun 17, 2006, 9:02:54 PM6/17/06
to django...@googlegroups.com
On 6/17/06, Malcolm Tredinnick <mal...@pointy-stick.com> wrote:
> There is already a way to test for posts via the request object:
> request.META['REQUEST_METHOD']. But your suggestion is not unreasonable,
> so best to file a ticket so that it can be considered without being
> forgotten.

Filed:
http://code.djangoproject.com/ticket/2183

James Bennett

unread,
Jun 17, 2006, 9:25:02 PM6/17/06
to django...@googlegroups.com
On 6/17/06, Malcolm Tredinnick <mal...@pointy-stick.com> wrote:
> There is already a way to test for posts via the request object:
> request.META['REQUEST_METHOD']. But your suggestion is not unreasonable,
> so best to file a ticket so that it can be considered without being
> forgotten.

I'm not convinced that it'd be a good thing to have request.POST
evaluate to True in these cases, but the reasoning is somewhat
pedantic.

First and foremost, there's a logical difference between the request
method and the request parameters, so it makes sense that a test for
the method could evaluate to True while a test for the parameters
could evaluate to False.

Second, since request.POST is supposed to be a "dictionary-like"
object, this would be unintuitive and, dare I say, "magical" behavior
-- an empty dictionary is False.

And, of course, there are practical considerations; if I want a view
to return particular output when someone POSTs with no content, now
instead of the expected "if not request.POST" -- checking for an empty
dictionary -- I have to come up with other tests like looking at the
length of request.POST to see if it's "empty but True". Again this is
unintuitive.

--
"May the forces of evil become confused on the way to your house."
-- George Carlin

Jeremy Dunck

unread,
Jun 17, 2006, 9:34:11 PM6/17/06
to django...@googlegroups.com
On 6/17/06, James Bennett <ubern...@gmail.com> wrote:
> I'm not convinced that it'd be a good thing to have request.POST
> evaluate to True in these cases, but the reasoning is somewhat
> pedantic.

I put the comment on the ticket.

The use of request.POST seems overloaded to mean both "is it a post"
and "what are the post variables" in the code. But implementation
doesn't matter to me.

I'm not in huge angst here, I just hoped to remove a newbie pitfall.

Adrian Holovaty

unread,
Jun 17, 2006, 11:14:39 PM6/17/06
to django...@googlegroups.com
On 6/17/06, James Bennett <ubern...@gmail.com> wrote:
> I'm not convinced that it'd be a good thing to have request.POST
> evaluate to True in these cases, but the reasoning is somewhat
> pedantic.
>
> First and foremost, there's a logical difference between the request
> method and the request parameters, so it makes sense that a test for
> the method could evaluate to True while a test for the parameters
> could evaluate to False.
>
> Second, since request.POST is supposed to be a "dictionary-like"
> object, this would be unintuitive and, dare I say, "magical" behavior
> -- an empty dictionary is False.

I agree 100% with James on this one. Having request.POST be an empty
dictionary evaluating to True -- that's just too odd. I think we ought
to bite the bullet and add request.method, which would be a shortcut
to a normalized (all caps?) version of request.META['REQUEST_METHOD'],
which is cumbersome to type and might not (?) always be upper case.

Adrian

--
Adrian Holovaty
holovaty.com | djangoproject.com

Ivan Sagalaev

unread,
Jun 18, 2006, 2:04:07 AM6/18/06
to django...@googlegroups.com
Adrian Holovaty wrote:
> I think we ought
> to bite the bullet and add request.method, which would be a shortcut
> to a normalized (all caps?) version of request.META['REQUEST_METHOD'],
> which is cumbersome to type and might not (?) always be upper case.
>
Per HTTP all request methods are defined in exact case ('GET', 'POST',
'PUT', 'DELETE', ...). No browser will ever send it otherwise even if
you have '<form method="post">' in HTML. So in a view method 'Post' is
not HTTP's POST and should be treated as garbage.

Luckily Django's pattern of doing:

if request.POST:
#post
else:
#get

is safe since every unknown method will end up in 'GET' part which
supposed to be idempotent anyway. And for better safety a user can list
exact methods with @require_http_methods decorator (which is somewhat
broken currently, I keep forgetting to file a ticket, will do now).

gabor

unread,
Jun 18, 2006, 6:20:50 AM6/18/06
to django...@googlegroups.com

i think the problem here is that we're mixing two things:

1. the method
2. the sent data


for GET requests it's simple, because the sent data is in the
querystring, so for GET requests the GET dictionary will contain the
relevant data.

but a POST request is more complicated. here you can do something like:


<form action="http://127.0.0.1:8000/foo/?query=string" method="post">
<input type="text" name="first" value="1st"/>
<input type="text" name="second" value ="2nd"/>
<input type="submit" name="submit">
</form>

and django will get (correctly):

POST <MultiValueDict: {'second': ['2nd'], 'submit': ['Submit'], 'first':
['1st']}>

GET <MultiValueDict: {'query': ['string']}>


and i think that people usually want do differentiate between GET and
POST requests, not GET and POST data.

so i'm also for request.method. maybe even is_get() and is_post(). i
understand that even currently you can do it with
request.META['REQUEST_METHOD'], but it's too long and ugly. and because
of that it does not look like the recommended approach.


gabor

Bill de hÓra

unread,
Jun 18, 2006, 7:20:03 AM6/18/06
to django...@googlegroups.com
Luke Plant wrote:
>
> Long version:
> request.POST is (essentially) a dictionary of post variables. As such,
> if it is empty, it evaluates to False, even if the request method is
> 'POST'.

That's a bug, imo.

cheers
Bill

Bill de hÓra

unread,
Jun 18, 2006, 7:29:22 AM6/18/06
to django...@googlegroups.com

Never mind - request.META["REQUEST_METHOD"] is news to me. On
reflection, the idiom request.POST is broken; time to fix my code.

cheers
Bill

Matt McDonald

unread,
Jun 18, 2006, 7:41:51 AM6/18/06
to django...@googlegroups.com
I have to agree that it should return false if there is no data.
Otherwise how are you going to tell the difference between a POST
with no data and a POST with data.

Another alternative is maybe request.method.POST and
request.method.GET for testing the method.

Adrian Holovaty

unread,
Jun 19, 2006, 11:52:27 PM6/19/06
to django...@googlegroups.com
On 6/17/06, Adrian Holovaty <holo...@gmail.com> wrote:
> I agree 100% with James on this one. Having request.POST be an empty
> dictionary evaluating to True -- that's just too odd. I think we ought
> to bite the bullet and add request.method, which would be a shortcut
> to a normalized (all caps?) version of request.META['REQUEST_METHOD'],
> which is cumbersome to type and might not (?) always be upper case.

As of changeset [3164], I've added the attribute request.method, which
is a string such as "POST" or "GET".

I debated making request.method a magic object that would let you
check via attribute access, as some syntactic sugar:

if request.method.POST:

...but I decided that would be too magic, and it would actually get in
the way of people doing stuff like "if request.method in ('GET',
'POST')". A plain string will do.

Reply all
Reply to author
Forward
0 new messages