Request object does not allow attributes to be set

1,753 views
Skip to first unread message

Beau Hargis

unread,
Nov 7, 2012, 7:45:47 PM11/7/12
to django-res...@googlegroups.com
Creating a custom API to login users which worked under 0.4.0, but does not work on with 2.1.x.

The problem is that the request object that the view gets does not allow attributes to be set as the standard django HttpRequest object does. So, when the user credentials are validated and some custom checks performed, the request object is passed to the login function and I get an 'AttributeError: can't set attribute' when attempting to set the user attribute on the request object:

  1.     request.session[SESSION_KEY] = user.id
  2.     request.session[BACKEND_SESSION_KEY] = user.backend
  3.     if hasattr(request, 'user'):
  1.         request.user = user

I can work around this by creating a property to return the original HttpRequest object and pass that along to the functions, but I wanted to ping the group and see if anyone else had any thoughts.

Jani Tiainen

unread,
Nov 8, 2012, 1:03:22 AM11/8/12
to django-res...@googlegroups.com
8.11.2012 2:45, Beau Hargis kirjoitti:
> Creating a custom API to login users which worked under 0.4.0, but does
> not work on with 2.1.x.
>
> The problem is that the request object that the view gets does not allow
> attributes to be set as the standard django HttpRequest object does. So,
> when the user credentials are validated and some custom checks
> performed, the request object is passed to the login function and I get
> an 'AttributeError: can't set attribute' when attempting to set the user
> attribute on the request object:
>
> 72.
>
> request.session[SESSION_KEY] = user.id
>
> 73.
>
> request.session[BACKEND_SESSION_KEY] = user.backend
>
> 74.
>
> if hasattr(request, 'user'):
>
> 75.
>
> * request.user = user*
>
> I can work around this by creating a property to return the original
> HttpRequest object and pass that along to the functions, but I wanted to
> ping the group and see if anyone else had any thoughts.
>

This is known issue with DRF2.

Though your login procedure is incorrect (see Django docs how you should
do the login) it would result the same error.

Problem is that DRF2 doesn't use Django HttpRequest but a wrapper around
it where user is read only. I was supposed to make a patch for it but so
far I hadn't have any time for it :)

You can get around of it when you need to pass request object to a
authentication function you can use undocumented feature and pass
"request._request" which points to original django HttpRequest.

--
Jani Tiainen

- Well planned is half done and a half done has been sufficient before...

Beau Hargis

unread,
Nov 8, 2012, 2:30:03 PM11/8/12
to django-res...@googlegroups.com
Hmm... The Django login procedure still requires the request object to be passed.

If I create a JSON API for authentication purposes which is used by AJAX functions in a client-side application and/or other non-web software, there exists an endpoint to which a JSON string, or form data, is posted. If the endpoint is being implemented with DRF2, then upon validating the user credentials, the request object, and user object, is passed to the login function which is part of contrib.auth. The request object provided by DRF2 fails to provide the same functionality required to preform the login procedure which exists within Django. It also makes it difficult to integrate DRF2 with other apps, modules or middleware which might receive that request object as it is passed through the request/response chain.

Tom Christie

unread,
Nov 8, 2012, 3:40:43 PM11/8/12
to django-res...@googlegroups.com
So,

1. If either you folks submit a failing test case for this I'll make sure it gets fixed as soon as possible.
2. Even better, if you make the fix yourselves and I'll review it immediately (.user and .auth on the Request object are currently gettable properties, they just need to be made into gettable/settable properties.)
3. As it happens having a client login via the API using SessionAuthentication isn't really best practice.
  Typically what you ought to do is:

* If the client is internal AJAX web stuff, use regular user-initiated login with a standard Django login page (rather than have the client perform the login itself.)
* If the client external, use a token authentication scheme.

I think perhaps one missing piece of the puzzle is that it would be helpful if REST framework's token auth also provided a view that clients could post the login credentials to, and have them exchanged for an auth token, that subsequent API calls would use for authentication.  If anyone wants to tackle that I'll gladly provide any help I can. 

Beau Hargis

unread,
Nov 9, 2012, 4:24:52 PM11/9/12
to django-res...@googlegroups.com
Nothing is broken so that I cannot fix it in some way (and have), and I have been pondering an elegant solution to it.

It it also worth noting that it is not just AJAX, but remote client software not existing in any web context which will be accessing these endpoints. I have a token-based auth solution which works, but the user/client still has to authenticate in some manner though an API to get a new token. I am using an API endpoint created with DRF2 to provide the capability for a client to authenticate and get a token. AJAX-enabled web pages is one client implementation, however, some of the pages are AJAX-enabled and some are pages are strictly generated server-side in the usual Django template fashion. This means that the standard cookie-based session/auth is used on conjunction with AJAX-enabled with limitations imposed by browser security. The idea is to be able to have a minimal layer of middleware which creates the exact same authenticated request context no matter which client is being used so that there is no code-duplication or special-case logic depending on the method of access.

Everything else after login is unaffected. Nothing else in the API ever manipulates the request object in anyway. In my mind the augmentation of features and objects provided by Django is great, but the wrapping of objects which negates or eliminates functionality or changes expected behavior is the wrong direction. Maybe Django is making it difficult to augment the request object, or maybe Django is just deficient in some way, but with something as fundamental as 'request' and 'response' objects nothing should be imposing arbitrary limitations. Seems very non-Pythonic and antithetical to object-oriented programming and dynamic languages in general.

I will be hammering on DRF2 in this current project and have forked it to make any needed changes and will send out pull requests if it is warranted.

Tom Christie

unread,
Nov 10, 2012, 4:43:49 AM11/10/12
to django-res...@googlegroups.com
> but with something as fundamental as 'request' and 'response' objects nothing should be imposing arbitrary limitations.

Indeed, the Request object should transparently behave just like an HttpRequest object.
This is an edge case where that isn't (yet) happening.
If you could code up a test case for this that'd be great - I'll make sure it get's fixed ASAP.

Mark Hughes

unread,
Nov 22, 2012, 12:34:49 PM11/22/12
to django-res...@googlegroups.com
On 10 November 2012 09:43, Tom Christie <christ...@gmail.com> wrote:
> but with something as fundamental as 'request' and 'response' objects nothing should be imposing arbitrary limitations.

Indeed, the Request object should transparently behave just like an HttpRequest object.
This is an edge case where that isn't (yet) happening.
If you could code up a test case for this that'd be great - I'll make sure it get's fixed ASAP.



This has bitten us as well. Unless someone else is working on this I'm happy to put something together and submit a pull request. 

Adding a setters to the user property plus a test in tests/requests.py should be enough?

Tom Christie

unread,
Nov 22, 2012, 12:47:33 PM11/22/12
to django-res...@googlegroups.com
Hi Mark

> This has bitten us as well. Unless someone else is working on this I'm happy to put something together and submit a pull request. 

Excellent, thanks!

> Adding a setters to the user property plus a test in tests/requests.py should be enough?

Exactly, yes.
Reply all
Reply to author
Forward
0 new messages