SQL alchemy query a single column - python

I am sure this has been answered before and I see a few related answers but none seem to be the issue I am facing. I am using a SQL Alchemy model that uses a SQL server DB underneath and I am using it to query the DB with a session. The normal queries etc work fine with no errors. However when I ask for only one field instead of all it gives me an error (see later).
Basically boiled down to the simplest I have a model like so:
class FactoryShop(Base):
# case insensitive, refers to the actual table in the DB called factoryshop
__tablename__ = 'factoryshop'
ID = Column(Integer, primary_key=True, autoincrement=True)
Name = Column(String(255))
Parts = Column(Integer)
Strength = Column(Integer)
Average = Column(Float)
...
Using a session I can query all columns like so:
>>> session.query(FactoryShop).filter(FactoryShop.Parts==20000)
<sqlalchemy.orm.query.Query object at 0x10578c280>
However if I try to just ask for the Name like below I get a long error. I searched for that specific error which involves 'selectable' but I didn't come across a relevant answer.
>>> session.query(FactoryShop.Name).filter(FactoryShop.Parts==20000)
AttributeError: Neither 'AnnotatedColumn' object nor 'Comparator' object has an attribute 'selectable'
If there is already an answer please point me to it and I will delete this one.

You are not querying for it correctly. But you are very close.
result = session.query(FactoryShop).filter(FactoryShop.Parts==20000).first()
Then, you can call result.Name to get the name of that FactoryShop Object.

Related

Sqlalchemy query on multiple relationship between two tables

I am having trouble with the following setup of a sqlalchemy ORM connected to a postgresql db.
class Map(Base):
__tablename__ = "map"
id = Column(BigInteger, Sequence(name="myseq"), primary_key=True)
cmp_1_id = Column(BigInteger, ForeignKey("component.id"))
cmp_2_id = Column(BigInteger, ForeignKey("component.id"))
class Component(Base):
__tablename__ = "component"
id = Column(BigInteger, Sequence(name="myseq"), primary_key=True)
map_1 = relationship("Map", back_populates="cmp_1", foreign_keys=Map.cmp_1_id, uselist=False)
map_2 = relationship("Map", back_populates="cmp_2", foreign_keys=Map.cmp_2_id, uselist=False)
Map.cmp_1 = relationship(
"Component", back_populates="map_1", primaryjoin=Map.cmp_1_id == Component.id
)
Map.cmp_2 = relationship(
"Component", back_populates="map_2", primaryjoin=Map.cmp_2_id == Component.id
)
Now I want to query a specific Map object, whose cmp_1 object has a certain "known_value" of other_attribute. I tried various statements, using Query API and Select API and with a colleague finally found this solution to be working:
(session.query(Map.id)
.join(Map.cmp_1)
.where(Component.other_attribute=="known_value")
.one()[0]
)
During my research on the topic I read through some other SO articles, which raised further questions. So here they come:
My main question: why can't I do the query like this:
(session.query(Alias_Map_Expanded.id)
.where(Map.cmp_1.other_attribute=="known_value")
).one()[0]
This raises the exception AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Map.cmp_1 has an attribute 'other_attribute'
More generally: how would I design the model better (as in: more robust and easier to jump between relations) to possibly be able to do the above? The relationships need to be One (Component) To Many (Map), i.e a Map object points to one to two (cmp_2 is optional) components. In turn a component can be pointed to from multiple Map rows/objects.
Based on this: Should I always define a foreign key along with a relationship to not break the relationship inside the db? Update: I removed this question because I now find it rather misleading and not really worth having it answered.
Based on that: I guess I also need to use the post_update to not have a circular dependency? Or do I misinterpret the use of post_update?
Thanks in advance!
After some thorough consulting of the extensive sqlalchemy docs I found some answers:
To my first question and the related query: in my ORM classes I did not specify the loading type of the data, leaving it at the default type "lazy". Therefore the other_attribute attribute's value is not loaded with the first query but rather it would take a second call to query1_result.other_attribute upon which the related content would be queried separately. Alternatively I'd need to specify an eager loading type for the proposed query to be working.
I just figured, even if I use eager-loaded queries, I still cannot filter related objects, using class-level ORM syntax, because at that point the ORM instance has not yet mapped its relative. The filtering ("where" clause) needs to be formulated on SQL level, i.e like the first example I gave above...
There is most likely no meaningful answer to that, especially without deeper knowledge of my database structure...
Third question, based on link: I think my question is somewhat strange and maybe even misleading. I will remove it from the original post.
Last question, based on 2nd link: I haven't investigated so much more on this question, being it the least important to me, but I think I got the concept of post_update wrong and will not need it for my purpose.
I got all of it from sqlalchemy docs, so in case you hit that question and have a similar problem, work your way through the extensive documentation conglomeration. The answer is most likely there.

How do I have Flask-SQLAlchemy with Postgres, have the primary key not reuse a deleted number?

I am using Flask-SQLAlchemy with Postgres. I noticed that when I delete a record, the next record will reuse that one's id, which is not ideal for my purposes. Another SO question that this is the default behavior. In particular, his SO question discussed the sql behind the scenes. However, when I tested the solution in this problem, it did not work. In fact, postgres was not using SERIAL for the primary key. I was having to edit it in pgadmin myself. Solutions in other programs mention using a Sequence but it is not shown where the sequence is coming from.
So I would hope this code:
class Test1(db.Model):
__tablename__ = "test1"
# id = ... this is what needs to change
id = db.Column(db.Integer, primary_key=True)
would not reuse say 3 if record 3 was deleted and another was created like so:
i1 = Invoice()
db.session.add(i1)
i2 = Invoice()
db.session.add(i2)
i3 = Invoice()
db.session.add(i3)
db.session.commit()
invs = Invoice.query.all()
for i in invs:
print(i.id) # Should print 1,2,3
Invoice.query.filter(id=3).delete() # no 3 now
db.session.commit()
i4 = Invoice()
db.session.add(i4)
db.session.commit()
invs = Invoice.query.all()
for i in invs:
print(i.id) # Should print 1,2,4
Other, solutions said to use autoincrement=False. Okay, but then how do I determine what the number to set the id to is? Is there a way to save a variable in the class without it being a column:
class Test2(db.Model)
__tablename__ = 'test2'
id = ...
last_id = 3
# code to set last_id when a record is deleted
Edit:
So I could (although I do not think I should) use Python to do this. I think this more clearly tries to illustrate what I am trying to do.
class Test1(db.Model):
__tablename__ = "test1"
# id = ... this is what needs to change
id = db.Column(db.Integer, primary_key=True)
last_used_id = 30
def __init__(self):
self.id = last_used_id + 1
self.last_used_id +=1
# Not sure if this somehow messes with SQLAlchemy / the db making the id first.
This will make any new record not touch an id that was already used.
However, with this I approach, I do encounter the class variable issue behavior of Python. See this SO question
Future self checking: See UUID per #net comment here:
You should use autoincrement=True. This will automatically increment the id everytime you add a new row.
class Test1(db.Model):
__tablename__ = "test1"
id = db.Column(db.Integer, primary_key=True, autoincrement=True, unique=True, nullable=False)
....
By default Postgres will not reuse ids due to performance issues. Attempting to avoid gaps or to re-use deleted IDs creates horrible performance problems. See the PostgreSQL wiki FAQ.
You don't need to keep track of the id. When you call db.session.add(i4) and db.session.commit() it will automatically insert with the incremented id.

Check if row exists in database table using SQLAlchemy

I am learning SQLAlchemy and trying to check if a record exists in postgreSQL database using SQLAlchemy. There are many similar question here, but I am stuck. Here is my query:
ret = session.query(exists().where(COMPANY.name == 'MyTestCompany')).scalar()
"COMPANY" is table name , when I run it, I get error:
"NameError: name 'COMPANY' is not defined"
Should I somehow "register" COMPANY table as an object in current session or the problem lies somewhere else?
The error you are getting means there is no COMPANY variable defined in your python code.
Did you use any other ORM features? Usually you would need to create a Company Model to run a query like this. A model would look something like this:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
Check out how they create a User model in this tutorial
If you want to interact with SQL directly without ORM features you might be interested in SQLAlchemy Core

SQLAlchemy argument error: ondelete undefined

I have a table where there is a User table and a Follow table. The follow table has two foreign keys which refer to the user's id who follows and the user who's being followed id.
If a user was to delete their account, I would like all the records in the following table to be deleted along with the User record. The way I thought to do this was by using onupdate='CASCASE', ondelete='CASCASE' like so:
follower = db.Column(db.Integer, db.ForeignKey('accounts.id'), onupdate='CASCADE', ondelete='CASCADE')
following = db.Column(db.Integer, db.ForeignKey('accounts.id'), onupdate='CASCADE', ondelete='CASCADE')
I try to update my database (using Flask-Migrate/Alembic) however, I receive the error:
sqlalchemy.exc.ArgumentError: Unknown arguments passed to Column: ['ondelete']
So it appears onupdate works fine but not ondelete.
Why do I have this issue and how can I solve it? Thanks.
onupdate and ondelete are parameters for the ForeignKey constructor, not the Column constructor. See http://docs.sqlalchemy.org/en/rel_0_9/core/constraints.html#sqlalchemy.schema.ForeignKey.
The Column constructor does have an onupdate parameter which is why it seemed to work, but what you are looking for is the ForeignKey onupdate and ondelete parameters.
It should look like this:
follower = db.Column(db.Integer, db.ForeignKey('accounts.id', onupdate='CASCADE', ondelete='CASCADE'))
following = ...

SQLAlchemy: a better way for update with declarative?

Let's say I have a user table in declarative mode:
class User(Base):
__tablename__ = 'user'
id = Column(u'id', Integer(), primary_key=True)
name = Column(u'name', String(50))
When I know user's id without object loaded into session, I update such user like this:
ex = update(User.__table__).where(User.id==123).values(name=u"Bob Marley")
Session.execute(ex)
I dislike using User.__table__, should I stop worrying with that?
Is there a better way to do this?
There's also some update capability at the ORM level. It doesn't handle any tricky cases yet but for the trivial case of single row update (or bulk update) it works fine. It even goes over any already loaded objects and applies the update on them also. You can use it like this:
session.query(User).filter_by(id=123).update({"name": u"Bob Marley"})
You're working on clause level here, not on model/entity/object level. Clause level is lower than mapped objects. And yes, something have to be done to convert one terms into others.
You could also stay on object level and do:
session = Session()
u = session.query(User).get(123)
u.name = u"Bob Marley"
session.commit()
but it will be significantly slower since it leads to the mapped object construction. And I'm not sure that it is more readable.
In the example your provided I see the most natural and “right” solution. I would not worry about little __table__ magic.
Similar functionality is available via the update() method on Table object.
class User(Base):
__tablename__ = 'user'
id = Column('id', Integer(), primary_key=True)
name = Column('name', String(50))
stmt = User.__table__.update().where(User.id==5).values(name='user #5')
To use User.__table__ is how its done in SQLAlchemy.

Categories