flask-marshmallow custom fields - mongoengine

I use flask-marshmallow and mongoengine.
Also flask-restplus for my API server.
Here is my api.py
class BoardSchema(ma.Schema):
class Meta:
fields = ('no', 'title', 'body', 'tags', 'created_at', 'views')
board_schema = BoardSchema()
boards_schema = BoardSchema(many=True)
class ArticleList(Resource):
def get(self):
articles = Board.objects.all()
return boards_schema.jsonify(articles)
model.py
from datetime import datetime
from mongoengine import *
from config import DB_NAME
connect(DB_NAME)
class Board(Document):
d = datetime.now()
date = "{}-{}-{}".format(d.year, d.month, d.day)
no = SequenceField()
title = StringField(required=True)
body = StringField(required=True)
tags = ListField(StringField())
likes = ListField(StringField())
views = ListField(StringField())
password = StringField(required=True)
created_at = DateTimeField(default=date)
updated_at = DateTimeField(default=date)
When I access to /article, it's result like this ->
{
"body": "123",
"created_at": "2018-08-20T00:00:00+00:00",
"no": 1,
"tags": [
"MySQL",
"C"
],
"title": "\ud14c\uc2a4\ud2b8",
"views": [
"127.0.0.1"
]
}
in "views", ip will be added who read article.
But I want to count of all the list of views and include it to my result.
The result I wanted is here.
{
"body": "123",
"created_at": "2018-08-20T00:00:00+00:00",
"no": 1,
"tags": [
"MySQL",
"C"
],
"title": "\ud14c\uc2a4\ud2b8",
"views": 20
}
I'm new at flask-marshmallow so I'm so confused how can I solve this issue.
Thanks.

Maybe you can try like this:
class BoardSchemaCustom(ma.ModelSchema):
class Meta:
model = Board
views = ma.fields.method(deserialize="_custom_serializer")
def _custom_serializer(self, obj):
return len(obj.views)
Create instance of your custom schema:
custom_board_schema = BoardSchemaCustom()
and dump it:
dump, errors = custom_board_schema.schema.dump(Board.query.first())
>>> dump

i've got the same problem. and my code works after installing marshmallow-sqlalchemy
pip install marshmallow-sqlalchemy
see from offical documentation
https://flask-marshmallow.readthedocs.io/en/latest/

Below snippet would also work:
class BoardSchemaCustom(ma.ModelSchema):
class Meta:
model = Board
views = ma.fields.Function(lambda obj: len(obj.views))

Related

Nested serializer representation for PrimaryKeyRelatedField

I want to add and remove products to an order by only using their ids, but I want the order representation to look like a nested serializer.
My Models:
class Product(models.Model):
title = models.CharField(max_length=200)
price = models.DecimalField(max_digits=1_000, decimal_places=2)
def __str__(self):
return self.title
class Order(models.Model):
date = models.DateField()
products = models.ManyToManyField(Product, blank=True, related_name='orders')
My Serializers:
class ProductSerializer(serializers.ModelSerializer):
price = serializers.DecimalField(max_digits=1_000,
decimal_places=2,
coerce_to_string=False)
class Meta:
model = Product
fields = ['id', 'title', 'price']
class OrderSerializer(serializers.ModelSerializer):
products = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all(), many=True)
class Meta:
model = Order
fields = ['id', 'date', 'products']
I'm trying to get the below representation:
{
"id": 1,
"date": "2021-08-12",
"products": [
{
"id": 1,
"title": "Item 1",
"price": 19.99
},
{
"id": 3,
"title": "Item 3",
"price": 49.99
}
]
}
However, if I want to create the above order the json should look like this:
{
"date": "2021-08-12",
"products":[1, 3]
}
And if I want to add a product with id==2 to the above order it should look like this:
{
"id": 1,
"date": "2021-08-12",
"products":[1, 3, 2]
}
I've tried overriding the to_representation() method and adding a nested serializer there, but I have no idea how to go about it. Should it look something like this, or am I going in a completely wrong direction here?
def to_representation(self, instance):
data = super().to_representation(instance)
data['products'] = ProductSerializer(data=instance['products'])
return data
Ended up fixing it with the to_representation() method. I don't know if this is the best way to do it, but it works for now.
def to_representation(self, instance):
data = super().to_representation(instance)
products_list = []
for product_id in data['products']:
product = instance.products.get(pk=product_id)
products_list.append(
{
'id': product.id,
'title': product.title,
'price': product.price
}
)
data['products'] = products_list
return data
use Nested Serializer Relationships.
and your OrderSerializer serializer class will be like this:
class OrderSerializer(serializers.ModelSerializer):
products = ProductSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'date', 'products']

Has an invalid foreign key Django TestCase

i have a model pruchase and a model transaction, transactions have a ForeignKey from pruchase and when a try run the tests success th first test_payment_request but the second test_payment_transaction_state faile an launch the next error:
django.db.utils.IntegrityError: The row in table 'transactions_transactionmodel' with primary key '0664aefce71447699d8ca9e7677ba4cc' has an invalid foreign key: transactions_transactionmodel.purchase_id contains a value 'ba7dc5ac0e1c4b9eb009e772f405f5db' that does not have a corresponding value in purchases_purchasemodel.id.
this is my test:
import datetime
import socket
from django.test import TestCase
from .payment import PaymentTransactions
from apps.purchases.models import PurchaseModel
class PaymentTransactionsTestCase(TestCase):
def setUp(self):
self.purchase = {"purchase":PurchaseModel( total_value = 124236,
products = [
{
"name": "Aretes",
"value": "6490"
},
{
"name": "Manilla",
"value": "6.000"
}
],
purchase_date = datetime.datetime.utcnow()
),
"value":124236,
"client_ip": socket.gethostbyname(socket.gethostname())
}
def test_payment_request(self):
error, payment, transaction = PaymentTransactions().\
payment_transaction_request(**self.purchase)
self.assertFalse(error)
self.assertTrue(payment)
self.assertIn("tpaga_payment_url", payment)
self.assertIn("token", payment)
self.assertEquals(transaction.token, payment["token"])
print("paso prueba 1")
def test_payment_transaction_state(self):
purchase = {"purchase":PurchaseModel( total_value = 124236,
products = [
{
"name": "Aretes",
"value": "6490"
},
{
"name": "Manilla",
"value": "6.000"
}
],
purchase_date = datetime.datetime.utcnow()
),
"value":124236,
"client_ip": socket.gethostbyname(socket.gethostname())
}
error, payment, transaction = PaymentTransactions().\
payment_transaction_request(**purchase)
self.assertFalse(error)
error, transaction_created = PaymentTransactions().\
payment_transaction_state(transaction.id)
self.assertFalse(error)
self.assertEquals(transaction_created.state, transaction.state)
but i don't know whatshappends if someone know, please can explain me.
Make change models.SOMETHING to models.CASCADE in your field of Model Payment.

How to resolve multiple querysets in graphene.Union?

I want to create a graphene.Union type of multiple existing types. I am able to resolve it but it is not in my required format.
Schema
from graphene_django import DjangoObjectType
class ageType(DjangoObjectType):
class Meta:
model = age
class ethnicityType(DjangoObjectType):
class Meta:
model = ethnicity
class combinedType(graphene.Union):
class Meta:
types = (ageType, ethnicityType)
class Query(graphene.ObjectType):
defaultPicker = graphene.List(combinedType)
def resolve_defaultPicker(self, info):
items = []
age_q = age.objects.all()
items.extend(age_q)
ethnicity_q = ethnicity.objects.all()
items.extend(ethnicity_q)
return items
The query I am using in the graphql admin:
{
defaultPicker{
... on ageType{
id
age
}
... on ethnicityType{
id
ethnicity
}
}
}
I want to get a output like this:
{
"data": {
"defaultPicker": [
'ageType': [{
"id": "2",
"ethnicity": "American",
"ethnicityFr": "Test"
}],
'ethnicityType': [{
"id": "1",
"familyPlans": "3 kids",
"familyPlansFr": "3 enfants"
}],
]
}
}
I tried many things but couldn't find a way to resolve it.

Scalarize a nested field using marshmallow

I have two Database Models like this
class Setting(db.Model):
id = db.Column(db.Integer, id=True)
container_id = ST_db.Column(ST_db.Integer, db.ForeignKey('settings_container.id'))
setting_name = ST_db.Column(ST_db.String(50))
setting_value = ST_db.Column(ST_db.String(50))
class SettingsContainer(db.Model):
id = db.Column(db.Integer, id=True)
settings = db.relationship('Settings', backref='container', cascade='all, delete')
When I serialize the model SettingsContainer using the following schemas
class Setting(ma.Schema):
class Meta:
fields = (
'setting_name',
'setting_value',
)
class SettingsContainer(ma.Schema):
settings = ST_ma.Nested(Setting, many=True)
class Meta:
fields = (
'id',
'settings'
)
What I get is
[
{
"id": 1
"settings": [
{
"setting_name": 'name1',
"setting_value": 'value1'
},
{
"setting_name": 'name2',
"setting_value": 'value2'
},
}
"id": 2
"settings": [
{
"setting_name": 'name3',
"setting_value": 'value3'
},
{
"setting_name": 'name4',
"setting_value": 'value4'
},
]
But I want to get data in the format of
[
{
"id": 1
"settings": [['name1', 'value1'], ['name2', 'value2']]
}
{
"id": 2
"settings": [['name3', 'value3'], ['name4', 'value4']]
}
]
I want to able to extract the Nested fields, I don't understand how to do it using the ma.Function method, thanks a lot to everyone in advance.
Okay so I found out the answer, what I have to do is to use ma.Function with ma.Pluck
The appropriate serializers are.
class Setting(ma.Schema):
complete_setting = ma.Function(lambda setting:(
setting.setting_name,
setting.setting_value,
))
class SettingsContainer(ma.Schema):
settings = ma.List(ma.Pluck(Setting, 'complete_setting'))
class Meta:
fields = (
'id',
'settings'
)

My API is returning blank json, instead of desired nested schema

My schema is:
import time
from marshmallow import fields, Schema
from ramas_core.api.resources.base import CamelCaseSchema, base_filters, unfilterable_fields
class colorMapLayersSchema(CamelCaseSchema):
#raster_layer
id = fields.UUID()
visibility = fields.Boolean()
color_map_type = fields.String()
#capture
field_id = fields.UUID()
event_date = fields.DateTime()
event_date_time = fields.String()
class gridMapLayersSchema(CamelCaseSchema):
#raster_layer
id = fields.UUID()
visibility = fields.Boolean()
metric_type = fields.String()
#capture
field_id = fields.UUID()
event_date = fields.DateTime()
event_date_time = fields.String()
class indexMapLayersSchema(CamelCaseSchema):
#raster_layer
id = fields.UUID()
visibility = fields.Boolean()
index_map_type = fields.String()
#capture
field_id = fields.UUID()
event_date = fields.DateTime()
event_date_time = fields.String()
class LayersSchema(CamelCaseSchema):
color_map_layers = fields.List(fields.Nested(colorMapLayersSchema))
grid_map_layers = fields.List(fields.Nested(gridMapLayersSchema))
index_map_layers = fields.List(fields.Nested(indexMapLayersSchema))
filterable_fields = {
"capture_id": fields.UUID()
}
filterable_fields.update(base_filters)
filterable_fields.update(unfilterable_fields)
layers_schema = LayersSchema()
My API code is:
import logging
import json
import flask
from flask_smorest import Blueprint
from datetime import datetime
from ramas_core.api.resources.v1.layers import layers_schema
from ramas_core.models.ramas import User
from ramas_core.models.raster_layer import RasterLayer
from ramas_core.models.capture import Capture
from sqlalchemy import func
from sqlalchemy.dialects.postgresql import JSON
logger = logging.getLogger(__name__)
blueprint = Blueprint("layers_v1", __name__)
#blueprint.route("", methods=["GET"])
#blueprint.response(layers_schema)
def get_layers():
"""Return current user details."""
session = flask.g.session
username = flask.g.identity.username
color = session.query(
RasterLayer.id,
RasterLayer.tags['visibility'].label('visibility'),
RasterLayer.tags['legacy_sub_layer_type'].label('color_map_type'),
Capture.field_id,
Capture.capture_datetime.label('event_date'),
).join(Capture, RasterLayer.capture_id == Capture.id) \
.filter(RasterLayer.tags['legacy_layer_type'].astext == 'color').all()
grid = session.query(
RasterLayer.id,
RasterLayer.tags['visibility'].label('visibility'),
RasterLayer.tags['legacy_sub_layer_type'].label('metric_type'),
Capture.field_id,
Capture.capture_datetime.label('event_date'),
).join(Capture, RasterLayer.capture_id == Capture.id) \
.filter(RasterLayer.tags['legacy_layer_type'].astext == 'grid').all()
index = session.query(
RasterLayer.id,
RasterLayer.tags['visibility'].label('visibility'),
RasterLayer.tags['legacy_sub_layer_type'].label('index_map_type'),
Capture.field_id,
Capture.capture_datetime.label('event_date'),
).join(Capture, RasterLayer.capture_id == Capture.id) \
.filter(RasterLayer.tags['legacy_layer_type'].astext == 'index').all()
color_map_layers = flask.jsonify(color)
grid_map_layers = flask.jsonify(grid)
index_map_layers = flask.jsonify(index)
result = {"colorMapLayers": [color_map_layers], "gridMapLayers": [grid_map_layers], "indexMapLayers": [index_map_layers]}
return result
My expected response is:
{
"colorMapLayers": [
{
"colorMapType": "rgb",
"eventDate": "string",
"eventDateTime": "2020-07-28T14:33:58.464Z",
"fieldId": 0,
"id": "string",
"visibility": true
}
],
"gridMapLayers": [
{
"eventDate": "string",
"eventDateTime": "2020-07-28T14:33:58.464Z",
"fieldId": 0,
"id": "string",
"metricType": "plant_count",
"visibility": true
}
],
"indexMapLayers": [
{
"eventDate": "string",
"eventDateTime": "2020-07-28T14:33:58.464Z",
"fieldId": 0,
"id": "string",
"indexMapType": "ndvi",
"visibility": true
}
]
}
But the response I get currently is : empty json {}
Any idea on what am I doing wrong here??
I would also like to know how do I convert the dateTime to string on the go?
Also is this the correct way to define a nested array json? Do I need to manipulate the query?
Base on the source code from Marshmallow's dump and _serialize methods, it looks like a schema is parsed by looping over the attributes, checking which are in the dictionary, and ignoring missing keys. However, the keys of that are checked are based on, in your case layers_schema.__dict__ - and those are probably snake_case, while the keys of the object you return are camelCase. Could it be that changing the keys of the dictionary fixes the problem? So
result = {"color_map_layers": [color_map_layers], "grid_map_layers": [grid_map_layers], "index_map_layers": [index_map_layers]}

Resources