Marshmallow: dynamically choose Schema depending on data? - python

For googlers also that question in github.
I had users of 2 types. I need to validate user data by one of 2 schemas.
I prepare awesome scratch with code of my idea, of course its not working.
class ExtraType0(Schema):
nickname = fields.String()
class ExtraType1(Schema):
id = fields.Integer()
class UserSchema(Schema):
type = fields.String()
extra = fields.Method(deserialize="get_extra_schema_by_user_type")
def get_extra_schema_by_user_type(self, obj):
if obj == 0:
# call ExtraType0 scheme and its (de)serialization
return fields.Nested(ExtraType0())
else:
# call ExtraType01scheme and its (de)serialization
return fields.Nested(ExtraType1())
# correct data
result = UserSchema().load(
{
"type": 0,
"extra": {
"id": 0
}
})
# also correct data
result1 = UserSchema().load(
{
"type": 1,
"extra": {
"nickname": "user123"
}
})
How I can proper choose schema depends on loaded in type field data?

Related

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?

Django - How to map object from another API and send in GET response

I would like to map object from another API and send in GET response. I'm going to change only id of received object. Let's assume I get data from another API in such format:
{
"id": "31242",
"name": "sth1",
"price": "44",
"data": "2017-06-07",
}
In my database I have table object1 with values:
{
"id": "123",
"name": "sth1",
},
{
"id": "124",
"name": "sth2",
},
{
"id": "125",
"name": "sth3",
}
Field name is unique both in data from API and in data from database. I receive an object named sth1. So now I would like to find it in my database and get his id, replace with id from API and send GET response. In this case my response would look in this way:
{
"id": "123",
"name": "sth1",
"price": "44",
"data": "2017-06-07",
}
At this moment this is my URL - url(r'^data/(?P<name>\w+)$', views.DataList),
but I would like to have such URL - localhost:8000/data?name=sth
Myview.py:
#api_view(['GET'])
def DataList(request, name=None):
if request.method == 'GET':
quote = getDataFromAPI().get(name)
return Response(quote)
serializers.py:
class Object1Serializer(serializers.ModelSerializer):
class Meta:
model = Object1
depth = 1
fields = '__all__'
models.py:
class Object1(models.Model):
name = models.CharField(max_length=200)
I have done it in this way:
#api_view(['GET'])
def DataList(request):
t = request.GET.get("t","")
quote = getDataFromAPI().get(t)
id = Object1.objects.get(t=t)
quote["id"] = id
return Response(quote)
But I get error:
TypeError: Object of type 'Object1' is not JSON serializable
I suppose, your view should look somewhat like this,
#api_view(['GET'])
def DataList(request):
t = request.GET.get("t","")
quote = getDataFromAPI().get(t)
id = Object1.objects.get(t=t).id #put the id of the object in the variable.
#not the object itself.
quote["id"] = id
return Response(quote)
If you want to change the url from
url(r'^data/(?P<name>\w+)$', views.DataList) to localhost:8000/data?name=sth you'd need to change your api endpoint from
#api_view(['GET'])
def DataList(request, name=None):
to
#api_view(['GET'])
def DataList(request):
name = request.GET.get("name","")
and then take the id of object from your database by querying
id = Object1.objects.get(name=name)
and then updating id in response to be sent
quote["id"] = id

Serialize a string without changes in Django Rest Framework?

I'm using Python's json.dumps() to convert an array to a string and then store it in a Django Model. I'm trying to figure out how I can get Django's REST framework to ignore this field and send it 'as is' without serializing it a second time.
For example, if the model looks like this(Both fields are CharFields):
name = "E:\"
path_with_ids= "[{"name": "E:\", "id": 525}]"
I want the REST framework to ignore 'path_with_ids' when serializing so the JSON output will look like this:
{ "name": "E:\", "path_with_ids":
[ {"name": "E:\", "id": 525} ] }
and not like this:
{
"name": "E:\",
"path_with_ids": "[{\"name\": \"E:\\\", \"id\": 525}]" }
I've tried to make another serializer class that spits out the input it gets 'as is' without success:
Serializers.py:
class PathWithIds(serializers.CharField):
def to_representation(self, value):
return value.path_with_ids
class FolderSerializer(serializers.ModelSerializer):
field_to_ignore = PathWithIds(source='path_with_ids')
class Meta:
model = Folder
fields = ['id', 'field_to_ignore']
Please help!
I ended up using a wasteful and sickening method of deserializing the array before serializing it again with the REST framework:
Serializers.py:
import json
class PathWithIds(serializers.CharField):
def to_representation(self, value):
x = json.loads(value)
return x
class FolderSerializer(serializers.ModelSerializer):
array_output = PathWithIds(source='field_to_ignore')
class Meta:
model = Folder
fields = ['id', 'array_output']
Output in the rest API:
{
"name": "E:\",
"array_output": [
{
"name": "E:\",
"id": 525
}
] }

How to access ForeignKey data without making extra queries in Wagtail(django)

I have the following two classes in my app.models and i'm using the wagtail APIs to get the data as json
class AuthorMeta(Page):
author=models.OneToOneField(User)
city = models.ForeignKey('Cities', related_name='related_author')
class Cities(Page):
name = models.CharField(max_length=30)
So, when I try /api/v1/pages/?type=dashboard.AuthorMeta&fields=title,city, it returns the following data:
{
"meta": {
"total_count": 1
},
"pages": [
{
"id": 11,
"meta": {
"type": "dashboard.AuthorMeta",
"detail_url": "http://localhost:8000/api/v1/pages/11/"
},
"title": "Suneet Choudhary",
"city": {
"id": 10,
"meta": {
"type": "dashboard.Cities",
"detail_url": "http://localhost:8000/api/v1/pages/10/"
}
}
}
]
}
In the city field, it returns the id and meta of the city. How can I get the name of the city in the response here, without making an extra query? :/
I couldn't find any solution in the Documentation. Am I missing something?
Use Django model property to return through the ForeignKey:
class AuthorMeta(Page):
author=models.OneToOneField(User)
city = models.ForeignKey('Cities', related_name='related_author')
city_name = property(get_city_name)
def get_city_name(self):
return self.city.name
Check Term Property to better understand the concept
In case you have the foreign key in a Streamfield, e.g. a PageChooserBlock, you can customize the api response by overwriting the get_api_representation of a block, as described in the example as provided here:
class CustomPageChooserBlock(blocks.PageChooserBlock):
""" Customize the api response. """
def get_api_representation(self, value, context=None):
""" Return the url path instead of the id. """
return value.url_path

auto field values in ndb.StructuredProperty

I want to store locations in google's datastore. Each entry shall have got 'sys'-fields, which shall contain information set by the datastore.
I've got the class model below and the WebService JSON request/response looks ok, but I have to set the values manually. It looks like auto_current_user_add, auto_now_add, auto_current_user and auto_now does not trigger.
from google.appengine.ext import ndb
from endpoints_proto_datastore.ndb import EndpointsModel
class Created(EndpointsModel):
by = ndb.UserProperty(auto_current_user_add=True)
on = ndb.DateTimeProperty(auto_now_add=True)
class Updated(EndpointsModel):
by = ndb.UserProperty(auto_current_user=True)
on = ndb.DateTimeProperty(auto_now=True)
class Sys(EndpointsModel):
created = ndb.StructuredProperty(Created)
updated = ndb.StructuredProperty(Updated)
class Location(EndpointsModel):
name = ndb.StringProperty(required=True)
description = ndb.TextProperty()
address = ndb.StringProperty()
sys = ndb.StructuredProperty(Sys)
When I submit a create request (location.put()) I get the following response:
{
"id": "4020001",
"name": "asdf"
}
When I set it manually using:
location.sys = Sys(created=Created(on=datetime.datetime.now(),
by=current_user),
updated=Updated(on=datetime.datetime.now(),
by=current_user))
location.put()
I get the expected result:
{
"id": "4020002",
"name": "asdf",
"sys": {
"created": {
"by": {
"auth_domain": "gmail.com",
"email": "decurgia#XYZ"
},
"on": "2015-01-27T16:05:41.465497"
},
"updated": {
"by": {
"auth_domain": "gmail.com",
"email": "decurgia#XYZ"
},
"on": "2015-01-27T16:05:41.465577"
}
}
}
How can I get those fields (sys.created.on, sys.created.by, sys.updated.on, sys.updated.by) automatically set?
In my limited work with StructuredProperty, I found it to be slower and more difficult to use than simply inserting the properties directly into the model. NDB seems to store those properties separately and perform a "join" when retrieving them. My recommendation is to use a "flat" model:
class Location(EndpointsModel):
name = ndb.StringProperty(required=True)
description = ndb.TextProperty()
address = ndb.StringProperty()
created_by = ndb.UserProperty(auto_current_user_add=True)
created_on = ndb.DateTimeProperty(auto_now_add=True)
updated_by = ndb.UserProperty(auto_current_user=True)
updated_on = ndb.DateTimeProperty(auto_now=True)
This should cause the auto_ properties to be triggered automatically.

Categories

Resources