Reputation: 9826
I'm using Django 2.x and Django REST framework.
I'm using django-oauth-toolkit
to enable OAuth2
authentication and django-rest-auth
for login and django-allauth
for user registration.
I want to generate access token in the response when a user is successfully registered. For that, I'm using a custom registration view.
For that, I have created a function utils like
def generate_token(request, user):
# Get OAuth application to use
application_: Application = Application.objects.filter(
client_type=Application.CLIENT_CONFIDENTIAL,
authorization_grant_type=Application.GRANT_PASSWORD
).first()
if not application_:
raise Exception('No OAuth2 Application is setup')
auth_data = {
'username': user.username,
'password': password,
'grant_type': 'password',
'client_id': application_.client_id,
'client_secret': application_.client_secret
}
if request.data:
mutable = request.data._mutable
request.data._mutable = True
request.data.update(auth_data)
request.data._mutable = mutable
if request.POST:
mutable = request.POST._mutable
request.POST._mutable = True
request.POST.update(auth_data)
request.POST._mutable = mutable
return TokenView().create_token_response(request=request)
When the endpoint is hit by Postman it works fine and request.data
has _mutable
attribute.
But when it is hit by Angular application, it gives error
'dict' object has no attribute '_mutable'
and the error points to mutable = request.data._mutable
Why _mutable
is missing for some requests?
Edit 2: Request headers
The request header sent by Postman is
Content-Type:"application/json"
Accept:"application/json, text/plain, /"
User-Agent:"PostmanRuntime/7.15.2"
Cache-Control:"no-cache"
Postman-Token:"b4461728-a6a9-48da-a4fa-3894920b5484"
Host:"api.staging.scanova.io"
Cookie:"messages="660481c3ce8c48b1884641ffdec0a3f701cdc9cf$..."; csrftoken=tNa6o6RDkEUTBti1cDJCvgV5oLG84qczgADeDfSY7hROsLP6cmhIgQKaamPQU7Xy; sessionid=r0l8kh58n8i14quyemj60ur6i59uhy1i"
Accept-Encoding:"gzip, deflate"
Content-Length:635
Connection:"keep-alive"
The request header from Angular is
Accept: application/json, text/plain, */*
Authorization: false false
Content-Type: application/json
Origin: https://localhost:4200
Referer: https://localhost:4200/auth/register
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36
Edit 3: Request type
The main view which is called from the endpoint is CreateAPIView
. generate_token
is called from this view which uses TokenView
to generate an access token. The TokenView
use Django's generic View
.
Upvotes: 2
Views: 2717
Reputation: 11
I used (request.POST._mutable = True) and this way does not work for me. then I copied request.POST in a new variable and I use the new variable in whole my code and this way work fine
Upvotes: 1
Reputation: 5426
For posterity (even though the question is rather old):
It seems like django/DRF treats multipart/form-data
and application/json
content type in a different enough way to cause issues.
So, even when using the same endpoint(viewset) and depending on whether the form is sent as multipart or app/json - request.data
will be a different object.
In one case it would be a 'normal' dict while in other it would be of QueryDict
type. So, in order to use the hacky _mutable
on/off an additional check similar to this is needed:
...
# one can also use:
# if 'multipart/form-data' in request.META.get('CONTENT_TYPE'):
# instead of the condition below
if isinstance(request.data, QueryDict):
auth_data = json.dumps(auth_data) # <----- QueryDict expects string values
request.POST._mutable = True # <----- mutable needs to be modified on POST and not on data
request.data.update(auth_data)
request.POST._mutable = False
...
Upvotes: 5