I'm getting this error when I try to post and create a new registry using fastapi and sqlalchemy:
Class 'endpoints.resultados.ResultadoPruebaSerializer.InsertTResultadoRegla' is not mapped
Here is my code, thanks for your help.
Sqlalchemy Models
#as_declarative()
class Base:
def as_dict(self) -> dict:
return {c.key: getattr(self, c.key) for c in inspect(self).mapper.column_attrs}
class TResultadoRegla(Base):
__tablename__ = 'T_resultadoReglas'
idresultadoReglas = Column(Integer, primary_key=True)
idtareas = Column(ForeignKey('T_tareas.idtareas', ondelete='RESTRICT', onupdate='RESTRICT'), nullable=False, index=True)
idreglas = Column(ForeignKey('M_reglas.idreglas', ondelete='RESTRICT', onupdate='RESTRICT'), index=True)
fecCreacion = Column(Date)
resultado = Column(String(30))
M_regla = relationship('MRegla', backref="resultados")
T_tarea = relationship('TTarea')
Pydantic crud and schemas
class InsertTResultadoRegla(BaseModel):
idtareas: int
idreglas: int
fecCreacion: date
resultado: str
class Config:
orm_mode = True
def get_regla(db: Session, skip: int = 0, limit: int = 100):
return db.query(Regla).offset(skip).limit(limit).all()
def check_resultados(db: Session, user_resultados: Dict):
db_item = None
for resultado in user_resultados:
regla = get_regla_per_id(db, resultado["idreglas"])
regla = regla.as_dict()
if resultado["resultado"] >= int(regla["minimo"]) and resultado["resultado"] <= int(regla["maximo"]):
db_item = InsertTResultadoRegla(idtareas=1, idreglas=regla["idreglas"], fecCreacion=datetime.date.today(), resultado="CUMPLE")
db.add(db_item)
db.commit()
db.refresh(db_item)
else:
db_item = InsertTResultadoRegla(idtareas=1, idreglas=regla["idreglas"], fecCreacion=datetime.date.today(), resultado="NO CUMPLE")
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
The issue is when you are trying to add it to the db, precisely,
db.add(db_item)
You have to add it like shown below:
db_item = InsertTResultadoRegla(idtareas=1, idreglas=regla["idreglas"], fecCreacion=datetime.date.today(), resultado="CUMPLE")
actual_db_item = models.TableName(** db_item.dict())
db.add(actual_db_item)
db.commit()
Before adding the data to the db you have to convert it into a proper "db_item". I hope this solves you issue.
Related
I have an app build on Fastapi and tortoise orm. With the following models:
MODELS :
class Tratamiento(models.Model):
id = fields.UUIDField(pk=True)
nombre = fields.CharField(max_length=100)
descripcion = fields.CharField(max_length=300)
estado = fields.BooleanField()
costo = fields.IntField()
Tortoise.init_models(["db.models"], "models")
tratamiento_in = pydantic_model_creator(Tratamiento, name="TratamientoIn", exclude_readonly=True)
tratamiento_out = pydantic_model_creator(Tratamiento, name="TratamientoOut")
class Presupuesto(models.Model):
id = fields.UUIDField(pk=True)
paciente = fields.ForeignKeyField('models.Paciente', related_name="presupuesto_paciente", on_delete=fields.CASCADE)
tratamiento = fields.ManyToManyField('models.Tratamiento', related_name="presupuesto_tratamiento", on_delete=fields.CASCADE)
descripcion = fields.CharField(max_length=500)
total = fields.IntField()
fecha = fields.DateField()
Tortoise.init_models(["db.models"], "models")
presupuesto_in = pydantic_model_creator(Presupuesto, name="PresupuestoIn", exclude_readonly=True)
presupuesto_out = pydantic_model_creator(Presupuesto, name="PresupuestoOut")
I'm trying to post some values from vuejs, to this route:
ROUTE:
#router.post("/presupuestos/add", response_model=presupuesto_out)
async def post_presupuesto(presupuesto: presupuesto_in, tratamientos: List[TratamientosPydantic]):
for item in tratamientos:
presupuesto.total += item.costo
insert_result = await Presupuesto.create(**presupuesto.dict(exclude_unset=True))
for item in tratamientos:
tratamiento = await tratamiento_out.from_queryset_single(Tratamiento.get(id=item.tratamiento_id))
insert_related = await insert_result.tratamiento.add(*tratamiento)
And I'm getting the next Error on adding values to many to many field (create works fine), don't know how to solve it...Please help!
AttributeError: type object 'tuple' has no attribute '_meta'
ROUTE PARAM SCHEMA:
class TratamientosPydantic(BaseModel):
paciente_id: str
tratamiento_id: str
costo: int
I have a db models like this:
class Payment(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id'))
ticket_status = db.Column(db.Enum(TicketStatus, name='ticket_status', default=TicketStatus.UNUSED))
departure_time = db.Column(db.Date)
I want to change the value from all ticket_status after datetime.utcnow() passed the date value from departure_time.
I tried to code like this:
class TicketStatus(enum.Enum):
UNUSED = 'UNUSED'
USED = 'USED'
EXPIRED = 'EXPIRED'
def __repr__(self):
return str(self.value)
class Payment(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id'))
ticket_status = db.Column(db.Enum(TicketStatus, name='ticket_status', default=TicketStatus.UNUSED))
departure_time = db.Column(db.Date)
# TODO | set ticket expirations time
def __init__(self):
if datetime.utcnow() > self.departure_time:
self.ticket_status = TicketStatus.EXPIRED.value
try:
db.session.add(self)
db.session.commit()
except Exception as e:
db.session.rollback()
I also tried like this:
def ticket_expiration(self, payment_id):
now = datetime.utcnow().strftime('%Y-%m-%d')
payment = Payment.query.filter_by(id=payment_id).first()
if payment.ticket_status.value == TicketStatus.USED.value:
pass
elif payment and str(payment.departure_time) < now:
payment.ticket_status = TicketStatus.EXPIRED.value
elif payment and str(payment.departure_time) >= now:
payment.ticket_status = TicketStatus.UNUSED.value
try:
db.session.commit()
except Exception as e:
db.session.rollback()
return str('ok')
But it seems no effect when the datetime.utcnow() passed the date value from departure_time.
So the point of my questions is, how to change the value from a row automatically after a set of times..?
Finally I figure out this by using flask_apscheduler, and here is the snippet of my code that solved this questions:
Install flask_apscheduler:
pip3 install flask_apscheduler
create new module tasks.py
from datetime import datetime
from flask_apscheduler import APScheduler
from app import db
from app.models import Payment, TicketStatus
scheduler = APScheduler()
def ticket_expiration():
utc_now = datetime.utcnow().strftime('%Y-%m-%d')
app = scheduler.app
with app.app_context():
payment = Payment.query.all()
for data in payment:
try:
if data.ticket_status.value == TicketStatus.USED.value:
pass
elif str(data.departure_time) < utc_now:
data.ticket_status = TicketStatus.EXPIRED.value
elif str(data.departure_time) >= utc_now:
data.ticket_status = TicketStatus.UNUSED.value
except Exception as e:
print(str(e))
try:
db.session.commit()
except Exception as e:
db.session.rollback()
return str('ok')
and then register the package with the flask app in the __init__.py
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
# The other packages...
# The other packages...
scheduler.init_app(app)
scheduler.start()
return app
# import from other_module...
# To avoid SQLAlchemy circular import, do the import at the bottom.
from app.tasks import scheduler
And here is for the config.py:
class Config(object):
# The others config...
# The others config...
# Flask-apscheduler
JOBS = [
{
'id': 'ticket_expiration',
'func': 'app.tasks:ticket_expiration',
'trigger': 'interval',
'hours': 1, # call the task function every 1 hours
'replace_existing': True
}
]
SCHEDULER_JOBSTORES = {
'default': SQLAlchemyJobStore(url='sqlite:///flask_context.db')
}
SCHEDULER_API_ENABLED = True
In the config above, we can call the function to update db every 1 hours, seconds or others time according to our case, for more informations to set the interval time we can see it here.
I hope this answer helps someone who facing this in the future.
You may replace your status column with just "used" column which will contain Boolean value and make a hybrid attribute for state. https://docs.sqlalchemy.org/en/13/orm/extensions/hybrid.html
class Payment(db.Model):
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id'))
used = db.Column(db.Boolean(), default=False)
departure_time = db.Column(db.Date)
#hybrid_property
def status(self):
if datetime.utcnow() > self.departure_time:
return "EXPIRED"
elif self.used:
return "USED"
return "UNUSED"
I try to save some columns (eg: tags, models) with JSON encoded string.
And I hope to always keep then decoded in use.
I have read some refers to add configs to disable autocommit and autoflush , but it doesn't work.
While the instance was added into db.session and then changed value , orm still try to commit an UPDATE OPERATION and then raise TypeError.
Here is my code.
```python
import json
from sqlalchemy import orm
from flask_sqlalchemy import SQLAlchemy
session_options = dict(
bind=None,
autoflush=False,
autocommit=False,
expire_on_commit=False,
)
db = SQLAlchemy(session_options=session_options)
class Sample(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
# tags, models : string of json.dumps(array)
tags = db.Column(db.String(128), default='')
models = db.Column(db.String(128), default='')
def __init__(self, **kwargs):
cls = self.__class__
super(cls, self).__init__(**kwargs)
self.formatting()
#orm.reconstructor
def init_on_load(self):
self.formatting()
def formatting(self):
self.tags = json.loads(self.tags)
self.models = json.loads(self.models)
def save(self):
self.tags = json.dumps(self.tags)
self.models = json.dumps(self.models)
db.session.add(self)
db.session.commit()
self.formatting()
## fixme !!!
## formatting after saved will cause auto-commit and raise TypeError
```
Thank you :)
ps: Flask-SQLAlchemy==2.3.2
This error was raised by lacking called db.session.close() after db.session.commit()
I was told that db.session.close() is automatically called in db.session.commit(). And the real has denied my cognition.
And I try to refer the source code of sqlalchmey, and then I find the db.session is an instance of sqlalchemy.orm.scoping.scoped_session, NOT sqlalchemy.orm.SessionTransaction.
Here is the source code in sqlalchemy.orm.SessionTransaction
def commit(self):
self._assert_active(prepared_ok=True)
if self._state is not PREPARED:
self._prepare_impl()
if self._parent is None or self.nested:
for t in set(self._connections.values()):
t[1].commit()
self._state = COMMITTED
self.session.dispatch.after_commit(self.session)
if self.session._enable_transaction_accounting:
self._remove_snapshot()
self.close()
return self._parent
It’s really confusing.
If you want to repeat this Error, here is Test code:
"""
# snippet for testing <class:Sample>
"""
from flask import Flask
app = Flask(__name__)
app.config.from_mapping(
SQLALCHEMY_ECHO=True,
SQLALCHEMY_TRACK_MODIFICATIONS=False,
SQLALCHEMY_DATABASE_URI='sqlite:///test_orm.sqlite.db',
)
db.init_app(app=app)
db.app = app
db.create_all()
d1 = dict(
tags='["python2","flask"]',
models='["m1"]'
)
m1 = Sample(**d1)
print(1111, type(m1.tags), m1.tags)
m1.save()
print(1112, type(m1.tags), m1.tags)
dm1 = Sample.query.filter(Sample.id == m1.id).all()[0]
print(1113, dm1, type(dm1.tags), dm1.tags)
## fixme[Q1] !!!
## if not continue with $d2, it won't raise error of UPDATE $d1
d2 = dict(
tags='["python3","flask"]',
models='["m2", "m3"]'
)
m2 = Sample(**d2)
print(2221, type(m2.tags), m2.tags)
## fixme[Q1] !!!
# db.session.close()
## If session was not closed, error raise here.
m2.save()
print(2222, type(m2.tags), m2.tags)
dm2 = Sample.query.filter(Sample.id == m2.id).all()[0]
print(2223, dm2, type(dm2.tags), dm2.tags)
Thank you for your read ,wish to solve your same confusion.
I use sqlalchemy to perform some queries and updates, but now I have trouble with UPDATE operation , when I change the model attribute pet_time(ins.role_time = plus_days(ins.role_time,30)) and use session to commit , sqlalchemy didn't perform the update operation.I don't know what's going on ,can anybody help?
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer,primary_key=True)
nick_name = Column(String)
create_time = Column(DateTime, default=datetime.datetime.now)
role_time = Column(DateTime)
# the db connection info is correct
def getDbSession():
return Session()
def queryUserById(id):
sess = getDbSession()
instance = sess.query(User)\
.filter(User.id == id)\
.limit(1).first()
return instance
def increaseRoleTime(id,inc=1):
ins = queryUserById(id)
sess = getDbSession()
if(ins.role_time is None):
ins.role_time = datetime.datetime.now()
inc = inc * 30
# ins.role_time = func.ADDDATE(ins.role_time,inc)
ins.role_time = plus_days(ins.role_time,inc)
sess.commit()
return 1
# import timedelta from datetime to plus days
def plus_days(mydatetime,days):
return mydatetime + timedelta(days=days)
in the function increaseRoleTime,I change attribute role_time and commit,but still not working.
I get a errormessage on my init function and i can't find the problem
class Character(Base):
__tablename__ = 'character'
id = Column(Integer, primary_key=True)
_info_id = Column(Integer, ForeignKey('character_info.id'))
_stats_id = Column(Integer, ForeignKey('character_stats.id'))
_defence_id = Column(Integer, ForeignKey('character_defences.id'))
_misc_id = Column(Integer)
_skills_id = Column(Integer)
_spells_id = Column(Integer)
_inventory_id = Column(Integer)
_info = relationship("Character_Info", backref=backref('character'))
_stats = relationship("Character_Stats", backref=backref('character'))
_defences = relationship("Character_Defences", backref=backref('character')
def __init__(self, _info, _stats, _defences, _misc, _skills, _spells, _inventory):
self._info = _info
self._stats = _stats
self._defences = _defences
self._misc = _misc
self._skills = _skills
self._spells = _spells
self._inventory = _inventory
I am new to sqlalchemy so i'm not really sure if i am doing the relationships right as well.
But problem seems to be in the def init line (my cmd tells me)
Please help out
You're missing a ) at the end of this line.
_defences = relationship("Character_Defences", backref=backref('character')
Plus the def __init__ block is not indented properly.