I've created the schema and 2 tables models with the relation between them.
class SubscriberBase(BaseModel):
subscriber_no: int
is_active: bool = False
class SubscriberCreate(BaseModel):
pass
class Subscriber(SubscriberBase):
owner: int
class Config:
orm_mode = True
class CustomerCreate(BaseModel):
customer_no: int
subscriber: Optional[List[SubscriberBase]] = None
class Customer(CustomerCreate):
id: int
class Config:
orm_mode = True
Models:
class CustomerModel(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True, index=True)
customer_no= Column(Integer,index=True)
subscriber= relationship("SubscriberModel", back_populates="owner")
class SubscriberModel(Base):
__tablename__ = 'subscriber'
id = Column(Integer, ForeignKey("customer.id"))
subscriber_no= Column(Integer, primary_key=True, index=True)
owner = relationship("CustomerModel", back_populates="subscriber")
for the following dictionary input:
test = {'customer_no': 2, 'subscriber': [{'subscriber_no': 2, 'is_active': False}, {'subscriber_no': 1, 'is_active': False}]}
I expect , it will insert one row to customer table ,
and 2 rows in subscriber table.
tried:
db_customer = models.CustomerModel(**test)
db.add(db_customer)
db.commit()
db.refresh(db_customer)
Getting error :
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py", line 1675, in emit_backref_from_collection_append_event
child_state, child_dict = instance_state(child), instance_dict(child)
AttributeError: 'dict' object has no attribute '_sa_instance_state'
How does sqlalchemy orm insert to multiple tables?
Is it possible to insert both customer and subscriber tables in one insert ?
#van
Updating the post with more Info:
Im Working with fastapi where the "customer" (the "test" dict i mention above)
is is actually the pydantic , and i did tried the following:
#customer_router.post("/customer/")
def overloaded_create_customer(customer: CustomerCreate, db: Session = Depends(get_db)):
db_customer = CustomerModel(**dict(customer))
db.add(db_customer)
db.commit()
db.refresh(db_customer)
It return similar error:
File "/usr/local/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py", line 1675, in emit_backref_from_collection_append_event
child_state, child_dict = instance_state(child), instance_dict(child)
AttributeError: 'SubscriberBase' object has no attribute '_sa_instance_state'
Creating a ORM model instance (like you try with the CustomerModel) is not the way to do it. Instead, with the assumption that those are pydantic schemas, you should try something along these lines:
# create pydantic model from the dictionary
cust_schema = schemas.Customer.parse_obj(test)
# convert to ORM model
db_customer = models.CustomerModel(**dict(cust_schema))
# ... (rest of your code)
Related
I have these models in my FastApi Python project with SqlAlchemy:
class Status(Enum):
SUCCESS = "SUCCESS"
FAILURE = "FAILURE"
class BarModel(Base):
__tablename__ = "Bar"
id = Column(Integer, ForeignKey("foo.id"), primary_key=True)
name = Column(String, nullable=True)
class FooModel(Base):
__tablename__ = "foo"
id = Column(Integer, autoincrement=True, primary_key=True)
flag_one = Column(Boolean, nullable=True, default=None)
bar = relationship("BarModel", lazy="joined", uselist=False)
#hybrid_property
def hybrid_enum(self) -> Status:
if (
self.flag_one
and self.bar is not None
and self.bar.name is not None
):
return Status.SUCCESS
else:
return Status.FAILURE
My hybrid_enum property works like a charm when reading data from the database.
However, when i want to do a filter query based on the hybrid property:
db.query(FooModel)
.filter(FooModel.hybrid_enum == Status.SUCCESS)
.all(),
I receive the following error:
AttributeError: Neither 'InstrumentedAttribute' object nor
'Comparator' object associated with FooModel.bar has an
attribute 'name'
I also tried to join the BarModel, but it results in the same error. What am i missing here?
I try to write tests for my FastAPI application but I get some import errors.
I'm trying to do very simple testing for my models, e.g.:
models/example.py:
class ExampleDbModel(ExampleBase, table=True):
__tablename__ = "example"
id: str
name: str
relation_id: str = Field(foreign_key="another_example.id")
...
relation: AnotherExampleDbModel = Relationship()
class AnotherExampleDbModel(AnotherExampleBase, table=True):
__tablename__ = "another_example"
id: str
some_field: str
relation_id: str = Field(foreign_key="third_example.id")
...
relation: ThirdExampleDbModel = Relationship()
tests/test_example.py:
def test_example():
example = ExampleDbModel(name="test")
fields = [
"id",
"name",
...
]
class_fields = example.dict().keys()
diff = set(fields) ^ set(list(class_fields))
assert not diff
This gives me an error: sqlalchemy.exc.InvalidRequestError: Table 'third_example' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.. Am I right when assuming it's because the model AnotherExampleDbModel has its own fk relation to another table? How could I test a model that has relations to another table (which has relations to another table)?
I have a following models with many-to-many relations:
dashboard_customer_association = Table(
"entry_customer",
Base.metadata,
Column("entry_id", ForeignKey("entry.id"), primary_key=True),
Column("customer_id", ForeignKey("customer.id"), primary_key=True),
)
class Customer(Base):
__tablename__ = "customer"
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
name = Column(String(64), unique=True, index=True)
class Entry(Base):
__tablename__ = "entry"
id = Column(String(16), primary_key=True, index=True)
customer = relationship("Customer", secondary=dashboard_customer_association)
Here's my pydantic schema.
class Entry(BaseModel):
id: str
customer: List[str] = []
class Config:
orm_mode = True
I've managed to insert the data and create the customers alongside,
but the problem is when I'm trying to retrieve data:
pydantic.error_wrappers.ValidationError: 2 validation errors for Entry
response -> customer -> 0
str type expected (type=type_error.str)
response -> customer -> 1
str type expected (type=type_error.str)
I understand that the Customer object is not a string, so customer
field cannot be directly serialized as List[str], but I fail to see
how am I supposed to do the conversion.
I return the data with the following function:
def get_data(item_id):
instance = db.query(models.Entry).filter(models.Entry.id == item_id).first()
return instance
I was trying to set instance.customer = [customer.name for customer in instance.customer],
but SQLalchemy prevents that. What is the right way to do that?
The best way would be to simply match the schema to the returned data and have a Customer object as well.
However, if that is not an option, you can use a validator to change the content when it's being populated - i.e. just return a single value from your Customer object.
#validator('customer')
def customer_as_string(cls, v):
return v.name
I want to create a temp table from a result of CTE using SQLAlchemy.
Tables definition:
class Data(Base):
__tablename__ = 'data'
c_id = Column(Integer, primary_key=True)
# ...
# temp table
class CIdTmp(Base):
__tablename__ = '#c_id_tmp'
c_id = Column(Integer, primary_key=True)
This is my CTE:
c_id_cte = (session.query(Data.c_id)).cte('c_id_cte')
I tried combining insert() with from_select() like this:
session.execute(CIdTmp.insert().from_select(['c_id'], c_id_cte))
But it produces this error:
AttributeError: type object 'CIdTmp' has no attribute 'insert'
Ok, I found out that I have to get a Table object to execute an insert expression. It can be done by using ___table__ on my model.
session.execute(CIdTmp.__table__.insert().from_select(['c_id'], c_id_cte))
I have a class with a one-many relationship. I would like to return all the parent’s children in the relationship ; specifically I’d like to return all the JSONB objects in my children tables.
These are my class:
class Parent(db.Model):
__tablename__ = ‘parent220416'
id = db.Column(db.Integer, primary_key=True)
children = db.relationship(‘Child’, backref=‘Parent’, lazy='dynamic')
class Child(db.Model):
__tablename__ = ‘child220416'
id = db.Column(db.Integer, primary_key=True)
parentid = db.Column(db.Integer, db.ForeignKey('words220416.id'))
data = db.Column(JSONB)
Then with Flask Restful, I’m trying to select all the child like this:
class ParentChild(Resource):
def get(self, id):
result = db.session.query(Parent).get(id)
result_child = result.children
return {'child': result_child}
There is an error:
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x106178da0> is not JSON serializable
If you want to get all of the data objects for each Child of the Parent. you could do the following query:
result_child = db.session.query(Child.data).filter(Child.parentid == id).all()
If you want to use the children relationship, it is possible to iterate over it:
result_child = [child.data for child in result.children]