flask-marshmallow validation on field data not working - python

Hi I made my dto something like this
class MyRequestDto(ma.Schema):
#pre_load
def wrap_data(self, in_data, **kwargs):
return {"rooms": in_data}
rooms = ma.Dict(ma.String, ma.Dict(ma.Integer, ma.String))
and I want to send request something like this :
{
"1faf8f07-2977-180e-7bc2-b5adf8badasda": {"student_id":11210687,"room_id":"100"}
}
but getting error like this
{
"rooms": {
"1faf8f07-2977-180e-7bc2-b5adf8badasda": {
"value": {
"student_id": {
"key": [
"Not a valid integer."
]
},
"room_id": {
"key": [
"Not a valid integer."
]
}
}
}
}
}
What I can do to pass data correctly in the required format?

Integer type supports cast.
From the documentation:
class marshmallow.fields.Integer(*, strict: bool = False, **kwargs)[source]
An integer field.
Parameters
strict – If True, only integer types are valid. Otherwise, any value castable to int is valid.
kwargs – The same keyword arguments that Number receives.
So try:
class MyRequestDto(Schema):
#pre_load
def wrap_data(self, in_data, **kwargs):
return {"rooms": in_data}
rooms = Dict(String, Dict(String, Integer))
It will automatically handle the str for room_id.
If you want to keep room_id as str then you need to define a Custom Field.
Example:
class CustomField(Field):
def _serialize(self, value, attr, obj, **kwargs):
if isinstance(value, (str, int)):
return value
raise ValidationError("Value is not int or str")
def _deserialize(self, value, attr, data, **kwargs):
if isinstance(value, (str, int)):
return value
raise ValidationError("Value is not int or str")
class MyRequestDto(Schema):
#pre_load
def wrap_data(self, in_data, **kwargs):
return {"rooms": in_data}
rooms = Dict(String, Dict(String, CustomField))

Related

How to convert string to key value in python

I have a Django Application and want to convert a value from a string field which is comma separated to a key vaule pair and add it to a json data block.
class MyClass1(models.Model):
keywords = models.TextField(_('Keywords'), null=True, blank=True)
Example of list:
blue,shirt,s,summer,for women
The JSON data in my code
data = {
"name": self.name,
"type": self.type,
...
"keywords": []
}
I want to split the comma separated string of self.keywords and append it to the keywords field in my json, but as a array like this:
{
"name": keyword,
},
I do the split with the split function, but dont know how to create a key value pair as array and append to keywords.
Expected output:
data = {
"name": "Name of item",
"type": "Type of item",
...
"keywords": [
{
"name": "blue"
},
{
"name": "shirt"
},
...
]
}
You can work with .split():
data = {
'name': self.name,
'type': self.type,
# …
'keywords': [{'name': n} for n in self.keywords.split(',')],
}
It might however be better to work with a custom field. You can define such field with:
from django.db import models
class ListAsCharField(models.Field):
def __init__(self, separator=',', *args, **kwargs):
self.separator = separator
super().__init__(*args, **kwargs)
def get_db_prep_value(self, value, connection, prepared=False):
if isinstance(value, (str, type(None))):
value = self.separator.join(str(x) for x in value)
return super().get_db_prep_value(value, connection, prepared)
def from_db_value(self, value, expression, connection):
if isinstance(value, str):
return value.split(self.separator)
def to_python(self, value):
if isinstance(value, str):
value = value.split(self.separator)
return value
then you can use this in the model to automatically do the wrapping/unwrapping from a list:
class MyClass1(models.Model):
keywords = ListAsCharField(
max_length=256, verbose_name=_('Keywords'), null=True, blank=True
)
Then you can process this with:
data = {
'name': self.name,
'type': self.type,
# …
'keywords': [{'name': n} for n in self.keywords],
}

Check if List contains object without fields

I have an API response that can respond with an array like this:
[
{
"id": 1,
"title": "Warning"
},
{
"id": 2,
"title": "Warning"
}
]
sometimes it can respond just empty array
[]
in my case i created a class for this object.
Something like this:
class Warning:
def __init__(self, data: Dict):
if bool(data):
self.id: int = data["id"]
self.title: str = data["title"]
else:
pass
if __name__ == '__main__':
data = {
"id": 123455,
"title": "Warning"
}
empty_data = {}
object1: List[Warning] = [Warning(data)]
object2: List[Warning] = [Warning(empty_data)]
if object1:
print("we have a warnings")
if object2:
print("we don't have warnings")
I can't understand, how can I check if i get List of Object with empty fields like object2?
I would suggest looking at the __bool__ class method which enables you to determine the boolean value of a python class.
However, you will also need to decide what the boolean value of the list should be e.g. should bool([Warning(empty_data), Warning(data)]) return False or True?

How to Read URL param and body typing in Fast API Python

I want to create a generic endpoint definition in Fast API Python that reads URL path parameter and then calls a specific method to do a derealisation.
But I always get
422 Unprocessable Entity
So I expect that it works like so:
/answer/aaa -> handle_generic_answer -> read_item_aaa, type body to ModelAAA
/answer/bbb -> handle_generic_answer -> read_item_bbb, type body to ModelBBB
etc.
Here's the generic endpoint code:
#app.post("/answer/{type}")
def handle_generic_answer(type: str, item):
# I also tried
# def handle_generic_answer(type: str, item: Any):
# or
# def handle_generic_answer(type: str, item: Optional):
switcher = {
'aaaa': read_item_aaa,
'bbb': read_item_bbb,
'nothing': unrecognised_answer
}
func = switcher.get(type, unrecognised_answer)
print('answer >> ' + type)
func(item)
then I have separate methods called based on a type value:
def read_item_aaa(item: ModelAAA):
update_aaa(item)
return {"type": "aaa", "result": "success"}
def read_item_bbb(item: ModelBBB):
update_bbb(item)
return {"type": "bbb", "result": "success"}
and a default -
def unrecognised_answer(type):
print("unrecognised_answer")
raise HTTPException(status_code=400, detail="answer type not found")
return {}
models are defined like this:
from pydantic import BaseModel, Field
class ModelAAA(BaseModel):
field1: str
field2: list = []
But whether I call
http://localhost:8000/answer/aaa
or http://localhost:8000/answer/some-other-url
I always get 422:
{
"detail": [
{
"loc": [
"query",
"item"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
You forgot to annotate body parameter item.
Without this item is treated as query str parameter. For example:
#app.post("/answer/{type}")
def handle_generic_answer(type: str, item: Union[ModelAAA, ModelBBB]):

cerberus schema validator for tuples

I have a variable declaration as follows
my_var = typing.List[typing.Tuple[int, int]]
and I want to write a validator as follows
schema_validator = "my_var": {
"type": "list",
"empty": False,
"items": [
{"type": "tuple"},
{"items": [
{"type": "int"}, {"type": "int"}
]}
]
}
In Cerberus documentation it does not specify a validator example for tuples.
How to accomplish this?
Given your typevar typing.List[typing.Tuple[int, int]], you expect an arbritrary length list of two-value tuples where each value is an integer.
class MyValidator(Validator):
# add a type definition to a validator subclass
types_mapping = Validator.types_mapping.copy()
types_mapping['tuple'] = TypeDefinition((tuple,), ())
schema = {
'type': 'list',
'empty': False,
'schema': { # the number of items is undefined
'type': 'tuple',
'items': 2 * ({'type': 'int'},)
}
}
validator = MyValidator(schema)
It's important to understand the difference of the items and the schema rule.
Mind that the default list type actually maps to the more abstract Sequence type and you might want to add another, stricter type for that.
While this isn't the cleanest solution, it will certainly do what you want.
from cerberus import Validator, TypeDefinition
class MyValidator(Validator):
def __init__(self, *args, **kwargs):
# Add the tuple type
tuple_type = TypeDefinition("tuple", (tuple,), ())
Validator.types_mapping["tuple"] = tuple_type
# Call the Validator constructor
super(MyValidator, self).__init__(*args, **kwargs)
def _validate_is_int_two_tuple(self, is_int_two_tuple, field, value):
''' Test that the value is a 2-tuple of ints
The rule's arguments are validated against this schema:
{'type': 'boolean'}
'''
if is_int_two_tuple:
# Check the type
if type(value) != tuple:
self._error(field, "Must be of type 'tuple'")
# Check the length
if len(value) != 2:
self._error(field, "Tuple must have two elements")
# Check the element types
if type(value[0]) != int or type(value[1]) != int:
self._error(field, "Both tuple values must be of type 'int'")
data = {"mylist": [(1,1), (2,2), (3,3)]}
schema = {
"mylist": {
"type": "list",
"schema": {
"type": "tuple",
"is_int_two_tuple": True
}
}
}
v = MyValidator(schema)
print("Validated: {}".format(v.validate(data)))
print("Validation errors: {}".format(v.errors))
print("Normalized result: {}".format(v.normalized(data)))
So as bro-grammer pointed out, the custom data types will get you validation of the types, but that's it. From the schema that you provided, it looks like you also want to validate other features like the length of the tuple and the types of the elements in the tuple. Doing that requires more than just a simple TypeDefinition for tuples.
Extending Validator to include a rule for this specific use-case isn't ideal, but it will do what you want. The more comprehensive solution would be to create a TupleValidator subclass that has rules for validating length, element-types, order, etc. of tuples.

Convert bool values to string in json.dumps()

I'm trying to convert a python dict into json, however the API I'm accessing doesn't take bool value instead it uses "true"/"false" string.
Example:
dct = { "is_open": True }
json.dumps(dct)
currently gives a bool output: { "is_open": true }
but what I want is lower-case string output:
{ "is_open": "true" }
I tried json.dumps(dct, cls=MyEncoder) but it doesn't work, only non-native object get passed to MyEncoder default.
class MyEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, bool):
return str(o).lower()
return super(MyEncoder, self).default(o)
Any help would be great.
(Btw this is not my API I'm accessing, so I can't modify the API to access true false value instead of the string alternative.)
If it were me, I'd convert the Python data structure to the required format and then call json.dumps():
import json
import sys
def convert(obj):
if isinstance(obj, bool):
return str(obj).lower()
if isinstance(obj, (list, tuple)):
return [convert(item) for item in obj]
if isinstance(obj, dict):
return {convert(key):convert(value) for key, value in obj.items()}
return obj
dct = {
"is_open": True
}
print (json.dumps(dct))
print (json.dumps(convert(dct)))
Output:
{"is_open": true}
{"is_open": "true"}

Categories

Resources