Format ValidationError in Serializer - json

DjangoRestFramework seems to handle errors with a variety of ways. The ValidationError in the serializer class does not consistently return JSON the same.
Current response includes a JSON list/object string:
{"detail":["Unable to log in with provided credentials."]}
Looking to achieve:
{"detail":"Unable to log in with provided credentials."}
I realize that this response is a result of default functions. However, I've overridden the validate function:
class AuthCustomTokenSerializer(serializers.Serializer):
username = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
token = serializers.CharField(read_only=True)
def validate(self, validated_data):
username = validated_data.get('username')
password = validated_data.get('password')
# raise serializers.ValidationError({'detail': 'Unable to log in with provided credentials.'})
if username and password:
user = authenticate(phone_number=username, password=password)
try:
if UserInfo.objects.get(phone_number=username):
userinfo = UserInfo.objects.get(phone_number=username)
user = User.objects.filter(user=userinfo.user, password=password).latest('date_joined')
if user:
if user.is_active:
validated_data['user'] = user
return validated_data
else:
raise serializers.ValidationError({"detail": "User account disabled."})
except UserInfo.DoesNotExist:
try:
user = User.objects.filter(email=username, password=password).latest('date_joined')
if user.is_active:
validated_data['user'] = user
return validated_data
except User.DoesNotExist:
#raise serializers.ValidationError("s")
raise serializers.ValidationError({'detail': 'Unable to log in with provided credentials.'})
else:
raise serializers.ValidationError({"detail" : "Must include username and password."})
class Meta:
model = Token
fields = ("username", "password", "token")
I've tried adding a custom exception handler:
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)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.status_code
return response
views.py: if serializer.is_valid(raise_exception=True):
However, that only appends the currently raised error:
{"detail":["Unable to log in with provided credentials."],"status_code":400}
How should I use change the format of the returning text?
It only returns the JSON like this for this particular serializer within the validate function.
I've also looked into formatting the non_field_errors template, but it works with all my other serializers e.g:
{"detail": "Account exists with email address."}

Maybe you should try overriding json renderer class and hook up a custom one, where you can check for status code and detail key in response data, then re-format the value appropriately.
I never tried that, so I can't give you the exact codebase, but this is the only approach I can think of to have consistent response.

Related

Django/Django Channels - weird looking json response with double \ between each field

Hello I'm trying to do a real time friend request notification system and having this weird looking json response. I'm new to backend development and django (1st year software engineering student). Im just wondering if this is normal since i havent seen anything like this and if theres a way to fix it. Ive worked on a chat app before but it was just all text messages and so I got confused when it comes to django models. I have tried multiple ways I found but only this works. I think it might be because I called json.dumps twice but if i remove either of them, it wont work. Thank you
When a user sends a friend request, this is what i got back from the web socket(with double \ for each field)
Heres the code
//views.py
class SendRequestView(views.APIView):
permission_class = (permissions.IsAuthenticated,)
def post(self, request, *args, **kwargs):
receiver_username = self.kwargs['receiver_username']
if receiver_username is not None:
receiver = get_object_or_404(User, username=receiver_username)
request = ConnectRequest.objects.create(sender=self.request.user, receiver=receiver)
notification = ConnectNotification.objects.create(type='connect request', receiver=receiver, initiated_by=self.request.user)
channel_layer = get_channel_layer()
channel = f'notifications_{receiver.username}'
async_to_sync(channel_layer.group_send)(
channel, {
'type': 'notify',
'notification': json.dumps(ConnectNotificationSerializer(notification).data, cls=DjangoJSONEncoder),
}
)
data = {
'status': True,
'message': 'Success',
}
return JsonResponse(data)
// consumer.py
class ConnectNotificationConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
user = self.scope['user']
group_layer = f'notifications_{user.username}'
await self.accept()
await self.channel_layer.group_add(group_layer, self.channel_name)
async def disconnect(self, close_code):
user = self.scope['user']
group_layer = f'notifications_{user.username}'
await self.channel_layer.group_discard(group_layer, self.channel_name)
async def notify(self, event):
notification = event['notification']
await self.send(text_data=json.dumps({
'notification': notification
})
)

Why is my model form failing to validate and save in Django?

I am sending django 2 forms in a post request 1 which works as intended and the other which fails to validate and as such is not saved.
the code for the form is here :
class Pick_Vulns(ModelForm):
class Meta:
model = ReportVuln
fields = "__all__"
widgets = {
'Vuln' : Select(attrs={'class':'form-control'}),
'Report' : HiddenInput(attrs={'Value': reportcount}),
}
This form loads correctly on the page with no issues but when the post request is sent it seems to be not be accepted and fails to validate.
an example of the post request :
a post request to the server the first param being a csrf token the next for being the working form the last two being part of the form that does not function.
The code for the post request within my django view is as follows :
def post(self, request):
forminput = Reportform(request.POST or None)
returnedvulns = Pick_Vulns(request.POST or None)
if request.user.is_staff == True:
if forminput.is_valid():
forminput.save()
if returnedvulns.is_valid():
returnedvulns.save()
forminput = Reportform
inoutform = {
'Reportform': forminput,
'reportvulns': pickvulns,
'ReportCount': Reportcount
}
return render(request, self.template_name, inoutform, )
the model to which the form is saved is here :
class ReportVuln(models.Model):
Vuln = models.ForeignKey(Vulnerability, on_delete=models.SET_NULL, null=True, related_name='Vulnerability')
Report = models.ForeignKey(Report, on_delete=models.SET_NULL, null=True) # allows for the referencing of reports
To my memory this has been working in the past and after some minor changes it seems to have broken.
error i recieve is :
ValueError at /reporting/Create
The ReportVuln could not be created because the data didn't validate.
Request Method: POST Request URL:
http://127.0.0.1:8000/reporting/Create Django Version: 3.0.2
Exception Type: ValueError Exception Value:
The ReportVuln could not be created because the data didn't validate.
if you need any more information please ask this is a key component and needs fixing. Thank you.

how to load data from a flask url and console log the data n react?

I want to load a json file that I get from a url generated in flask.
After d3.json(url, function) I'm trying to console log the json, but nothing happens and I dont know what's wrong. So maybe someone can help
This is basically my code:
Component where I want to display a graph (Display.js):
import React, { Component } from 'react';
import "./Display.css";
import * as d3 from "d3";
export default class Display extends Component {
componentWillReceiveProps() {
const url = "http://localhost:5000/company?company_name=" + this.props.inputDataFromParent //the url depends on the input the user made. InputDataFromParent is the passed value (from the parent) that the user typed in
d3.json(url, function (data) {
console.log(data)
})
}
when I type in an input (company name) that doesnt exist in the database I get an error: "Uncaught (in promise) SyntaxError: Unexpected token N in JSON at position 0"at (index):1
when I type in an input that does exist nothing happens in the console.
Here's my main.py:
import flask
from pandas import DataFrame
from models import company_search
from flask import request
from models import subsidiaries
app=flask.Flask("__main__")
#app.route("/company")
def result():
if request.method == 'GET':
company_name = request.args.get('company_name', None)
if company_name:
return subsidiaries(company_name)
return "No place information is given"
app.run(debug=True)
and this is models.py (neo4j is used as the database):
def subsidiaries(eingabe):
if regex_eingabe_kontrolle(eingabe):
namelistdf = graph.run("MATCH (c:Company)-[rel:Relation]->(d:Company) WHERE rel.relation_group='OWNERSHIP' AND rel.percent_share >= 50 AND c.company_name= $eingabe RETURN c, rel, d",eingabe=eingabe).to_data_frame()
if namelistdf.empty:
return "No company with this name exists"
namelistjson = namelistdf.to_json(orient="records",date_unit="s",default_handler=str)
return namelistjson
else:
return "Please enter a valid company name"
I get the data from a neo4j database.
It's not about to console log the data because I want to generate a graph from the data. The console log is only for testing if the data is right. But now it seems that the data isn't passed to d3.json(url, function (data) correctly
Your problem occurs in your main file when you return No place information is given. Since this is not in JSON format, JavaScript fails to parse it causing the error to be thrown. To fix this, you can change it to:
import json
#app.route("/company")
def result():
if request.method == 'GET':
company_name = request.args.get('company_name', None)
if company_name:
return subsidiaries(company_name)
return json.dumps("No place information is given")
If you do the same for No company with this name exists and Please enter a valid company name, you won't get anymore JSON deserialization errors. However, D3 may still throw errors because it does not know what to do with a string.
You may want to return HTTP status codes along with your error messages so you can control the error. To do this in Flask, you can return the text along with the status code. For example: return json.dumps("Error message"), 400 will return the JSON string Error message with the status code 400. Since d3.json makes an underlying call to the JavaScript fetch function, you should be able to access the status code by using data.status.

Tornado POST request not detecting json input as argument

I have written a service which takes a json as input. I am using the website hurl.it to send post requests to check. Below is my code snippet:
class BatchSemanticSimilarityHandler(tornado.web.RequestHandler):
def post(self):
self.set_header('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Credentials', 'true')
self.set_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
self.set_header('Access-Control-Allow-Headers','Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token')
data = json.loads(self.request.body)
apikey = data["apikey"]
try:
UA = self.request.headers["User-Agent"]
except:
UA = "NA"
if bool(usercoll.find_one({"apikey":apikey})) == True:
sentence = data["sentence"]
sentence_array = data["sentence_array"]
n = data["num_of_results"]
if sentence is None or sentence_array is [] or apikey is None or n is None:
self.set_status(200)
output = {"error":[{"code":334,"message":"Bad Input data"}]}
misscoll.insert({"apitype":"batchsemanticsimilarity","timestamp":datetime.datetime.now(), "ip":self.request.remote_ip, "useragent":UA, "uri":self.request.uri,"apikey":apikey, "output":output, "input":{"s1":sentence,"s2":sentence_array}})
self.write(output)
return
results = nb.get_similar(sentence, sentence_array, apikey, n)
print "results is",results
output = {"similar_sentences": results, 'credits':'ParallelDots'}
hitscoll.insert({"apitype":"batchsemanticsimilarity","timestamp":datetime.datetime.now(), "ip":self.request.remote_ip, "useragent":UA, "uri":self.request.uri,"apikey":apikey, "output":output, "input":{"s1":sentence,"s2":sentence_array}})
self.write(output)
return
else:
rejectcoll.insert({"apitype":"batchsemanticsimilarity","apikey":apikey,"timestamp":datetime.datetime.now(), "ip":self.request.remote_ip, "useragent":UA, "url":self.request.uri})
self.write({"error":[{"code":333,"message": "Bad Authentication data"}]})
return
The json that I am giving as the body of the request is as below:
{
"sentence": "BJP leads in Bengaluru civic body`s poll, all eyes on JD(S)",
"sentence_array": [
"Narendra Modi is the prime minister",
"Sonia Gandhi runs Congress",
"Sachin is a good batsman"
],
"apikey": "DyMe1gSNhvMV1I1b20a7KARYIwuQX5GAQ",
"num_of_results": 2
}
I have verified on jsonlint that this is a valid JSON.
However while sending the request it gives me below error:
ValueError: No JSON object could be decoded
Can anyone please help me sort this out!!
The JSON object that you are passing in POST request is encoded into the url.
JSON library cannot read the encoded data.So you need to decode the url first.
Decoding of url can be done using urlparse library in python.so you need something like this.
post_data=urlparse.parse_qsl(self.request.body)
According to your need of final format to read there are various methods in urlparse.check this
or
As specified in the docs you can override a method to enable JSON parsing
def prepare(self):
if self.request.headers["Content-Type"].startswith("application/json"):
self.json_args = json.loads(self.request.body)
else:
self.json_args = None
check this

Tastypie how to get custom Json Response after injecting Post data

i want to have custom json response after data post sendind to my Tastypie API models django.
class MyModelResource(ModelResource):
my_field=""
class Meta:
queryset = MyModel.objects.all()
resource_name = 'nick_name'
authentication = ApiKeyAuthentication()
authorization = DjangoAuthorization()
def hydrate(self, bundle):
#on recupere les donnée injectée par bundle.data['title']
#et on inject les donnée via bundle.obj.title
#bundle.data['my_field'] ="1234"
bundle.obj.my_field=bundle.data['my_field']
self.my_field = bundle.data['my_field']
return bundle
def wrap_view(self, view):
"""
Wraps views to return custom error codes instead of generic 500's
"""
#csrf_exempt
def wrapper(request, *args, **kwargs):
try:
callback = getattr(self, view)
response = callback(request, *args, **kwargs)
if request.is_ajax():
patch_cache_control(response, no_cache=True)
lst_dic=[]
mon_dic = dict(success=True, my_field=self.my_field
)
# response is a HttpResponse object, so follow Django's instructions
# to change it to your needs before you return it.
# https://docs.djangoproject.com/en/dev/ref/request-response/
lst_dic.append(mon_dic)
response = HttpResponse(simplejson.dumps(lst_dic), content_type='application/json')
return response
except (BadRequest, fields.ApiFieldError), e:
return HttpBadRequest({'success':False,'code': 666, 'message':e.args[0]})
except ValidationError, e:
# Or do some JSON wrapping around the standard 500
return HttpBadRequest({'success':False,'code': 777, 'message':', '.join(e.messages)})
except Exception, e:
# Rather than re-raising, we're going to things similar to
# what Django does. The difference is returning a serialized
# error message.
return self._handle_500(request, e)
return wrapper
My problem here, i can't grab the self.my_field value to put in mon_dic, i always have data object, not value...
thx for help
EDIT : Add my_field global variable, and then grab value from bundle that's it ;)
Maybe I am not understanding what you want to do here. But wrap_view is for handling customer error responses. If all you want to do is return the data that was posted, you can set always_return_data to true in your Meta:
class Meta:
always_return_data = True
Or if you want to control what data gets sent back, you can use the dehydrate method:
def dehydrate(self, bundle):
bundle.data['custom_field'] = "Whatever you want"
return bundle