According to the documentation, there should be a delete() method (https://docs.sqlalchemy.org/en/13/core/tutorial.html#deletes), but my model does not seem to have a delete() method, what am I doing wrong?
Models.py
from sqlalchemy import BigInteger, Column, DateTime, ForeignKey, Integer, Numeric, String, Text, text
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class Category(Base):
__tablename__ = 'category'
id = Column('id', Integer, primary_key=True)
name = Column('name', String(200))
parentid = Column('parentid', Integer)
Main.py
def crud_delete_single_cat(db: Session, cat_id: int) -> int:
query = models.Category.delete().where(models.Category.id == cat_id)
print(query)
results = db.query()
return results.rowcount
The error message when i try to run the method:
AttributeError: type object 'Category' has no attribute 'delete'
Thanks for your answers, i found the error and fix it, i change the way how i create the Model. I used the "Classical Mappings" from the Documentation and now Deleting works :)
Models.py
from sqlalchemy import BigInteger, Column, DateTime, ForeignKey, Integer, Numeric, String, Text, text, Table
from sqlalchemy.orm import relationship, mapper
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
category = Table('category', metadata,
Column('id', Integer, primary_key=True),
Column('name', String(200)),
Column('parentid', Integer),
)
class Category(object):
def __init__(self, cat_id, name, parentid):
self.id = cat_id
self.name = name
self.parentid = parentid
mapper(Category, category)
main.py
def crud_delete_single_cat(db: Session, cat_id: int) -> bool:
# records = db.query(models.Category).all()
stm = models.category.delete().where(models.category.c.id == cat_id)
print(stm)
results = db.execute(stm)
db.commit()
# result_set = db.execute("SELECT id, name, parentid FROM public.category;")
# rint(type(result_set))
# for r in result_set:
# print(r)
# return [{column: value for column, value in rowproxy.items()} for rowproxy in result_set]
# return await databasehelper.database.fetch_all(query)
return True
#router.delete("/category/{cat_id}", tags=["category"])
def read_item(cat_id: int, db: Session = Depends(get_db)):
deleted = crud_delete_single_cat(db, cat_id)
return {"cat_deleted": deleted}
Related
Here's my code / what I've tried. How can I query a json key containing a list?
import sqlalchemy
from sqlalchemy import Column, Integer, JSON
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Session = sessionmaker()
Base = declarative_base()
class Track(Base): # noqa: WPS230
__tablename__ = "track"
id = Column(Integer, primary_key=True)
fields = Column(JSON(none_as_null=True), default="{}")
def __init__(self, id):
self.id = id
self.fields = {}
engine = sqlalchemy.create_engine("sqlite:///:memory:")
Session.configure(bind=engine)
Base.metadata.create_all(engine) # creates tables
session = Session()
track1 = Track(id=1)
track2 = Track(id=2)
track1.fields["list"] = ["wow"]
track2.fields["list"] = ["wow", "more", "items"]
session.add(track1)
session.commit()
session.query(Track).filter(Track.fields["list"].as_string() == "wow").one()
session.query(Track).filter(Track.fields["list"].as_string() == "[wow]").one()
session.query(Track).filter(
Track.fields["list"].as_json() == ["wow", "more", "items"]
).one()
I've also tried contains() instead of ==, but that seems to match substrings of elements as well, which I don't want..
I managed to get the behavior I was after by utilizing json_each. To filter against the entire list, I just need to create a new json_each function for each element I want to test against.
#!/usr/bin/env python3
import sqlalchemy
from sqlalchemy import func
from sqlalchemy import Column, Integer, JSON
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Session = sessionmaker()
Base = declarative_base()
class Track(Base):
__tablename__ = "track"
id = Column(Integer, primary_key=True)
fields = Column(JSON, default="{}")
def __init__(self, id):
self.id = id
self.fields = {}
engine = sqlalchemy.create_engine("sqlite:///:memory:")
Session.configure(bind=engine)
Base.metadata.create_all(engine)
session = Session()
track = Track(id=1)
track.fields["list"] = ["a", "list"]
session.add(track)
session.commit()
fields_func1 = func.json_each(Track.fields, "$.list").table_valued(
"value", joins_implicitly=True
)
fields_func2 = func.json_each(Track.fields, "$.list").table_valued(
"value", joins_implicitly=True
)
session.query(Track).filter(fields_func1.c.value == "list").one()
session.query(Track).filter(fields_func1.c.value == "a").one()
session.query(Track).filter(
fields_func1.c.value == "a", fields_func2.c.value == "list"
).one()
I have a question for sqlalchemy;
Why the sqlalchemy orm Can't obj.select?
code on there.
this orm:
from sqlalchemy import BigInteger, Boolean, Column, Date, Float, Index, Integer, JSON, MetaData, String, Table, Text, text
from sqlalchemy.dialects.postgresql import JSONB
metadata = MetaData()
t_clinicaltrials = Table(
'clinicaltrials', metadata,
Column('id', BigInteger, primary_key=True, server_default=text("nextval('shuimubio_auto_id.clinicaltrials_auto_id'::regclass)"), comment='数据库自增主键'),
Column('nct_id', String, comment='Clinicaltrial的唯一ID'),
Column('study_title', String, comment='标题'),
Column('study_type', String, server_default=text("0"), comment="研究类型(现有四种'Expanded Access', 'Interventional', None, 'Observational')"),
Column('phase_id_list', JSONB(astext_type=Text()), comment='研究阶段(逻辑外键表phase)'),
Column('intervention_id_list', JSONB(astext_type=Text()), comment='介入方式(逻辑外键表 intervention_function 给药方式)'),
Column('intervention_model', String, comment='干预模型'),
Column('primary_purpose', String, comment='首要目标(主要的目的)'),
Column('masking', String, comment='设盲方法'),
Column('conditions_id_list', JSONB(astext_type=Text()), comment='适应症列表(逻辑外键表 condition)'),
Column('number_enrolled', Integer, comment='入组人数'),
Column('sex', String(16), comment='性别(逻辑外键表 sex)'),
Column('study_age', JSONB(astext_type=Text()), comment='受试者年龄'),
Column('study_start_date', Date, comment='试验开始时间'),
Column('study_completion_date', Date, comment='试验结束时间'),
Column('official_title', String, comment='官方标题'),
Column('sponsor', String, comment='资助机构'),
Column('collaborators', JSONB(astext_type=Text()), comment='合作者'),
Column('result_first_posted', Date, comment='首次发布时间')
)
this metadata:
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class Clinicaltrial(Base):
__tablename__ = 'clinicaltrials'
id = Column(BigInteger, primary_key=True, server_default=text("nextval('shuimubio_auto_id.clinicaltrials_auto_id'::regclass)"), comment='数据库自增主键')
nct_id = Column(String, comment='Clinicaltrial的唯一ID')
study_title = Column(String, comment='标题')
study_type = Column(String, server_default=text("0"), comment="研究类型(现有四种'Expanded Access', 'Interventional', None, 'Observational')")
phase_id_list = Column(JSONB(astext_type=Text()), comment='研究阶段(逻辑外键表phase)')
intervention_id_list = Column(JSONB(astext_type=Text()), comment='介入方式(逻辑外键表 intervention_function 给药方式)')
intervention_model = Column(String, comment='干预模型')
primary_purpose = Column(String, comment='首要目标(主要的目的)')
masking = Column(String, comment='设盲方法')
conditions_id_list = Column(JSONB(astext_type=Text()), comment='适应症列表(逻辑外键表 condition)')
number_enrolled = Column(Integer, comment='入组人数')
sex = Column(String(16), comment='性别(逻辑外键表 sex)')
study_age = Column(JSONB(astext_type=Text()), comment='受试者年龄')
study_start_date = Column(Date, comment='试验开始时间')
study_completion_date = Column(Date, comment='试验结束时间')
official_title = Column(String, comment='官方标题')
sponsor = Column(String, comment='资助机构')
collaborators = Column(JSONB(astext_type=Text()), comment='合作者')
result_first_posted = Column(Date, comment='首次发布时间')
this code has error
AttributeError: 'Query' object has no attribute 'filter_by'
Query(Clinicaltrial).filter_by(Uniprot.uniprot_id.in_(uniprot_Ids))
But this line of code will work。
select__filter = t_uniprot.select().filter(t_uniprot.columns["uniprot_id"].in_(uniprot_Ids))
return await database.fetch_all(select__filter)
What's the difference between the two and how to change this class orm Make it accessible for select attr
According to the migration guide:
The Query object (as well as the BakedQuery and ShardedQuery extensions) become long term legacy objects, replaced by the direct usage of the select() construct in conjunction with the Session.execute() method.
So we can use select(…).where(…) for both ORM and Core (Table) queries:
from sqlalchemy import create_engine, Table, MetaData, Column, String,\
BigInteger, select
from sqlalchemy.orm import declarative_base, Session
engine = create_engine("sqlite://")
Base = declarative_base()
class Clinicaltrial(Base):
__tablename__ = 'clinicaltrials'
id = Column(BigInteger, primary_key=True, autoincrement=False)
nct_id = Column(String, comment='Clinicaltrial的唯一ID')
def __repr__(self):
return f"<Clinicaltrial(id={self.id})>"
Base.metadata.create_all(engine)
with engine.begin() as conn:
conn.exec_driver_sql(
"INSERT INTO clinicaltrials (id, nct_id) "
"VALUES (1, 'test1'), (2, 'test2'), (3, 'test3')"
)
with Session(engine) as sess:
# ORM query
q = select(Clinicaltrial).where(Clinicaltrial.id.in_([1, 3]))
results = sess.execute(q).all()
print(results)
# [(<Clinicaltrial(id=1)>,), (<Clinicaltrial(id=3)>,)]
# Core query
ct = Table("clinicaltrials", MetaData(), autoload_with=engine)
q = select(ct).where(ct.c.id.in_([1, 3]))
results = sess.execute(q).all()
print(results)
# [(1, 'test1'), (3, 'test3')]
I'm defining an PostgreSQL table using an SQLAlchemy declarative base, like this:
from sqlalchemy import Column, String, BigInteger
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class MyTable(Base):
__tablename__ = 'my_table'
id = Column('id', BigInteger, primary_key=True)
some_string = Column('some_string', String(256), nullable=False)
The nullable constraint guarantees that some_string cannot be null. However, I'd additionally like to give some_string a minimum length, or just forbid it from being the empty string. How can I do this?
Ideally we want the validation to be applied both at the database layer via a constraint and also in our model by rejecting attempts to set the property to a string that's too short even before trying to save it to the database. We can do the former with a CheckConstraint using the char_length function, and the latter by adding a validator. Below is an example that enforces a minimum length of 3 characters on some_string:
from sqlalchemy import Column, String, BigInteger
from sqlalchemy.schema import CheckConstraint
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import validates
Base = declarative_base()
class MyTable(Base):
__tablename__ = 'my_table'
id = Column('id', BigInteger, primary_key=True)
some_string = Column('some_string', String(256), nullable=False)
__table_args__ = (
CheckConstraint('char_length(some_string) > 2',
name='some_string_min_length'),
)
#validates('some_string')
def validate_some_string(self, key, some_string) -> str:
if len(some_string) <= 2:
raise ValueError('some_string too short')
return some_string
I wanna to get Primary Key of last inserted, I already know two way for this :
1) "lastrowid" with "raw SQL"
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, text
engine = create_engine('sqlite://')
meta = MetaData()
tbl = Table('tbl', meta,
Column('f1', Integer, primary_key=True),
Column('f2', String(64))
)
tbl.create(engine)
sql = text("INSERT INTO tbl VALUES (NULL, 'some_data')")
res = engine.execute(sql)
print(res.lastrowid)
2) "inserted_primary_key" with "insert()"
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite://')
meta = MetaData()
tbl = Table('tbl', meta,
Column('f1', Integer, primary_key=True),
Column('f2', String(64))
)
tbl.create(engine)
ins = tbl.insert().values(f2='some_data')
res = engine.execute(ins)
print(res.inserted_primary_key)
but my problem is "declarative_base()"
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite://')
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class TBL(Base):
__tablename__ = 'tbl'
f1 = Column(Integer, primary_key=True)
f2 = Column(String(64))
Base.metadata.create_all(engine)
rcd = TBL(f2='some_data')
session.add(rcd)
session.commit()
If i do this:
res = session.add(rcd)
It give me "None". or if i do this:
res = session.commit()
same thing happend. My question is:
Is there any good way to access "lastrowid" or "inserted_primary_key" in case of "declarative_base()"?
What is the best approach ?
After calling session.commit(), accessing rcd.f1 will return its generated primary key. SQLAlchemy automatically reloads the object from database after it has been expired by the commit.
I'm new to SQLAlchemy and relational databases, and I'm trying to set up a model for an annotated lexicon. I want to support an arbitrary number of key-value annotations for the words which can be added or removed at runtime. Since there will be a lot of repetition in the names of the keys, I don't want to use this solution directly, although the code is similar.
My design has word objects and property objects. The words and properties are stored in separate tables with a property_values table that links the two. Here's the code:
from sqlalchemy import Column, Integer, String, Table, create_engine
from sqlalchemy import MetaData, ForeignKey
from sqlalchemy.orm import relation, mapper, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///test.db', echo=True)
meta = MetaData(bind=engine)
property_values = Table('property_values', meta,
Column('word_id', Integer, ForeignKey('words.id')),
Column('property_id', Integer, ForeignKey('properties.id')),
Column('value', String(20))
)
words = Table('words', meta,
Column('id', Integer, primary_key=True),
Column('name', String(20)),
Column('freq', Integer)
)
properties = Table('properties', meta,
Column('id', Integer, primary_key=True),
Column('name', String(20), nullable=False, unique=True)
)
meta.create_all()
class Word(object):
def __init__(self, name, freq=1):
self.name = name
self.freq = freq
class Property(object):
def __init__(self, name):
self.name = name
mapper(Property, properties)
Now I'd like to be able to do the following:
Session = sessionmaker(bind=engine)
s = Session()
word = Word('foo', 42)
word['bar'] = 'yes' # or word.bar = 'yes' ?
s.add(word)
s.commit()
Ideally this should add 1|foo|42 to the words table, add 1|bar to the properties table, and add 1|1|yes to the property_values table. However, I don't have the right mappings and relations in place to make this happen. I get the sense from reading the documentation at http://www.sqlalchemy.org/docs/05/mappers.html#association-pattern that I want to use an association proxy or something of that sort here, but the syntax is unclear to me. I experimented with this:
mapper(Word, words, properties={
'properties': relation(Property, secondary=property_values)
})
but this mapper only fills in the foreign key values, and I need to fill in the other value as well. Any assistance would be greatly appreciated.
Simply use Dictionary-Based Collections mapping mapping - out of the box solution to your question. Extract from the link:
from sqlalchemy.orm.collections import column_mapped_collection, attribute_mapped_collection, mapped_collection
mapper(Item, items_table, properties={
# key by column
'notes': relation(Note, collection_class=column_mapped_collection(notes_table.c.keyword)),
# or named attribute
'notes2': relation(Note, collection_class=attribute_mapped_collection('keyword')),
# or any callable
'notes3': relation(Note, collection_class=mapped_collection(lambda entity: entity.a + entity.b))
})
# ...
item = Item()
item.notes['color'] = Note('color', 'blue')
print item.notes['color']
Or try the solution for Inserting data in Many to Many relationship in SQLAlchemy. Obviously you have to replace the list logic with the dict one.
Ask question author to post hist final code with associationproxy, which he mentioned he used in the end.
There is very similar question with slight interface difference. But it's easy to fix it by defining __getitem__, __setitem__ and __delitem__ methods.
Comment for Brent, above:
You can use session.flush() instead of commit() to get an id on your model instances. flush() will execute the necessary SQL, but will not commit, so you can rollback later if needed.
I ended up combining Denis and van's posts together to form the solution:
from sqlalchemy import Column, Integer, String, Table, create_engine
from sqlalchemy import MetaData, ForeignKey
from sqlalchemy.orm import relation, mapper, sessionmaker
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
meta = MetaData()
Base = declarative_base(metadata=meta, name='Base')
class PropertyValue(Base):
__tablename__ = 'property_values'
WordID = Column(Integer, ForeignKey('words.id'), primary_key=True)
PropID = Column(Integer, ForeignKey('properties.id'), primary_key=True)
Value = Column(String(20))
def _property_for_name(prop_name):
return s.query(Property).filter_by(name=prop_name).first()
def _create_propval(prop_name, prop_val):
p = _property_for_name(prop_name)
if not p:
p = Property(prop_name)
s.add(p)
s.commit()
return PropertyValue(PropID=p.id, Value=prop_val)
class Word(Base):
__tablename__ = 'words'
id = Column(Integer, primary_key=True)
string = Column(String(20), nullable=False)
freq = Column(Integer)
_props = relation(PropertyValue, collection_class=attribute_mapped_collection('PropID'), cascade='all, delete-orphan')
props = association_proxy('_props', 'Value', creator=_create_propval)
def __init__(self, string, freq=1):
self.string = string
self.freq = freq
def __getitem__(self, prop):
p = _property_for_name(prop)
if p:
return self.props[p.id]
else:
return None
def __setitem__(self, prop, val):
self.props[prop] = val
def __delitem__(self, prop):
p = _property_for_name(prop)
if p:
del self.props[prop]
class Property(Base):
__tablename__ = 'properties'
id = Column(Integer, primary_key=True)
name = Column(String(20), nullable=False, unique=True)
def __init__(self, name):
self.name = name
engine = create_engine('sqlite:///test.db', echo=False)
Session = sessionmaker(bind=engine)
s = Session()
meta.create_all(engine)
The test code is as follows:
word = Word('foo', 42)
word['bar'] = "yes"
word['baz'] = "certainly"
s.add(word)
word2 = Word('quux', 20)
word2['bar'] = "nope"
word2['groink'] = "nope"
s.add(word2)
word2['groink'] = "uh-uh"
del word2['bar']
s.commit()
word = s.query(Word).filter_by(string="foo").first()
print word.freq, word['baz']
# prints 42 certainly
The contents of the databases are:
$ sqlite3 test.db "select * from property_values"
1|2|certainly
1|1|yes
2|3|uh-uh
$ sqlite3 test.db "select * from words"
1|foo|42
2|quux|20
$ sqlite3 test.db "select * from properties"
1|bar
2|baz
3|groink