Django rest basic authentication issue (HTTP 400) - python

I am building an API which has basic authentication enabled. When I'm trying to add an instance to the database (on a viewset which has the AllowAny setting) using the browsable api it. This works when I am not logged. However when I'm not logged in it gives me an error:
HTTP 400 Bad Request
Content-Type: application/json
Vary: Accept
Allow: POST, OPTIONS
{
"product": [
"This field is required."
],
"buyername": [
"This field is required."
],
"buyeremail": [
"This field is required."
]
}
This is the viewset:
class TicketBuyerViewSet(mixins.CreateModelMixin,
viewsets.GenericViewSet):
queryset = Ticket.objects.all()
serializer_class = TicketSerializer
permission_classes = (IsOwnerOrReadOnly,permissions.AllowAny)
def perform_create(self, serializer):
serializer.save(isPayed=False,checkedIn=False,isRefunded=False,checkedInDate=None,paymentID="",paymentDate=None)
And this is my urls.py file:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^', include('events.urls')),
url(r'^docs/', include('rest_framework_swagger.urls')),
]
urlpatterns += [
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
I have been having this problem only recently. I made the snippets app from the django docs which worked perfectly for about a week. However, since a couple day I have the same problem with this api as with my 'own' api. I have tried chrome and firefox as well.
Edit: While I get why the http error codes may be confusing opposed to my question I do highly suspect the error lies in the django-rest authentication because when I log out and I fill in the EXACT same data it DOES work. Here are the response error codes for the PUT request when I'm logged in and logged out respectively:
[03/Nov/2015 20:38:44] "POST /ticketsbuyer/ HTTP/1.1" 400 10513
[03/Nov/2015 20:39:24] "POST /ticketsbuyer/ HTTP/1.1" 201 4543
Edit 2: I downloaded the exact source code from the django-rest-framework-tutorial github. I created a superuser and the EXACT same thing happened, for some reason django rest browsable api or my browser is not sending the post data correctly.
Edit 3 For some reason it worked for me to downgrade to version 3.2.5 of the rest-framework. I'm not the only one with this problem: Django Rest Framework - Browsable API Form always returns 400 bad request

There's an opened bug about this: https://github.com/tomchristie/django-rest-framework/issues/3588
However, I wasn't able to reproduce yet. Help is welcomed to understand what's going on.
Edit: thanks, it seems indeed that the authentication is the key in this bug.
Edit: upgrading to 3.3.1 should fix the issue.

HTTP400 is not generally an authentication error. Typically it is a data error. The message it is sending back says that you did not send the required fields so check the data you are sending and make sure it is meeting all the required fields. If it was an authentication issue it would be returning a HTTP401

Related

Why isn't Django Rest Framework Token Authentication working?

I am currently using Django rest framework and trying to implement a Token Authentication system. Currently, my settings.py looks like this:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication'
]
}
and rest_framework.authtoken is in installed_apps.
My urls.py looks like this:
urlpatterns = [
...
url('^v1/users/$', views.users_view),
...
]
My views.py looks like this:
#authentication_classes((TokenAuthentication,))
#api_view(['PUT', 'POST'])
def users_view(request):
...
I'm working in postman to test the API and regardless of whether I put the token in the authorization field, the API works as intended. What do I need to change for the token authentication to work as intended?
Update:
Reqbin is also giving me the same functionality so I don't think it's a problem with postman.
You need to add permission class as well.
#authentication_classes((TokenAuthentication,))
#permission_classes((IsAuthenticated,))
#api_view(['PUT', 'POST'])
def users_view(request):
...
It appears there is a bug in Django that won't allow some authentications to work with function based views. I can confirm it doesn't work for TokenAuthentication and needed to use class based views.

django-rest-auth: Issue with Password Reset functionaliity

I have been trying to setup password reset functionality in DRF using django-rest-auth. Earlier I was getting error TemplateDoesNotExist:registration/password_reset_email.html which I resolved by adding the following code
serializer.py
from rest_auth.serializers import PasswordResetSerializer
from allauth.account.forms import ResetPasswordForm
class PasswordSerializer(PasswordResetSerializer):
password_reset_form_class = ResetPasswordForm
settings.py
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': 'api.serializers.PasswordSerializer',
}
However, Now I am getting into another issue - "NoReverseMatch: Reverse for 'account_reset_password_from_key' not found. 'account_reset_password_from_key' is not a valid view function or pattern name.". And haven't found any solution or workaround for this.
Any help would be appreciated.
So, finally I got the password reset functionality working. Here is how it goes -
We just need one URL in our urls.py -
urlpatterns = [
url(r'^account/', include('allauth.urls')),
url(r'^rest-auth/', include('rest_auth.urls')),
# This is the only URL required for BASIC password reset functionality.
# This URL creates the confirmation link which is sent via e-mail. All of the rest
# password reset features get their reverse lookup via django-allauth and django-rest-auth.
url(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', TemplateView.as_view(), name='password_reset_confirm'),
url(r'^rest-auth/registration/account-confirm-email/(?P<key>[-:\w]+)/$', allauthemailconfirmation,
name="account_confirm_email"),
url(r'^rest-auth/registration/', include('rest_auth.registration.urls'), name='account_signup'),
]
Using this URL configuration raised TemplateDoesNotExist at /api/rest-auth/password/reset/ error first. After a lot of debugging, I found that the issue was raised for the template - registration/password_reset_email.html which resides under the Django Admin's template directory. This happened due to another Django app that I was using and it had disabled the django admin app.
So, adding 'django.contrib.admin' under INSTALLED_APPS and removing the serializers resolved the issue.
I hope this resolves issue for others as well.
PS: Debugger is your best friend. ;)

LocaleMiddleware redirects on a non-i18n url pattern

I'm using Django's LocaleMiddleware to internationalize a part of the website I'm working on.
Here is my project's urls.py:
from django.conf.urls import patterns, include, url
from django.conf.urls.i18n import i18n_patterns
urlpatterns = patterns('',
url(r'^api/stuff/(?P<stuff_id>)\d+/$', ApiStuff.as_view()),
)
urlpatterns += i18n_patterns('',
url(r'^stuff/', DoStuff.as_view()),
)
The problem is, when ApiStuff.as_view() returns a 404 response (other error codes behave as expected), the LocaleMiddleware operates the request to make it redirect to /en/api/stuff/<stuff_id>, even though the /api namespace is clearly not in the i18n_patterns (at the end, it generates a 404 error too, but the content of my original response is lost).
Here is the code of ApiStuff:
import django.http
from django.views.generic import View
from project.stuff.models import Stuff
class ApiStuff(View):
#staticmethod
def get(request, *args, **kwargs):
stuff_id = kwargs['stuff_id']
try:
stuff = Stuff.objects.get(pk=stuff_id)
except Stuff.DoesNotExist:
return response({"error": "stuff not found"}, 404)
result = stuff.serialize()
return response(result)
def response(data, status=200):
data = json.dumps(data)
return django.http.HttpResponse(data, status=status, content_type='application/json')
I'm using django 1.6.10 (I know, it's late, but I can't update the version right now).
Am I missing something?
This is an old bug that has been tracked here:
https://code.djangoproject.com/ticket/17734
The 404 error handler is a default handler of Django. It obviously takes the current language into account in order to translate the "not found" message.
Unless you can upgrade to a newer version you probably need to define your own error handler (including route/url).
EDIT:
As a workaround it might be possible to extend the LocaleMiddleware and maybe also CommonMiddleware. However, it could be that the 404 handler does its magic nevertheless. Might be worth a shot though because it should only require few lines of code.
Citing the ticket:
when you request /api/raising/404/, it is redirecting the user to /en/api/raising/404/ (assuming it detects the language as en). If it tests before redirecting the user if /en/api/raising/404/ is resolvable, it would find out that this would raise a 404 as well and thus would not redirect the user.
EDIT 2:
As an alternative you could simply not use i18n_patterns and just detect/switch the language via browser/cookie/parameter. I did that for a project (which included Django CMS) where I kept running into problems with these URLs. It is definitely not a prerequisite for localized Django sites to use i18n URL patterns. It's just something on top, but not at all necessary. Localization will work just fine without it. If you need help with the middleware for switching languages, drop a comment.
One can get around this issue by using following url config
urlpatterns += i18n_patterns(
url(r'^(?!api.*)stuff', DoStuff.as_view()),
)

Django: admin login with parameter

I want to have my custom "/login" page. So in settings.py I did a simple LOGIN_URL = '/login'. But before doing it, I want to develop all other more complex pages. I found a simple but very effective hack like this:
urlpatterns = [
# blabla
url(r'^admin/', include(admin.site.urls)),
url(r'^login/$', RedirectView.as_view(
url=reverse_lazy('admin:login'))),
# blabla
]
This means: when the user is not connected he/she is redirected to /login. In the urls, /login is converted to 'admin:login' which is admin/login. This is a "double" redirect. Everthing works fine except this:
origin URL: "/my_jobs"
redirected to "login?next=/my_jobs"
redirected to "/admin/login"
So my problem is that I want do pass again the "next" parameter in the RedirectView. I found a lot about redirection and custom login, but not something about that on stackoverflow (this is not a duplicate).
You can set query_string to True, so that query strings are appended to the URL.
RedirectView(
url=reverse_lazy('admin:login'),
query_string=True,
# You might want to set permanent=False,
# as it defaults to True for Django < 1.9
permanent=False,
)
Note that Django comes with a built in login view. You can enable it by adding the URL pattern and a simple template, which isn't much more work than your code above.

404 Not Found Django when clearly in urls.py

I have the following code in the urls.py file
urlpatterns = patterns('ecomstore.accounts.views',
(r'^register/$', 'register', {'template_name':'registration/register.html', 'SSL':settings.ENABLE_SSL}, 'register'),
(r'^my_account/$','my_account', {'template_name':'registration/my_account.html'},'my_account'),
(r'^order_details/(?P<order_id>[-\w]+)/$', 'order_details', {'template_name':'registration/order_details.html'}, 'order_details'),
(r'^order_info//$', 'order_info', {'template_name':'registration/order_info.html'},'order_info'),
)
All the redirects work i.e. /register/, /my_account/, etc except for /order_info//. I'm getting a 404 page not found error and I'm not sure why this is any different than the others.
It's working on my development server, but not apache for some reason if that has anything to do with it.

Categories