feel free to hang here, note that some of the "chat" happens on libera.chat #sqlalchemy
Looks like I don't use session properly. Not sure what am I missing 🤔
When fetching existing data from the database and then updating, looks like SQL Alchemy doesn't recognize already stored objects that are part of relationship and tries to insert them again.
""" Here I generate data and merge it into the database """
for track_name in ['track-one','track-two','track-three']:
track = Track(name=track_name)
track.load_top_tags()
session.merge(track)
session.commit()
""" Clean session """
session.expunge_all()
""" Here I fetch stored tracks and update their related tag objects (doesnt work) """
for track in session.query(Track).all():
for top_tag in track.top_tags: # Load tags from database
print(f"{track.name} has top tag {top_tag.tag.name}")
track.load_top_tags()
session.merge(track)
session.commit()
This results in (sqlite3.IntegrityError) UNIQUE constraint failed: tags.id
error because SQLAlchemy is trying to insert data that already exists
Here is the full example with resulting output: https://colab.research.google.com/drive/18OMhYNxZGnKU4gaQ8l-ObKmVQEf50wtp?usp=sharing
my_instance in set(fetched_objs_from_table)
), but that doesn't seem to work. Or I could try to insert each object and catch IntegrityError
when the unique-key constraint fails, but that is probably bad for performance.
Thanks Federico, I will check that out.
What I am using now as workaround is a class decorator which adds an equality check (i.e., overriding __eq__
and __ne__
of the objects) similar to the one described here: https://stackoverflow.com/questions/39043003/comparing-sqlalchemy-object-instances-for-equality-of-attributes. The difference is my decorator takes the names of the attributes to compare as arguments, instead of checking all. (Unfortunately, at decoration time the __table_args__
are not populated yet, otherwise I could use the attributes used in the UniqueConstraint
of the table for the comparison).
With this I can do:
# Too bad those are in a list and can't be put in a set
foos_existing = session.execute(sqlalchemy.select(Foo)).scalars().all()
for foo in foos_new:
# Fancy equality check happening here
if foo not in foos_existing:
session.add(foo)
session.commit()
Issues:
set()
.Foo
records get inserted to the table while this code runs, the insert might insert a record which already exists (i.e., has just been added). So I might have to wrap this in a transaction.Any ideas to improve this overall?
Ok, I'm trying to do something like this with postgresql:
class ThingWithPaths(BaseModel):
...
path1 = Column(Array(Text))
path2 = Column(Array(Text))
@hybrid_property
def paths(self):
return self.path1, self.path2
@paths.expression
def paths(cls):
return postgresql.array((self.path1, self.path2))
I'd hoped that that last expression would make the "fake" paths field queryable, like:
...filter(ThingWithPaths.paths == postgresql.array((postgresql.array(('one', 'two')), postgresql.array(('three', 'four')))))...
...which does produce the kindof right looking WHERE clause:
WHERE ARRAY[thing_with_paths.path1, thing_with_paths.path2] = ARRAY[ARRAY[%(param_1)s, %(param_2)s, %(param_3)s], ARRAY[%(param_4)s, %(param_5)s, %(param_6)s]]]
But upon execution, I get a non-specific DB error. Any suggestions?
if country in [TDCountry.america, TDCountry.us_america]:
query = query.where(
TwelveStocks.country=(TDCountry.america.value | TDCountry.us_america.value)) â– â– Operator "|" not supported for types "Literal['United States']" and "Literal['United States of America']"
)
if country in [TDCountry.america, TDCountry.us_america]:
query = query.where(
or_(
stocks.c.country == TDCountry.america,
stocks.c.country == TDCountry.us_america,
)
)
sqlalchemy.exc.ProgrammingError: (sqlalchemy.dialects.postgresql.asyncpg.ProgrammingError) <class 'asyncpg.exceptions.DatatypeMismatchError'>: column "market" is of type tdmarket but expression is of type character varying
HINT: You will need to rewrite or cast the expression.
[SQL: INSERT INTO halts (date, "haltTime", ticker, "haltPrice", market, reason, "resumeTime", "resumePrice", country, watching) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)]
[parameters: (datetime.date(2021, 12, 16), datetime.time(11, 11, 11), 'string', 0.0, <TDMarket.nasdaq: 'NASDAQ'>, 'string', None, None, <Country.canada: 'canada'>, False)]
(Background on this error at: https://sqlalche.me/e/14/f405)
class TDMarket(str, enum.Enum):
nasdaq = "NASDAQ"
nyse = "NYSE"
iex = "IEX"
otc = "OTC"
cboe = "CBOE"
tsx_v = "TSX_V" # "TSX-Venture" "TSXV"
tsx = "TSX"
cnq = "CNQ"
neo = "NEO"
halt = Halts(
date=halt.date,
haltTime=halt.haltTime,
ticker=halt.ticker,
haltPrice=halt.haltPrice,
market=TDMarket(halt.market),
reason=halt.reason,
resumeTime=halt.resumeTime,
resumePrice=halt.resumePrice,
country=halt.country.value,
watching=halt.watching,
)