Error codes while form validation in django rest framework - django-rest-framework

Is there a nice and clean way to use custom error codes while form validation? Eg:
{
"name": {
"code": 121,
"message": "This field can't be blank."
}
}
instead of:
{
"name": ["This field can't be blank."]
}
Thank you.

You can overwrite the error message in the __init__ method of your serializer:
self.fields['field_name'].error_messages['error_message_key'] = your_custom_error
You can find all error_messages keys related to each Field in the documentation. For instance, CharFields error message keys are required, max_length, min_length.
If you're using custom field from DRF:
name = serializers.CharField(
...,
error_messages={error_message_key: your_custom_error}
)
If you want to raise more specific errors, I encourage you to take a look at Raising ValidationError from the official documentation and ValidationError from DRF documentation.

Related

How to change priority of WTForms validators?

tl;dr: I need to change the order in which WTForms validators validate the user input. How do I do that?
Details:
Flask code:
class SampleForm(Form):
user_email = user_email_field
...
#api.route('/sample-route')
class ClassName(Resource):
#api.expect(sample_payload)
#api.marshal_with(sample_response)
def post(self):
form = SampleForm(formdata=MultiDict(api.payload))
if not form.validate():
return {"form_errors": form.errors}, 400
...
WTForms validation field:
user_email_field = EmailField('Email Address',[
validators.InputRequired(Errors.REQUIRED_FIELD),
validators.Length(min=5, max=256),
validators.Email(Errors.INVALID_EMAIL_ADDRESS),
])
Problem is, user_email is checked by validators in the wrong order. I send a request with the following body:
{
"user_email": ""
}
I get this response:
{
"form_errors": {
"user_email": [
"'' is too short"
]
}
}
As you see, despite being 2nd in the list of validators, validators.Length() kicks in before everything else.
If I comment it out in the validation field like that:
user_email_field = EmailField('Email Address',[
validators.InputRequired(Errors.REQUIRED_FIELD),
# validators.Length(min=5, max=256),
validators.Email(Errors.INVALID_EMAIL_ADDRESS),
])
then the exact same request will yield a desired response:
{
"errors": null,
"success": null,
"form_errors": {
"user_email": [
"REQUIRED_FIELD"
]
}
}
However, this is not a working solution because, of course, then eMail won't be checked for its length.
Question:
How do I change the priority of these validators? How do I make WTForms always check the user input with validators.InputRequired() FIRST and with validators.Length() SECOND and not the other way around?
As per your question:
You can use custom validators for that purpose. Then you can change the order of validation by if, elif, else logic within.
class Test(Form):
test_field = StringField('Test_Field')
submit = SubmitField()
def validate_test_field(self, test_field):
try:
if len(test_field.data) == 0:
raise wtforms.validators.ValidationError('Input is required for this field!')
elif len(test_field.data) not in range(5, 257):
raise wtforms.validators.ValidationError(f'Error in input length.')
except TypeError as e:
raise wtforms.validators.ValidationError('Input is required for this field!')
Check the documentation for custom validators here

Is there a better way to construct a controller with multiple parameters the might not be required?

I'm making a simple spring boot application and i have to get a list of objects, which i filter by parameters that aren't required. So basically the user can send the parameters, but doesn't have to. Best solution that i found is the use of #RequestParam annotation but it doesn't seem to work. If i put both parameters it works perfectly but if I set just one or none i get an error. Do i have to overload methods for every case or is there a smoother way to deal with this(if possible by still using the get request)?
My controller:
#RequestMapping(
value = "/fruits",
params = {"apple", "orange"},
method = GET)
public ResponseEntity<List<Fruit>> getFruits(
#RequestParam(value = "apple", required = false, defaultValue = "")
List<Apple> apple,
#RequestParam(value ="orange", required = false, defaultValue = "")
List<Orange> orange) {
List<Fruit> fruit = projectService.getFruits(apple, orange);
return ResponseEntity.ok().body(fruit);
}
Error:
{
"timestamp": "2019-01-20T21:26:52.287+0000",
"status": 400,
"error": "Bad Request",
"message": "Parameter conditions \"apple, orange\" not met for actual
request parameters: ",
"path": "/api/fruits"
}
Remove this line:
params ={"apple","orange"}
and it will work.
Because you don't need to call twice, it's enough with those #RequestParam annotations.

createUser update related field - understanding relation

I need to set a related field's value on create, is this possible?
Details:
I have a User model with fields: email, displayname.
I have a Verify model with fields: code, action.
I created a relation between the two models like this:
I want to createUser and set the related fields of code and action at the same time. I tried this:
mutation {
createUser
(
email:"noit#mail.com",
displayname:"noit",
password:"123",
code: "this is a code",
action: "REGISTER"
) {
id
}
}
This fails with:
{
"data": null,
"errors": [
{
"message": "Unknown argument 'code' on field 'createUser' of type 'Mutation'. (line 2, column 76):\n createUser(email: \"noit#mail.com\", displayname: \"noit\", password: \"123\", code: \"this is a code\", action: \"REGISTER\") {\n ^",
"locations": [
{
"line": 2,
"column": 76
}
]
},
{
"message": "Unknown argument 'action' on field 'createUser' of type 'Mutation'. (line 2, column 100):\n createUser(email: \"noit#mail.com\", displayname: \"noit\", password: \"123\", code: \"this is a code\", action: \"REGISTER\") {\n ^",
"locations": [
{
"line": 2,
"column": 100
}
]
}
]
}
We specifically designed the Graphcool API to handle cases like this as simple as possible, you can do it like this:
mutation {
createUser (
email:"noit#mail.com",
displayname:"noit",
password:"123",
blahVerify: {
code: "this is a code",
action: "REGISTER"
}) {
id
blahVerify {
id
}
}
}
Note the nested blahVerify object argument.
This answer to a similar question goes a bit more into detail and also shows how you can use GraphQL variables to send nested mutations from Apollo Client.
As a sidenote, depending on the different possible value for the action of a Verify node, you might want to use an enum field rather than strings. You can read more about enum fields in the documentation.
You can do this on scaphold.io. The Logic system includes more than just mutation callbacks. You can fire functions before mutations to validate/clean input before it is saved to the DB, after to manage connections like this that will get returned in that same mutation payload, and asynchronously (like mutation callbacks) for kicking off long standing tasks. You can even compose functions together to pass meta-data through a chain of function invocations.

How do i serialize objects during enrichment with stream-django and django rest framework?

Im using stream-django with django REST framework and the enriched activities are throwing "not JSON serializable" on the objects returned from enrichment, which is as expected as they have not gone through any serializing.
How do i customize the enrichment process so that it returns a serialized object from my drf serializer and not the object itself?
Some example data, not enriched:
"is_seen": false,
"is_read": false,
"group": "19931_2016-04-04",
"created_at": "2016-04-04T08:53:42.601",
"updated_at": "2016-04-04T11:33:26.140",
"id": "0bc8c85a-fa59-11e5-8080-800005683205",
"verb": "message",
"activities": [
{
"origin": null,
"verb": "message",
"time": "2016-04-04T11:33:26.140",
"id": "0bc8c85a-fa59-11e5-8080-800005683205",
"foreign_id": "chat.Message:6",
"target": null,
"to": [
"notification:1"
],
"actor": "auth.User:1",
"object": "chat.Message:6"
}
The view:
def get(self, request, format=None):
user = request.user
enricher = Enrich()
feed = feed_manager.get_notification_feed(user.id)
notifications = feed.get(limit=5)['results']
enriched_activities=enricher.enrich_aggregated_activities(notifications)
return Response(enriched_activities)
I solved it by doing the following:
property tag on the model that returns the serializer class
#property
def activity_object_serializer_class(self):
from .serializers import FooSerializer
return FooSerializer
Then used this to serialize the enriched activities. Supports nesting.
#staticmethod
def get_serialized_object_or_str(obj):
if hasattr(obj, 'activity_object_serializer_class'):
obj = obj.activity_object_serializer_class(obj).data
else:
obj = str(obj) # Could also raise exception here
return obj
def serialize_activities(self, activities):
for activity in activities:
for a in activity['activities']:
a['object'] = self.get_serialized_object_or_str(a['object'])
# The actor is always a auth.User in our case
a['actor'] = UserSerializer(a['actor']).data
return activities
and the view:
def get(self, request, format=None):
user = request.user
enricher = Enrich()
feed = feed_manager.get_notification_feed(user.id)
notifications = feed.get(limit=5)['results']
enriched_activities = enricher.enrich_aggregated_activities(notifications)
serialized_activities = self.serialize_activities(enriched_activities)
return Response(serialized_activities)
The enrich step replaces string references into full Django model instances.
For example: the string "chat.Message:6" is replaced with an instance of chat.models.Message (same as Message.objects.get(pk=6)).
By default DRF does not know how to serialize Django models and fails with a serialization error. Luckily serializing models is a very simple task when using DRF. There is a built-in serializer class that is specific to Django models (serializers.ModelSerializer).
The documentation of DRF explains this process in detail here: http://www.django-rest-framework.org/api-guide/serializers/#modelserializer.
In your case you probably need to use nested serialization and make the serialization of the object field smart (that field can contain references to different kind of objects).
There is an open issue about this on Github: https://github.com/GetStream/stream-django/issues/38. Ideally this is something the library will provide as helper/example, so any code contribution / input will help making that happen.

WebApi 2.1 Model validation works locally, but does not show missing fields when run on the server

We are using WebApi v2.1 and validating ModelState via a filter applied in the WebApiConfig class.
Fields specified as required are not listed on the error message when we run on the server (Win Server 2008R2), but they work perfectly when we run locally (on IISExpress).
The request is correctly rejected locally and on the server, but the server response does not show the missing fields.
For Example:
Given a Local request that lacks the required abbreviation and issuerName fields, the response shows as expected:
{
"message": "The request is invalid.",
"modelState": {
"value": [
"Required property 'abbreviation' not found in JSON. Path '', line 18, position 2.",
"Required property 'issuerName' not found in JSON. Path '', line 18, position 2."
]
}
When the same request is sent to the server, the response shows:
{
"message": "The request is invalid.",
"modelState": {
"value": [
"An error has occurred.",
"An error has occurred."
]
}
}
Our filter is the following:
public class ValidateModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Our data model class is decorated with the DataContract attribute, and the required fields are attributed like so:
[DataMember(IsRequired=true)]
public string IssuerName
The server is more restrictive about sending errors down ot the client. Try setting the IncludeErrorDetails flag on your httpconfiguration to verify that this is the underlying issue.
In general though turning this flag on is not the best idea, and you will want to serialize the errors down differently.
For more info:
http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx

Resources