Marshmallow deserialization [(obj, "str"), (obj, "str"), (obj, "str"), ...] - flask-marshmallow

I'm working with an existing mssql database, where I'm not able to make any changes. Trying to make an API using Flask and Marshmallow. I have some issues deserializing the following query returning all people working on a project.
query = (
sa.session.query(Employee, sa.func.sum(JobEntry.number_registered).label("total"))
.join(JobEntry, Employee.employee_hashkey==JobEntry.employee_hashkey)
.filter(JobEntry.project_number==f'{project_number}')
.group_by(Employee)
).limit(3).all()
The query returns
print(query)
[(<Employee 188ED6A858997A48FDA53A404779A16F>, 229.0), (<Employee 1D40AB9C2A973C2BD33B1EF6108D2A70>, 2.0), (<Employee 38584E42E883131DC35151E4922A8094>, 176.75)]
The Employee contains the name, id, etc. How would I create a marshmallow schema returning, the following example.
[
{"name": "somename a", "total" 229.0, "id": 11},
{"name": "somename b", "total" 2.0, "id": 22},
{"name": "somename c", "total" 176.75, "id": 33}
]
Being a noob, I have experimented a bit... The following code returns almost what I want. But I get "Employee." in my keys ...
class ProjectUsersSchema(Schema):
class Meta:
model = Employee
fields = (
"Employee.name",
"Employee.id"
"total"
)
# returns "[{"Employee.name": "somename a", "total" 229.0, "Employee.id": 11}, ..."

Made a temporary f#ckly fix using nested schemas by including .split(".")[-1] into my snake_case to PascalCase schema
def pascalcase(s):
part_lst = s.split(".")[-1].split("_") # todo fix split(".")[-1]
if len(s) <= 2:
return "".join(i.upper() for i in part_lst)
else:
return "".join(i.title() for i in part_lst)
class PascalCaseSchema(ma.SQLAlchemyAutoSchema):
def on_bind_field(self, field_name, field_obj):
field_obj.data_key = pascalcase(field_obj.data_key or field_name)`

Related

Design pattern for DRY approach to dynamic serializer fields

Basically, what I want to achieve is to make the list of fields in a serializer be optionally dynamic depending on whether the user has provided the list of fields they are interested in.
Here's my serializer for DRF serializer:
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
fields = self.context['request'].query_params.get('fields')
if fields:
fields = fields.split(',')
allowed = set(fields)
existing = set(self.fields.keys())
for field_name in existing - allowed:
self.fields.pop(field_name)
And my serializer:
class MySerializer(serializer_mixins.DynamicFieldsModelSerializer, serializers.ModelSerializer):
# fields...
This achieves the goal of not including the fields that the user has not mentioned in fields param of the queryset. But! We end up with actual query to the database that fetches the entire set of fields. This issue, in turn, could be solved by just adding the following code to the view:
class Rfs(ListAPIView):
serializer_class = MySerializer
def get_queryset(self):
qs = ...
fields = request.query_params.get('fields')
if fields:
qs = qs.only(*fields.split(','))
return qs
However, fills like two issues issues here:
non-DRY pattern since we have to repeat ourselves both in the view and the serializer
Sometimes it might be the case that the field name inside the queryset does not correspond exactly to the field name of the model.
So maybe there's some more elegant and Django-native solution for this usecase ?
I am using drf_queryfields
In dependence of your query_params your view will be modified
GET http://127.0.0.1:8000/snippets/
[
{
"id": 1,
"title": "",
"code": "foo = \"bar\"\n",
"linenos": false,
"language": "python",
"style": "friendly"
},
{
"id": 2,
"title": "",
"code": "print \"hello, world\"\n",
"linenos": false,
"language": "python",
"style": "friendly"
}
]
GET http://127.0.0.1:8000/snippets/?fields=id,code
[
{
"id": 1,
"code": "foo = \"bar\"\n",
},
{
"id": 2,
"code": "print \"hello, world\"\n",
}
]
I hope that´s it what you would like to achieve.

Getting undefined when I drill into my redux store

I'm using useSelector to retrieve my store. However when I console.log(items) there is data but when I do this console.log(items.totalAmount) I get undefined.
import {useSelector} from 'react-redux';
const items = useSelector(state => state.items);
//I'm able to see the data
console.log(items)
The data when its logged
[{"code": "SR71", "description": "Keyboard", "quantity": 1, "selling_price": 166.99, "totalAmount": 166.99}, {"code": "10", "description": "Cement", "quantity": 7, "selling_price": 20, "totalAmount": 140}]
//I get undefined
console.log(items.totalAmount);
Could any please explain what am I doing wrong.
items looks like this when you do console.log:
[
{..., "totalAmount": 166.99},
{..., "totalAmount": 140},
...
]
So you can access totalAmount of each items like: items[0].totalAmount
And for getting the totalAmount of all items, you can use:
let sum = Array.from(items).reduce((acc, cur) => acc + cur.totalAmount, 0)
Also i used Array.from since there is no guaranty for items to be a serializable (normal) array. (as you confirmed that it was non-serializable .)

Using django rest framework, how to add new nested child object for existing parent object

I am trying to build a data storage for time series data, for this I have created nested objects Coin and Data, where Coin is parent object and contains Data entries that each data entry is individual object. at this moment my code creates nested object Coin[Data] as I build create function within CoinSerializer, but I could not use proper method to add/create child object within existing Coin object
In my python virtual environment I've been using django 2.1.4 drf 3.9 and python 3.6.. also as a backend db engine for my project I got mongodb and use djongo 1.2 to maintain it
Any suggested idea or way for my problem would be greatly appreciated, as its my first post ever and sorry for any Inappropriate style..
models.py
class Coin(models.Model):
coin_name = models.CharField(max_length=100,blank=True)
class Data(models.Model):
coin = models.ForeignKey(Coin, related_name='data', on_delete=models.CASCADE,blank=True)
date = models.DateField(("Date"),blank=True)
open = models.FloatField(null=True, blank=True)
high = models.FloatField(null=True, blank=True)
low = models.FloatField(null=True, blank=True)
close = models.FloatField(null=True, blank=True)
class Meta:
unique_together = ('coin', 'date',)
ordering = ['date']
def __unicode__(self):
return '%d: %d %d %d %d' % (self.date, self.open, self.high,
self.low, self.close)
serializers.py
class DataSerializer(serializers.ModelSerializer):
class Meta():
model = models.Data
fields = ('coin_id','pk','id','date','open','high','low','close')
class CoinSerializer(serializers.ModelSerializer):
data = DataSerializer(many=True)
class Meta:
model = models.Coin
fields = ('pk','id','coin_name', 'data')
def create(self, validated_data):
data = validated_data.pop('data')
coin = models.Coin.objects.create(**validated_data)
models.Data.objects.create(coin=coin, **data[0])
return coin
my result is kind of this
{
"pk": 101,
"id": 101,
"coin_name": "ripple",
"data": [
{
"coin_id": 101,
"pk": 56,
"id": 56,
"date": "2016-12-25",
"open": 4036.0,
"high": 4101.0,
"low": 3983.0,
"close": 4065.0
}
]
},
and expect to consist lots of data objects which I will add by the time in existing coin object
{
"pk": 101,
"id": 101,
"coin_name": "ripple",
"data": [
{
"coin_id": 101,
"pk": 56,
"id": 56,
"date": "2016-12-25",
"open": 4036.0,
"high": 4101.0,
"low": 3983.0,
"close": 4065.0
}
{
"coin_id": 102,
"pk": 57,
"id": 57,
"date": "2016-12-26",
"open": 4065.0,
"high": 4189.0,
"low": 3967.0,
"close": 4075.0
}
...
...
]
},
You're going about it the wrong way. You should instead make another endpoint for Data too. There you can create data and pass the id of the parent coin. Using the nested architecture is only meaningfull when you're creating both the coin and the data at the same time. In this case, just use a data endpoint to create data while passing the id of the coin
EDIT: BULK CREATE
And just to throw a little light on how to implement bulk create for several Data objects - you will need to imlement it using a loop as model.objects.create() excpects data for a single object. You could use bulk_create but it has a lot of caveats, so I would use a loop
try change your input data with more than one item in array like example:
data = [{'date': '2016-12-25', 'high': 4101.0, 'open': 0.0, 'low': 3983.0, 'close': 4065.0}, {'date': '2016-12-26', 'high': 4101.0, 'open': 0.0, 'low': 3983.0, 'close': 4065.0}]
This example have one more item in array data.
And change this line:
coin = models.Coin.objects.create(**validated_data)
models.Data.objects.create(coin=coin, **data[0])
to
coin = models.Coin.objects.create(**validated_data)
for item_data in data:
models.Data.objects.create(coin=coin, **item_data)
This will create some Data with FK is Coin created.
This is how I did it.. Inside my viewset.ModelViewSet implementation In my case.. The parent class contains a list of manyToMany objects. Im posting new objects in the manyToMany..> Creating them.. then reinserting the IDs into the post data and calling the base class. Worked out pretty simple.. and I like it's contained in the view. Im newer to Django however.. but this worked for me.
class CaseDeepViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
queryset = Case.objects.all().order_by('-id')
def get_serializer_class(self):
if self.request.method in ['GET']:
return CaseDeepSerializer
return CaseSerializer
def create(self, request):
print('IM here: ')
print(request.data)
case_interactions = request.data.pop('new_case_interactions')
listCreatedInteractions = []
for interaction in case_interactions:
print("interaction", interaction)
interaction['issara_staff'] = obj = IssaraUser.objects.get(pk=interaction.get('issara_staff'))
listCreatedInteractions.append(CaseInteraction.objects.create(**interaction).id)
request.data['case_interactions'] = listCreatedInteractions
return super().create(request)

Recommended way to transform data when using Django Rest Framework Serializers

When using Django Rest Framework Serializers, what is the recommended way to transform data? eg:
input:
{
"companyName" : "Acme , inc.",
"id": 2,
"parent": {
"id": 1
}
}
desired output:
{
"name" : "Acme , inc.",
"id": 2,
"parentId": 1
}
Use Serializer Method Field:
class Serializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
parentId = serializers.SerializerMethodField()
class Meta:
model =
fields = ('name', 'parentId')
def get_name(self, obj):
#write logic
def get_parentId(self, obj):
#write logic

Enforce that a Grape Entity always returns an array?

How can I enforce that my Grape Entity always returns an array (collection) even if its just a singular object? I have heard that some people create a helper method that gets called inside their endpoint, but I have not found any examples of anyone doing that, online.
The default functionality of an Entity is that it returns an object if only a single document (mongoid object) is returned. If a collection of documents is returned then it returns an array, I dont want my client application having to do a check every time to see if an object or an array got returned from my API.
## Resource (HTTP Endpoint)
desc 'List departments a user can and cannot access'
params do
requires :user_id
end
get :department_access do
#user = BACKBONE::User.find(#access_key.user_id)
requires_admin!
user = BACKBONE::User.find(params[:user_id])
can_access = BACKBONE::Department.user_can_access(user)
no_access = BACKBONE::Department.user_cannot_access(user)
present_success can_access
present :can_access, can_access, with: BACKBONE::Entities::DepartmentBase
present :no_access, no_access, with: BACKBONE::Entities::DepartmentBase
end
-
## Entity
module BACKBONE
module Entities
class DepartmentBase < BACKBONE::Entities::Mongoid
expose :name
expose :prefix
with_options(format_with: :mongo_id) do
expose :company_id
end
end
end
end
JSON Response
{
"status": "success",
"request_time": 0.009812,
"records": 1,
"can_access": {
"id": "59699d1a78cee4f8d07528fc",
"created_at": "2017-07-14T21:42:02.666-07:00",
"updated_at": "2017-07-14T21:42:02.666-07:00",
"name": "Tenant Improvement",
"prefix": "CACC",
"company_id": "596927fb670f6eec21c4f409"
},
"no_access": {
"id": "59699cca78cee4f8d07528fb",
"created_at": "2017-07-14T21:40:42.005-07:00",
"updated_at": "2017-07-14T21:40:42.005-07:00",
"name": "Field Operations",
"prefix": "CACC",
"company_id": "596927fb670f6eec21c4f409"
}
}
a coworker and I came up with a solution with a helper, create a helper method that always returns an array:
def present_array(key, data, entity)
d = (data.class.respond_to? :count) ? [data] : data
d = [] if d.nil?
present key, d, entity
end

Resources