Get string (not dict) for SQLAlchemy JSON column - json

Given e.g.:
class Alarm(Base):
__tablename__ = 'alarm'
cfg = Column(JSON, nullable=False)
alarm = Alarm.query.one()
SQLAlchemy returns alarm.cfg as a dict, which is useful most of the time,
but how do I get the JSON as a str, unconverted?

Related

Interaction between pydantic models/schemas in fastAPI Tutorial

I follow the FastAPI Tutorial and am not quite sure what the exact relationship between the proposed data objects is.
We have the models.py file:
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship
from .database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
items = relationship("Item", back_populates="owner")
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
description = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))
owner = relationship("User", back_populates="items")
and the schemas.py file:
from typing import List, Union
from pydantic import BaseModel
class ItemBase(BaseModel):
title: str
description: Union[str, None] = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
owner_id: int
class Config:
orm_mode = True
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
is_active: bool
items: List[Item] = []
class Config:
orm_mode = True
Those classes are then used to define db queries like in the crud.py file:
from sqlalchemy.orm import Session
from . import models, schemas
def get_user(db: Session, user_id: int):
return db.query(models.User).filter(models.User.id == user_id).first()
def get_user_by_email(db: Session, email: str):
return db.query(models.User).filter(models.User.email == email).first()
def get_users(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.User).offset(skip).limit(limit).all()
def create_user(db: Session, user: schemas.UserCreate):
fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def get_items(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Item).offset(skip).limit(limit).all()
def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
db_item = models.Item(**item.dict(), owner_id=user_id)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
and in the FastAPI code main.py:
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session
from . import crud, models, schemas
from .database import SessionLocal, engine
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
#app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)
#app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
#app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
#app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)
#app.get("/items/", response_model=List[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items
Now from what I understand:
the models data classes define the sql tables
the schemas data classes define the api that FastAPI uses to interact with the database
they must be convertible into each other so that the set-up works
What I don't understand:
in crud.create_user_item I expected the return type to be schemas.Item, since that return type is used by FastAPI again
according to my understanding the response model of #app.post("/users/{user_id}/items/", response_model=schemas.Item) in the main.py is wrong, or how can I understand the return type inconsistency?
however inferring from the code, the actual return type must be models.Item, how is that handled by FastAPI?
what would be the return type of crud.get_user?
I'll go through your bullet points one by one.
The models data classes define the sql tables
Yes. More precisely, the ORM classes that map to actual database tables are defined in the models module.
the schemas data classes define the api that FastAPI uses to interact with the database
Yes and no. The Pydantic models in the schemas module define the data schemas relevant to the API, yes. But that has nothing to do with the database yet. Some of these schemas define what data is expected to be received by certain API endpoints for the request to be considered valid. Others define what the data returned by certain endpoints will look like.
they must be convertible into each other so that the set-up works
While the database table schemas and the API data schemas are usually very similar, that is not necessarily the case. In the tutorial however, they correspond quite neatly, which allows succinct CRUD code, like this:
db_item = models.Item(**item.dict(), owner_id=user_id)
Here item is a Pydantic model instance, i.e. one of your API data schemas schemas.ItemCreate containing data you decided is necessary for creating a new item. Since its fields (their names and types) correspond to those of the database model models.Item, the latter can be instantiated from the dictionary representation of the former (with the addition of the owner_id).
in crud.create_user_item I expected the return type to be schemas.Item, since that return type is used by FastAPI again
No, this is exactly the magic of FastAPI. The function create_user_item return an instance of models.Item, i.e. the ORM object as constructed from the database (after calling session.refresh on it). And the API route handler function create_item_for_user actually does return that same object (of class models.Item).
However, the #app.post decorator takes that object and uses it to construct an instance of the response_model you defined for that route, which is schemas.Item in this case. This is why you set this in your schemas.Item model:
class Config:
orm_mode = True
This allows an instance of that class to be created via the .from_orm method. That all happens behind the scenes and again depends on the SQLAlchemy model corresponding to the Pydantic model with regards to field names and types. Otherwise validation fails.
according to my understanding the response model [...] is wrong
No, see above. The decorated route function actually deals with the schemas.Item model.
however inferring from the code, the actual return type must be models.Item
Yes, see above. The return type of the undecorated route handler function create_item_for_user is in fact models.Item. But its return type is not the response model.
I assume that to reduce confusion the documentation example does not annotate the return type of those route functions. If it did, it would look like this:
#app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
) -> models.Item:
return crud.create_user_item(db=db, item=item, user_id=user_id)
It may help to remember that a decorator is just syntactic sugar for a function that takes a callable (/function) as argument and returns a function. Typically the returned function actually internally calls the function passed to it as argument and does additional things before and/or after that call. I could rewrite the route above like this and it would be exactly the same:
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
) -> models.Item:
return crud.create_user_item(db=db, item=item, user_id=user_id)
create_item_for_user = app.post(
"/users/{user_id}/items/", response_model=schemas.Item
)(create_item_for_user)
what would be the return type of crud.get_user?
That would be models.User because that is the database model and is what the first method of that query returns.
db.query(models.User).filter(models.User.id == user_id).first()
This is then again returned by the read_user API route function in the same fashion as I explained above for models.Item.
Hope this helps.

serialize Map with key and value as case class using circe

Below is the simplified code that has a Map with key and value as case class.
Key and value object are successfully serialized with circe.
But facing challenge to serialize Map[CaseClassKey, CaseClassValue] with circe.
//////case classes and common vals starts here//////
case class Person(personId : Int, phoneNumber : Int)
case class Item(name : String)
case class Basket(items : List[Item], bills : Map[Int, Int])
def createBasketInstance() : Basket = {
val milk = Item("milk")
val coffee = Item("coffee")
val bills = Map(1 -> 20, 2 -> 75)
Basket( List(milk, coffee), bills )
}
val basket = createBasketInstance()
val person = Person(1, 987654)
//////case classes and common vals ends here//////
import io.circe._
import io.circe.generic.semiauto._
import io.circe.syntax._
import io.circe.parser._
//Serializing Person instance that is used as Key in Map
{
implicit val personCodec :Codec[Person] = deriveCodec[Person]
val jsonString = person.asJson.spaces2
println(jsonString)
}
println("-" * 50)
//Serializing Basket instance that is used as Value in Map
{
implicit val itemCodec :Codec[Item] = deriveCodec[Item]
implicit val basketCodec :Codec[Basket] = deriveCodec[Basket]
val jsonString = basket.asJson.spaces2
println(jsonString)
}
println("-" * 50)
//Serializing Map[Person, Basket]
//TODO : not able to make it work
{
implicit val itemCodec :Codec[Item] = deriveCodec[Item]
implicit val basketCodec :Codec[Basket] = deriveCodec[Basket]
val map = Map(person -> basket)
//TODO : How to make below lines work
//val jsonString = map.asJson.spaces2
//println(jsonString)
}
scalafiddle link : https://scalafiddle.io/sf/SkZNa1L/2
Note : Looking to just serialize and deserialize data (Map[Person, Basket]) correctly. How json looks is not really important in this particular case.
Strictly speaking, you are trying to create an invalid json.
Json map structure isn't an arbitrary map, it's an object structure where keys are property names.
https://www.json.org/json-en.html
See also Can we make object as key in map when using JSON?
Update:
I suggest a slight change to your model to get the job done.
Instead of Map use array of objects, each having two properties: key and value
Something like this:
case class Entry(key: Person, value: Basket)
So you can replace Map[Person, Basket] with Seq[Entry], and convert it back to Map if needed.

Object of type 'Mycart' is not JSON serializable in Django

I want to add dictionary(having models) datatype object to request.session
but getting "Object of type 'Mycart' is not JSON serializable in Django"
product_details = {}
for product in products_in_cart:
product_details.update({product.id:
(product,request.POST['quantity'+str(product.product.id)])})
request.session['product_details'] = product_details
I expect the dictionary updated in session but the actual output is "Object of type 'Mycart' is not JSON serializable in Django"
The problem is with the product which is the first parameter of your tuple inside of your dictionary. you need to serialize it before you can use it in your tuple like this:
from django.core import serializers
product_details = {}
for product in products_in_cart:
s_product = serializers.serialize("json", [product])
product_details.update({product.id:
(s_product,request.POST['quantity'+str(product.id)])})
request.session['product_details'] = product_details

serialize data to ArrayField(models.IntegerField(),default=list) field

I have the following field in postgres database :ArrayField(models.IntegerField(),default=list).
When I send data from post request I get the following error:
invalid literal for int() with base 10: '[176,68]'
while the data sent is price:[176,68]
Here is my serializer for price :
class StringArrayField(ListField):
"""
String representation of an array field.
"""
def to_representation(self, obj):
obj = super().to_representation( obj)
# convert list to string
return ",".join([(element) for element in obj])
def to_internal_value(self, data):
return super().to_internal_value( data)
class MySerializer(serializers.ModelSerializer):
price = StringArrayField()
class Meta:
model = myModel
fields =('price')
You must pass a child serializer to the listfield.
price = serializers.ListField(
child=serializers.IntegerField(min_value=0, max_value=100)
)

scala spray json optional id parameter in entity class

I'm using Salat library for serializing case classes as mongoDb objects. My Item.scala file looks like this:
case class Item(_id: String = (new ObjectId).toString, itemId: Int, var name: String, var active: Boolean) extends WithId {
override def id: ObjectId = new ObjectId(_id)
}
object Item extends MongoDb[Item] with MongoDao[Item] {
override def collectionName: String = "items"
}
object ItemJsonProtocol extends DefaultJsonProtocol {
implicit val itemFormat = jsonFormat4(Item.apply)
}
Now, I'm using it to post the Item entities as Json via Spray HTTP. I'd want to invoke it as follows:
curl.exe -H "Content-Type: application/json" -X PUT -d "{\"itemId\":
1, \"active\":true, \"name\" : \"test\"}" http://localhost:8080/items/
hoping it would provide generated id if I don't provide one.
However, after invoking curl command I'm getting an error:
The request content was malformed:
Object is missing required member '_id'
Is there any way to mark the _id field as optional without making the Option out of it (this field will always be set) and defining custom JsonFormat thus (de)serializing the object by myself?
I've read this post: https://stackoverflow.com/a/10820293/1042869, but I was wondering if there's any other way to do that as I got many cases of the _id fields. There was a comment too saying "you but you can give that field a default value in the case class definition, so if the field is not in the json, it will assign the default value to it.", but as you can see here it doesn't seem to work.
Best,
Marcin
So I solved the problem by writing a custom RootJsonFormat::
implicit object ItemJsonFormat extends RootJsonFormat[Item] {
override def read(json: JsValue): Item = json.asJsObject.getFields("_id", "itemId", "name", "active") match {
case Seq(JsString(_id), JsNumber(itemId), JsString(name), JsBoolean(active)) => Item(_id = _id, itemId = itemId.toInt, name = name, active = active)
case Seq(JsNumber(itemId), JsString(name), JsBoolean(active)) => Item(itemId = itemId.toInt, name = name, active = active)
case _ => throw new DeserializationException("Item expected")
}
override def write(obj: Item): JsValue = JsObject(
"_id" -> JsString(obj._id),
"itemId" -> JsNumber(obj.itemId),
"name" -> JsString(obj.name),
"active" -> JsBoolean(obj.active)
)
}
Basically what it does is it checks if we received the _id in json, if we did then we're using it to construct the object, and in other case keep the auto-generated id field.
One other thing which might cause some trouble but in my opinion deserves mentioning somewhere - if anyone has a problem with nested objects ("non-primitive" types) - I advise using .toJson in write def (like obj.time.toJson, where obj.time is jodatime's DateTime) and JsValue's .convertTo[T] def in read, like time = JsString(time).convertTo[DateTime]. In order for this to work there have to be defined implicit json formats for those "non-primitive" objects.
Best,
Marcin
I would use this solution:
case class Item(_id: Option[String], itemId: Int, var name: String, var active: Boolean)
implicit object ItemJsonFormat extends RootJsonFormat[Item] {
override def read(value: JsValue) = {
val _id = fromField[Option[String]](value, "_id")
val itemId = fromField[Int](value, "itemId")
val expires = fromField[Long](value, "expires")
val name = fromField[String](value, "name")
val active = fromField[Boolean](value, "active")
Item(_id, itemId, name, active)
}
override def write(obj: Item): JsValue = JsObject(
"_id" -> JsString(obj._id),
"itemId" -> JsNumber(obj.itemId),
"name" -> JsString(obj.name),
"active" -> JsBoolean(obj.active)
)
}
The advantage over the json.asJsObject.getFields solution is that you have better control over what gets accepted on the case of an undefined id. The example where that would fail is the following:
itemId is a string, same as id
id is defined but itemId is not
In this case the match case would interpret the specified id as a itemId and not catch the error.