Using Django Bad Request - django

I'm new in Django and Rest Framework and I didn't find how to do that:
Filter an endpoint request without argument to return Bad Request. Example:
get_foo/?foo_id=
Return:
{
"status": 400,
"error": "Bad Request"
}
At this time, a get request without argument gives all the values from the database. It's a big DB, so I have to do this filter.

You could filter it so If you have an empty queryset you will get that response:
If foo_id == '' # or If not foo_id
return Response(
{"res": "Bad Request"},
status=status.HTTP_404_NOT_FOUND)
else:
return . . .
In your resposne you can also put your text
If foo_id == '' # or If not foo_id
return Response({
"status": 400,
"error": "Bad Request"})

Related

Google cloud reCAPTCHA Enterprise showing invalid argument error

devs.
I am migrating my Recaptcha V2 to the Recaptcha enterprise using the below link.
https://cloud.google.com/recaptcha-enterprise/docs/using-features
the frontend integration part work and the Recaptcha checkbox part are showing.
On the backend side after the user submit the form passing the Recaptcha test. to verify the Recaptcha test, I call google REST API to create an assessment.
I am using this link.
https://cloud.google.com/recaptcha-enterprise/docs/create-assessment#rest-api
code of the function to verify user response.
def verify_captcha_response(self, recaptcha_response):
"""
Verify the Google Recaptcha V2 response of the request
- if recatacha response value is more than the value set
on google recaptcha admin.
- if request fail error will be raise and no response will be accept
"""
if not recaptcha_response:
return False
if isinstance(recaptcha_response, list):
recaptcha_response = recaptcha_response[0]
url = "https://recaptchaenterprise.googleapis.com/v1/projects/%s/assessments?key=%s" % ("Project id", settings.RECAPTCHA_SITE_KEY)
recaptcha_secret_key = settings.RECAPTCHA_SECRET_KEY,
headers={'Content-Type': 'application/json'}
data = {
"event":{
"token": recaptcha_response,
"siteKey": recaptcha_secret_key,
"expectedAction": "login"
}
}
response = requests.post(url, headers=headers,
data=data)
result = response.json()
if not result['success']:
if 'timeout-or-duplicate' in result['error-codes']:
raise forms.ValidationError(msg.RECAPTCHA_FAILED)
return False
return True
The response I am getting.
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"status": "INVALID_ARGUMENT"
}
}
but as per the documentation, I am passing all the arguments.
Thank you.

How to change the status code that is recieved in the response in case of specific exceptions in Django rest framework

I am adding some details to my Django model from a react frontend. Everything is working fine and the API request is made with Axios. The data that is being submitted is an OneToOne Field with the user and hence submitting more than once raises an Integrity error of Unique Constraint. But the response I receive back has a status of 200. This triggers a notification in my frontend that says details submitted every time the button is pressed. However. that is not being submitted the second time because of the Integrity Error.
My question is if I handle the Integrity Error exception separately how can I send a different status rather than 200 as shown below
config: {transitional: {…}, transformRequest: Array(1), transformResponse: Array(1), timeout: 0, adapter: ƒ, …}
data: {message: 'UNIQUE constraint failed: teacher_teacherdetail.user_id'}
headers: {content-length: '39', content-type: 'application/json'}
request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: "OK"
Currently I am handling the exception as below
try:
# some code
except IntegrityError as e:
message = {
"error": str(e)
}
return Response(message)
except Exception as e:
message = {
"message": "exception",
"error": str(e)
}
return Response(message)
You need to specify the error.
Here you can find all valid variables
from rest_framework import status
message = {
"error": str(e)
}
return Response(message, status=status.HTTP_400_BAD_REQUEST)

Django Webhook receive post request for Mailgun

I've setup a webhook in Django to receive updates from Mailgun.
The mailgun POST payload is delivered to the webhook in the below format:
{
“signature”:
{
"timestamp": "1529006854",
"token": "a8ce0edb2dd8301dee6c2405235584e45aa91d1e9f979f3de0",
"signature": "d2271d12299f6592d9d44cd9d250f0704e4674c30d79d07c47a66f95ce71cf55"
}
“event-data”:
{
"event": "opened",
"timestamp": 1529006854.329574,
"id": "DACSsAdVSeGpLid7TN03WA",
// ...
}
}
If I try and retrieve event parameter using the below code, I get an error saying TypeError: 'method' object is not subscriptable
#csrf_exempt
#require_POST
def mailgun(request):
event_data = request.POST.get['event-data']['event']
return HttpResponse(event_data, status=200)
Any help is appreciated.
After a lot of troubleshooting, the answer was hidden in the StackOverflow link below. In Python 3.0 to Python 3.5.x, json.loads() will only accept a unicode string, so you must decode request.body (which is a byte string) before passing it to json.loads().
Trying to parse `request.body` from POST in Django
body_unicode = request.body.decode('utf-8')
body = json.loads(body_unicode)
content = body['event-data']
recipient = content['recipient']
Try to update .get to call function and add some input checks, ie:
if request.POST.get('event-data'):
event_data = request.POST.get('event-data')['event']
return HttpResponse(event_data, status=200)
else:
return HttpResponse("[unknown event]", status=400)
or if you sure event-data never be empty or null call directly:
request.POST['event-data']['event']

How do i flatten error messages to display a single consistent error message in Django?

I am making a mobile app that uses Django Rest Framework. When one of my models fails validation, it uses one of the model.attributes as a key inside the error message, for example:
{'status_code': 400, 'name': [u'Ensure this field has no more than 32 characters.']}
{'status_code': 400, 'password': [u'Ensure this field has no more than 32 characters.']}
{'status_code': 400, 'arbitrary_field': [u'Ensure this field has no more than 32 characters.']}
This is very difficult to scale in a mobile, so I want to take the error messages and deliver a consistent 'error' key to the mobile device. For example,
{'status_code': 400, 'error': [u' Name: Ensure this field has no more than 32 characters.']}
{'status_code': 400, 'error': [u'Password: Ensure this field has no more than 32 characters.']}
{'status_code': 400, 'error': [u'Arbitrary Field: Ensure this field has no more than 32 characters.']}
In Rails, I could do this by saying:
model.errors.full_messages
But I'm not sure what the equivalent in Django is?
Thanks
You can define a property custom_full_errors in your serializer which will return the errors formatted according to your requirement. Doing serializer.custom_full_errors will give you the desired response.
We first call the serializer.errors to get the default errors dictionary. Then we iterate on this dictionary and create our desired response.
class MySerializer(serializers.ModelSerializer):
#property
def custom_full_errors(self):
"""
Returns full errors formatted as per requirements
"""
default_errors = self.errors # default errors dict
errors_messages = []
for field_name, field_errors in default_errors.items():
for field_error in field_errors:
error_message = '%s: %s'%(field_name, field_error)
errors_messages.append(error_message) # append error message to 'errors_messages'
return {'error': errors_messages}
...
For example:
# serializer.errors
{'name': [u'Ensure this field has no more than 32 characters.']}
will translate to
# serializer.custom_full_errors
{'error': [u'Name: Ensure this field has no more than 32 characters.']}
In case of multiple errors for a single field, we will get the following output on doing serializer.custom_full_errors:
# serializer.custom_full_errors
{'error': [u'Name: Error1', u'Name: Error2', u'Password: Error1', u'Password: Error2' ]}
I read this blog article, am yet to test it out
https://masnun.com/2015/11/06/django-rest-framework-custom-exception-handler.html
You create a custom error handler that concatenates the key-value pairs, after passing the exception through the default error handler method
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
if response is not None:
data = response.data
response.data = {}
errors = []
for field, value in data.items():
errors.append("{} : {}".format(field, " ".join(value)))
response.data['errors'] = errors
response.data['status'] = False
response.data['exception'] = str(exc)
return response
Then you point DRF to your custom error handler
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'EXCEPTION_HANDLER': 'api.utils.custom_exception_handler'
}
In case somebody is still looking for a solution, here is a great library that helps a lot in dealing with custom error validation: https://pypi.org/project/drf-standardized-errors/, here is the GitHub repo: https://github.com/ghazi-git/drf-standardized-errors
The response will be flat and instead of this:
{
"shipping_address":
{
"non_field_errors": [ErrorDetail("We do not support shipping to the provided address.", code="unsupported")]
}
}
You'll get this:
{
"code": "unsupported",
"detail": "We do not support shipping to the provided address.",
"attr": "shipping_address.non_field_errors"
}
It deals pretty good with List Serializers, changing such response:
{
"recipients": [
{"name": [ErrorDetail("This field is required.", code="required")]},
{"email": [ErrorDetail("Enter a valid email address.", code="invalid")]},
]
}
To this:
{
"type": "validation_error",
"errors": [
{
"code": "required",
"detail": "This field is required.",
"attr": "recipients.0.name"
},
{
"code": "invalid",
"detail": "Enter a valid email address.",
"attr": "recipients.1.email"
}
]
}
Based on this flat response it's much easier to form one string message to represent all the errors if you need to.
Basically, it handles the hardest part - getting a flat response out of a complex Django structure (all the nested fields, dicts, lists, etc.)
I know it is a bit late but I made this one using backtracking in case you have a lot of embedded nested serialisers.
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
stack = [response.data]
response.data = {}
errors = {}
if response is not None:
while len(stack) > 0:
data = stack.pop()
for field, value in data.items():
if type(value) is dict:
stack.append(value)
else:
if value is not list:
errors[field] = value
continue
for val in value:
code = val.code
message = str(val)
errors[field] = {
'message': message,
'code': code
}
response.data['errors'] = errors
return response

KeyError: access token

I have already test this before and it's work. Now the error back again and I didn't do any changes on my social app.
Here are my codes:
def get_profile(request, token=None):
args = {
'client_id': settings.FACEBOOK_APP_ID,
'client_secret': settings.FACEBOOK_APP_SECRET,
'redirect_uri': request.build_absolute_uri(reverse('social:fb_callback')),
'code': token,
}
target = urllib.urlopen('https://graph.facebook.com/oauth/access_token?' + urllib.urlencode(args)).read()
response = cgi.parse_qs(target)
access_token = response['access_token'][-1]
return access_token
Obviously, your request is not successful and the response doesn't have an access token. According to facebook docs, when a request isn't good, it returns a response with an error element, something like:
{
error: {
message: "Missing redirect_uri parameter.",
type: "OAuthException",
code: 191
}
}
So, in your function, you should do something like:
class FacebookAccessException(Exception): pass
def get_profile(request, token=None):
...
response = json.loads(urllib_response)
if 'error' in response:
raise FacebookAccessException(response['error']['message'])
access_token = response['access_token'][-1]
return access_token
PS:
Try to use better urllib. You should try Requests.