I'm using FastAPI & SQLModel to insert data into SQLite database (I'm posting a few fields, then SQLModel adds UUID and datetime fields and inserts into db).
While posting data to FastAPI, I occasionally get ValueError:
ValueError: badly formed hexadecimal UUID string
I don't think there's a problem with the data I'm sending as I'm not sending id field, so maybe I misconfigured my SQLModel?
Models:
# -*- coding: utf-8 -*-
import uuid
from typing import Optional
from datetime import datetime, date
from pydantic import UUID4, HttpUrl
from sqlmodel import Field, SQLModel
class EventBase(SQLModel):
title: str
text: str
date: date
URL: HttpUrl
category: Optional[str] = Field(default=None)
class Event(EventBase, table=True):
id: UUID4 = Field(default_factory=uuid.uuid4, primary_key=True)
created_at: datetime = Field(default_factory=datetime.utcnow)
Edit: decided to try this approach replacing UUID4 with uuid.UUID, but it didn't work as well:
Uvicorn debug:
INFO: 127.0.0.1:53312 - "POST /speeches/ HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "c:\github\speeches-api\env\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 373, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "c:\github\speeches-api\env\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__
return await self.app(scope, receive, send)
File "c:\github\speeches-api\env\lib\site-packages\uvicorn\middleware\debug.py", line 96, in __call__
raise exc from None
File "c:\github\speeches-api\env\lib\site-packages\uvicorn\middleware\debug.py", line 93, in __call__
await self.app(scope, receive, inner_send)
File "c:\github\speeches-api\env\lib\site-packages\fastapi\applications.py", line 208, in __call__
await super().__call__(scope, receive, send)
File "c:\github\speeches-api\env\lib\site-packages\starlette\applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "c:\github\speeches-api\env\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
raise exc
File "c:\github\speeches-api\env\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "c:\github\speeches-api\env\lib\site-packages\starlette\exceptions.py", line 82, in __call__
raise exc
File "c:\github\speeches-api\env\lib\site-packages\starlette\exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "c:\github\speeches-api\env\lib\site-packages\starlette\routing.py", line 656, in __call__
await route.handle(scope, receive, send)
File "c:\github\speeches-api\env\lib\site-packages\starlette\routing.py", line 259, in handle
await self.app(scope, receive, send)
File "c:\github\speeches-api\env\lib\site-packages\starlette\routing.py", line 61, in app
response = await func(request)
File "c:\github\speeches-api\env\lib\site-packages\fastapi\routing.py", line 226, in app
raw_response = await run_endpoint_function(
File "c:\github\speeches-api\env\lib\site-packages\fastapi\routing.py", line 161, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "c:\github\speeches-api\env\lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
File "c:\github\speeches-api\env\lib\site-packages\anyio\to_thread.py", line 28, in run_sync
return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
File "c:\github\speeches-api\env\lib\site-packages\anyio\_backends\_asyncio.py", line 818, in run_sync_in_worker_thread
return await future
File "c:\github\speeches-api\env\lib\site-packages\anyio\_backends\_asyncio.py", line 754, in run
result = context.run(func, *args)
File ".\app\main.py", line 29, in create_events
session.refresh(event)
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\session.py", line 2315, in refresh
loading.load_on_ident(
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\loading.py", line 407, in load_on_ident
return load_on_pk_identity(
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\loading.py", line 541, in load_on_pk_identity
return result.one()
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 1407, in one
return self._only_one_row(
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 558, in _only_one_row
row = onerow(hard_close=True)
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 1271, in _fetchone_impl
return self._real_result._fetchone_impl(hard_close=hard_close)
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 1674, in _fetchone_impl
row = next(self.iterator, _NO_ROW)
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\orm\loading.py", line 147, in chunks
fetch = cursor._raw_all_rows()
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 392, in _raw_all_rows
return [make_row(row) for row in rows]
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\engine\result.py", line 392, in <listcomp>
return [make_row(row) for row in rows]
File "c:\github\speeches-api\env\lib\site-packages\sqlalchemy\sql\type_api.py", line 1537, in process
return process_value(value, dialect)
File "c:\github\speeches-api\env\lib\site-packages\sqlmodel\sql\sqltypes.py", line 59, in process_result_value
value = uuid.UUID(value)
File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\uuid.py", line 169, in __init__
raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string
I learnt that the problem was described in this issue: data validation 'works except whenever the uuid.uuid4 default factory creates a UUID with a hexstring that starts with a zero.'
Proposed (temp) solution is posted below in the comments, by chriswhite199:
adjusted for my case:
def new_uuid() -> uuid.UUID:
# Note: Work around UUIDs with leading zeros: https://github.com/tiangolo/sqlmodel/issues/25
# by making sure uuid str does not start with a leading 0
val = uuid.uuid4()
while val.hex[0] == '0':
val = uuid.uuid4()
return val
class Event(EventBase, table=True):
id: UUID4 = Field(default_factory=new_uuid, primary_key=True)
created_at: datetime = Field(default_factory=datetime.utcnow)
I think you need to do a str(uuid.uuid4()) in default factory
Related
I have a table called city in PostgreSQL
CREATE TABLE CITY (
CITYID serial PRIMARY KEY,
CNAME VARCHAR (50) UNIQUE NOT NULL,
STATE VARCHAR (50) NOT NULL
);
When I use simple select with SQLAlchemy it works fine
query = session.query(models.City)
cities = query.all() #works fine
cities = session.execute("SELECT cityid, cname FROM city where cityid = 1").fetchall() # works fine
By doing above, I make sure that my session works fine and connecting the DB properly.
When I try to call a stored procedure in SQLAlchemy it is not working.
Here is my SP (Postgres)
create or replace procedure addcity(
cname varchar(20),
state varchar(20)
)
language plpgsql
as $$
begin
insert into city
(cname, state)
values (cname, state);
commit;
end;$$
Here is my SQLAlchemy code:
session.execute('CALL addcity (?, ?)', [('One', 'Two')])
The above code throws error as below:
Traceback (most recent call last):
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 369, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 59, in __call__
return await self.app(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/fastapi/applications.py", line 208, in __call__
await super().__call__(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/routing.py", line 580, in __call__
await route.handle(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/routing.py", line 241, in handle
await self.app(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/routing.py", line 52, in app
response = await func(request)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/fastapi/routing.py", line 219, in app
raw_response = await run_endpoint_function(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/fastapi/routing.py", line 154, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/concurrency.py", line 40, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "/Users/str-kwml0020/projects/fastapi-crud-sqlite/./main.py", line 128, in add_city
session.execute('CALL addcity (?, ?)', ['One', 'Two'])
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1689, in execute
result = conn._execute_20(statement, params or {}, execution_options)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1575, in _execute_20
args_10style, kwargs_10style = _distill_params_20(parameters)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/util.py", line 142, in _distill_params_20
raise exc.ArgumentError(
sqlalchemy.exc.ArgumentError: List argument must consist only of tuples or dictionaries
I have tried changing the params like below:
session.execute('CALL addcity (?, ?)', [('One', 'Two')])
The above code throws
Traceback (most recent call last):
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 369, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 59, in __call__
return await self.app(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/fastapi/applications.py", line 208, in __call__
await super().__call__(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/routing.py", line 580, in __call__
await route.handle(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/routing.py", line 241, in handle
await self.app(scope, receive, send)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/routing.py", line 52, in app
response = await func(request)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/fastapi/routing.py", line 219, in app
raw_response = await run_endpoint_function(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/fastapi/routing.py", line 154, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/starlette/concurrency.py", line 40, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "/Users/str-kwml0020/projects/fastapi-crud-sqlite/./main.py", line 128, in add_city
session.execute('CALL addcity (?, ?)', [('One', 'Two')])
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 1689, in execute
result = conn._execute_20(statement, params or {}, execution_options)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1583, in _execute_20
return meth(self, args_10style, kwargs_10style, execution_options)
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/sql/elements.py", line 323, in _execute_on_connection
return connection._execute_clauseelement(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1452, in _execute_clauseelement
ret = self._execute_context(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1814, in _execute_context
self._handle_dbapi_exception(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1995, in _handle_dbapi_exception
util.raise_(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/util/compat.py", line 207, in raise_
raise exception
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1771, in _execute_context
self.dialect.do_execute(
File "/opt/anaconda3/envs/fastapi38/lib/python3.8/site-packages/sqlalchemy/engine/default.py", line 717, in do_execute
cursor.execute(statement, parameters)
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.SyntaxError) syntax error at or near ","
LINE 1: CALL addcity (?, ?)
^
[SQL: CALL addcity (?, ?)]
(Background on this error at: https://sqlalche.me/e/14/f405)
When I checked SOF, most of the answers quoting the params as list of string. However, having strings inside the list as param didn't help either.
These are the library versions I have used:
fastapi==0.68.0
uvicorn==0.14.0
sqlalchemy_filters==0.12.0
SQLAlchemy==1.4.21
typing-extensions==3.10.0.0
Werkzeug==2.0.1
FastAPI-SQLAlchemy==0.2.1
Flask==2.0.1
Flask-SQLAlchemy==2.5.1
PyYaml
psycopg2-binary==2.9.1
As noted in a comment to the question, psycopg2 does not like the commit; in the SP. If you remove that then this will work with SQLAlchemy:
import sqlalchemy as sa
from sqlalchemy.orm import Session
engine = sa.create_engine("postgresql://scott:tiger#192.168.0.199/test")
with Session(engine) as session, session.begin():
session.execute(
sa.text("CALL addcity (:param1, :param2)"),
{"param1": "One", "param2": "Two"},
)
# automatically commits when the context manager exits (assuming no errors)
After #GordThompson and #AdrianKlaver asked some questions I have played around a bit and got this code as a solution
SP:
create or replace procedure addcity(
cname varchar(20),
state varchar(20)
)
language plpgsql
as $$
begin
insert into city
(cname, state)
values (cname, state);
# removed commit from the original SP
end;$$
Python code
cname = "Five"
state = "Six"
session.execute('CALL addcity (:cname, :state)', {'cname' : cname, 'state': state})
session.commit()
This works like a charm! Thanks #GordThompson and #AdrianKlaver. You guys saved my time!
I have some legacy code that has a polymorphic base class definition and two sub classes.
The entity definitions are below.
class Field(Base):
__tablename__ = "field"
field_parent = Column(String(64), index=True)
controller_id = Column(ForeignKey('controller.id'))
controller = relationship("Controller")
node_id = Column(ForeignKey("node.id"))
__mapper_args__ = {"polymorphic_on": field_parent}
class ControllerField(Field):
__mapper_args__ = {"polymorphic_identity": "controller"}
class NodeField(Field):
__mapper_args__ = {"polymorphic_identity": "node"}
class Controller:
__tablename__ = "controller"
class Node:
__tablename__ = "node"
Some example data from my local DB.
enter image description here
I'm trying to query the 'controller' table and SQLAlchemy is trying to load all the class's relationships (my assumption). When it gets to the polymorphic subclass the query fails because the 'field' table contains foreign key references to both the 'controller' and 'node' tables and because the latter is supposed to be of type 'node' it still has an FK to the 'controller' table and thus the identity is not the right type. It seems like SQLAlchemy doesn't like this in the current configuration. The stacktrace can be seen below.
File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
await super().__call__(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/usr/local/lib/python3.9/site-packages/starlette/middleware/cors.py", line 78, in __call__
await self.app(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 580, in __call__
await route.handle(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 390, in handle
await self.app(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
await super().__call__(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
await self.middleware_stack(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/usr/local/lib/python3.9/site-packages/starlette/middleware/cors.py", line 78, in __call__
await self.app(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 580, in __call__
await route.handle(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 241, in handle
await self.app(scope, receive, send)
File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 52, in app
response = await func(request)
File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 201, in app
raw_response = await run_endpoint_function(
File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 150, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "/usr/local/lib/python3.9/site-packages/starlette/concurrency.py", line 40, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/usr/src/app/./app/subapp/shim/v2/endpoints/controller.py", line 25, in get
return Wrapper(data=service.get(id=id, controller_category=controllerCategory, condensed=condensed))
File "/usr/src/app/./app/subapp/shim/services/controller_service.py", line 23, in get
"controller": self.__append_nodes(controller.dict(logged_in_user=self.__logged_in_user))
File "/usr/src/app/./app/database/base_new.py", line 28, in dict
rs = getattr(self, relationship.key)
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 481, in __get__
return self.impl.get(state, dict_)
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 926, in get
value = self._fire_loader_callables(state, key, passive)
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 962, in _fire_loader_callables
return self.callable_(state, passive)
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/strategies.py", line 892, in _load_for_state
return self._emit_lazyload(
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/strategies.py", line 1056, in _emit_lazyload
result = result.unique().scalars().all()
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 1319, in all
return self._allrows()
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 400, in _allrows
rows = self._fetchall_impl()
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 1232, in _fetchall_impl
return self._real_result._fetchall_impl()
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/engine/result.py", line 1626, in _fetchall_impl
return list(self.iterator)
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 124, in chunks
rows = [proc(row) for row in fetch]
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 124, in <listcomp>
rows = [proc(row) for row in fetch]
File "/usr/local/lib/python3.9/site-packages/sqlalchemy/orm/loading.py", line 1274, in polymorphic_instance
raise sa_exc.InvalidRequestError(
sqlalchemy.exc.InvalidRequestError: Row with identity key (<class 'app.database.models.field.Field'>, (150,), None) can't be loaded into an object; the polymorphic discriminator column 'field.field_parent' refers to mapped class NodeField->field, which is not a sub-mapper of the requested mapped class ControllerField->field
If I delete the reference to the 'controller' table on the instance with identity 'node' the queries run as expected. As this is legacy code I can't just delete the extra FK references as we don't know what will break if we do this. Is there a way that I can tell SQLAlchemy to load the correct definition without looking at the FK reference or something?
Extra info:
FastAPI Docker Container with a MySQL database
Python 3.8
SQLAlchemy 1.4.21
I've come across the same stuff here, hence I'm going through your post ;) The obvious solution is to select a common ancestor class defining the concrete polymorphic identity via filter or smth.
I just had the same issue as well and found a very simple solution: the base class needs also to have a polymorphic identity (e.g. 'base'). Set this value to be the default value of 'field_parent'.
The new code looks like this:
class Field(Base):
__tablename__ = "field"
field_parent = Column(String(64), index=True, default="base")
__mapper_args__ = {"polymorphic_on": field_parent,
"polymorphic_identity": "base"}
controller_id = Column(ForeignKey("controller.id"))
controller = relationship("Controller")
node_id = Column(ForeignKey("node.id"))
So I have an image which I read the contents off of through .read() which is part of the UploadFile class and I believe it reads in bytes https://fastapi.tiangolo.com/tutorial/request-files/. To convert the bytes objects into bytea data type I used psycopg2.Binary() but I got an error message saying KeyError: '$fu'
This is my code for the portion that's reading off the bytes from the image and trying to store it in Postgresql through the bytea data type
image_array = []
for image in images_upload:
contents = await image.read()
image_array.append(contents)
# add image validation
for (permission, text, characteristics, image) in zip(p, c, t, image_array):
query = ("insert into images(permissions,text,characteristics,date_created,user_id,img)"
f"values('{permission}','{text}',"
f"'{characteristics}'"
f",{func.now()},{result[0]['id']}"
f",'{psycopg2.Binary(image)}')")
await database.execute(query=query)
Here's the trace
INFO: 127.0.0.1:53168 - "POST /add/?api_key=stringstring&texts=string&characteristics=string&permissions=string HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 396, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
await super().__call__(scope, receive, send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/fastapi/routing.py", line 201, in app
raw_response = await run_endpoint_function(
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/fastapi/routing.py", line 148, in run_endpoint_function
return await dependant.call(**values)
File "./app/controllers/add.py", line 49, in add_images
await database.execute(query=query)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/databases/core.py", line 161, in execute
return await connection.execute(query, values)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/databases/core.py", line 263, in execute
return await self._connection.execute(built_query)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/databases/backends/postgres.py", line 188, in execute
query, args, result_columns = self._compile(query)
File "/Users/admin/Library/Caches/pypoetry/virtualenvs/backend-DSfoN2Wn-py3.9/lib/python3.9/site-packages/databases/backends/postgres.py", line 221, in _compile
compiled_query = compiled.string % mapping
KeyError: '$fu'
I have build a classification model using pycaret and already produced the pickle file. Then, I tried to build an API and load the pickle file using fastapi.
import logging
import pickle
from pydantic import BaseModel
import numpy as np
from pycaret.classification import *
import uvicorn
from fastapi import FastAPI
from fastapi import Depends
app = FastAPI()
model = load_model('catboost_cm_creditable')
class Data(BaseModel):
age: float
live_province: str
live_city: str
live_area_big: str
live_area_small: str
sex: float
marital: float
bank: str
salary: float
amount: float
#app.post("/predict")
def predict(data: Data = Depends()):
predictions_df = predict_model(estimator=model, data=data)
predictions = predictions_df["Score"][0]
return predictions
The api run flawlessly, but the things is when I tried to test the api and giving it inputs based on the features it returns:
INFO: 127.0.0.1:60235 - "POST /predict?age=24&live_province=DKI%20Jakarta&live_city=Jakarta%20Selatan&live_area_big=Pesanggrahan&live_area_small=Bintaro&sex=1&marital=2&bank=BCA&salary=7000000&amount=1000000 HTTP/1.1" 500 Internal Server Error
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 390, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/fastapi/applications.py", line 179, in __call__
await super().__call__(scope, receive, send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/fastapi/routing.py", line 183, in app
dependant=dependant, values=values, is_coroutine=is_coroutine
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/fastapi/routing.py", line 135, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/starlette/concurrency.py", line 34, in run_in_threadpool
return await loop.run_in_executor(None, func, *args)
File "/Users/bhaskoromuthohar/anaconda3/lib/python3.7/concurrent/futures/thread.py", line 57, in run
result = self.fn(*self.args, **self.kwargs)
File "./main.py", line 31, in predict
predictions_df = predict_model(estimator=model, data=data)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/pycaret/classification.py", line 10700, in predict_model
Xtest = prep_pipe_transformer.transform(data)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/sklearn/pipeline.py", line 549, in _transform
Xt = transform.transform(Xt)
File "/Users/bhaskoromuthohar/dev/Credit-Scoring-App/.venv/lib/python3.7/site-packages/pycaret/preprocess.py", line 300, in transform
data.columns = [str(i) for i in data.columns]
AttributeError: 'Data' object has no attribute 'columns'
! Output from the fastapi documentations
I think the problem is the way I load the pickle into the API. Can you guys give me advice for correct way to load the pickle?
My guess is that your error is due to this line
def predict(data: Data = Depends()):
In order to declare the pydantic model as mandatory, you don't have to run a Depends(). Just use data: Data.
Depends is only used for dependency injection, but this is not the case.
See the documentation for more information about the request body https://fastapi.tiangolo.com/tutorial/body/
I'm using FastAPI, Pydantic, SQLAlchemy, and Postgres to build a service that receives post requests and stores the data on the database. There is a List in the Pydantic model like the following:
from typing import List
from pydantic import BaseModel, Field
class Note(base model):
id: int
title: str
authors: List[str]
And the table:
notes = Table(
"notes",
metadata,
Column("id", Integer, primary_key=True),
Column("title", String),
Column("authors", ARRAY(String(50), dimensions=3)),
)
Here is the way I do the post request, when there is not a List value:
def post(payload: Note):
query = questions.insert().values(title=payload.title)
return database.execute(query=query)
Post request's body:
{
"title": "some value"
}
And it works fine. But adding the List value breaks it with Pydantic's validation error:
def post(payload: Note):
query = questions.insert().values(title=payload.title, authors=payload.authors)
return database.execute(query=query)
{
"title": "some value",
"authors": ["name1", "name2", "name3"]
}
value is not a valid list
type_error.list
How can I change the post function and request body, to make this work?
Edit: The traceback:
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/usr/lib/python3/dist-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/home/saeed/.local/lib/python3.8/site-packages/fastapi/applications.py", line 149, in __call__
await super().__call__(scope, receive, send)
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/applications.py", line 102, in __call__
await self.middleware_stack(scope, receive, send)
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/routing.py", line 550, in __call__
await route.handle(scope, receive, send)
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/home/saeed/.local/lib/python3.8/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/home/saeed/.local/lib/python3.8/site-packages/fastapi/routing.py", line 204, in app
response_data = await serialize_response(
File "/home/saeed/.local/lib/python3.8/site-packages/fastapi/routing.py", line 126, in serialize_response
raise ValidationError(errors, field.type_)
pydantic.error_wrappers.ValidationError: 1 validation error for Note
response
value is not a valid list (type=type_error.list)
I figured this out. The problem resolved by fixing the respone_model that has an error, and all I was doing for storing the data was correct.
#router.post("/", response_model=Note, status_code=201)
def create_note(payload: Note):
note_id = post(payload)
response_object = {
"id": note_id,
"title": payload.title,
"authors": payload.authors,
}
return response_object
#router.get("/fetchusuarios", response_model=List[providers.schemas.User])
async def fetchall_users(db:Session=Depends(get_db)):
usuarios = db.query(models.usermodel.User).all()
return usuarios
I was using Django-Ninja (based on FastAPI) and had the same error. Originally, my decorator was like this:
#router.get('/apartment-sale-hanoi/', response=List[ApartmentSaleHanoiSchema])
I fixed value is not a valid list (type=type_error.list) just by removing List from the response model as following:
#router.get('/apartment-sale-hanoi/', response=ApartmentSaleHanoiSchema)
my returning response (displayed as JSON) was a list of objects
response = ApartmentSaleHanoi.objects.all()