First of all you should think on what errors you want to expose:
Usually 4xx errors (Errors that are attributed to the client-side) are disclosed so the user may correct the request.
On the other side, 5xx errors (Errors that are attributed to the server-side) are usually only presented without information. In my opinion for those you should use tools like Sentry do monitor and resolve this errors, that may have security issues embedded in them.
Having this is mind in my opinion for a correct Ajax request you should return a status code and then some json to help understand what happened like a message and an explanation (when applicable).
If your objective is to use ajax to submit information I suggest setting a form for what you want. This way you get past some of the validation process with ease. I will assume the case is this in the example.
First - Is the request correct?
def test_view(request):
message = None
explanation = None
status_code = 500
# First, is the request correct?
if request.is_ajax() and request.method == "POST":
....
else:
status_code = 400
message = "The request is not valid."
# You should log this error because this usually means your front end has a bug.
# do you whant to explain anything?
explanation = "The server could not accept your request because it was not valid. Please try again and if the error keeps happening get in contact with us."
return JsonResponse({'message':message,'explanation':explanation}, status=status_code)
Second - Are there errors in the form?
form = TestForm(request.POST)
if form.is_valid():
...
else:
message = "The form has errors"
explanation = form.errors.as_data()
# Also incorrect request but this time the only flag for you should be that maybe JavaScript validation can be used.
status_code = 400
You may even get error field by field so you may presented in a better way in the form itself.
Third - Let's process the request
try:
test_method(form.cleaned_data)
except `PermissionError` as e:
status_code= 403
message= "Your account doesn't have permissions to go so far!"
except `Conflict` as e:
status_code= 409
message= "Other user is working in the same information, he got there first"
....
else:
status_code= 201
message= "Object created with success!"
Depending on the exceptions you define, different codes may be required. Go to Wikipedia and check the list.
Don't forget that response also vary in code. If you add something to the database you should return a 201
. If you just got information then you were looking for a GET request.
Responding to the questions
Django exceptions will return 500 errors if not dealt with, because if you don't know that an exception is going to happen then it is an error in the server. With exception to 404 and login requirements I would do try catch
blocks for everything. (For 404 you may raise it and if you do @login_required
or a permission required django will respond with the appropriate code without you doing anything).
I don't agree completely to the approach. As you said errors should be explicit so you should know allways what is suppose to happen and how to explain it, and make it dependable on the operation performed.
I would say a 400 error is ok for that. It is a bad request you just need to explain why, the error code is for you and for your js code so just be consistent.
(example provided) - In the text_view
you should have the test_method
as in the third example.
Test method should have the following structure:
def test_method(validated_data):
try:
my_business_logic_is_violated():
catch BusinessLogicViolation:
raise
else:
... #your code
The in my example:
try:
test_method(form.cleaned_data)
except `BusinessLogicViolation` as e:
status_code= 400
message= "You violated the business logic"
explanation = e.explanation
...
I considered the business logic violation to be a Client Error because if something is needed before that request the client should be aware of that and ask the user to do it first. (From the Error Definition):
The 400 (Bad Request) status code indicates that the server cannot or
will not process the request due to something that is perceived to be
a client error (e.g., malformed request syntax, invalid request
message framing, or deceptive request routing).
By the way, you can see the Python Docs on User-defined Exceptions so you may give appropriate error messages. The idea behind this example is that you raise a BusinessLogicViolation
exception with a different message in my_business_logic_is_violated()
according to the place where it was generated.