how to do a join in sqlalchemy session query? - python

I need to find the equivalent of this query in sqlalchemy.
SELECT u.user_id, u.user_name, c.country FROM
table_user u , table_country c WHERE u.user_email = 'abc#def.com'
i tried this below code:
session.query(User).join(Country.country).filter(User.user_email == 'abc#def.com').first()
and this gave me below error :
AttributeError: 'ColumnProperty' object has no attribute 'mapper'
can anyone give an example of join query with tables mapped to new class objects ?

Try this, assuming your User mapper has a relationship to Country configured.
user, country = session.query(User, Country.country).join(Country).filter(User.user_email == 'abc#def.com').first()

Related

SQLAlchemy ORM: "AttributeError: Could not locate column in row"

I'm learning SQLAlchemy right now, but I've encountered an error that puzzles me. Yes, there are similar questions here on SO already, but none of them seem to be solved.
My goal is to use the ORM mode to query the database. So I create a model:
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import Session, registry
from sqlalchemy.sql import select
database_url = "mysql+pymysql://..."
mapper_registry = registry()
Base = mapper_registry.generate_base()
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(32))
engine = create_engine(database_url, echo=True)
mapper_registry.metadata.create_all(engine)
New I want to load the whole row for all entries in the table:
with Session(engine) as session:
for row in session.execute(select(User)):
print(row.name)
#- Error: #
Traceback (most recent call last):
...
print(row.name)
AttributeError: Could not locate column in row for column 'name'
What am I doing wrong here? Shouldn't I be able to access the fields of the ORM model? Or am I misunderstanding the idea of ORM?
I'm using Python 3.8 with PyMySQL 1.0.2 and SQLAlchemy 1.4.15 and the server runs MariaDB.
This is example is as minimal as I could make it, I hope anyone can point me in the right direction. Interestingly, inserting new rows works like a charm.
session.execute(select(User)) will return a list of Row instances (tuples), which you need to unpack:
for row in session.execute(select(Object)):
# print(row[0].name) # or
print(row["Object"].name)
But I would use the .query which returns instances of Object directly:
for row in session.query(Object):
print(row.name)
I'd like to add some to what above #Van said.
You can get object instances using session.execute() as well.
for row in session.execute(select(User)).scalars().all():
print(row.name)
Which is mentioned in migrating to 2.0.
I just encountered this error today when executing queries that join two or more tables.
It turned out that after updating psycopg2 (2.8.6 -> 2.9.3), SQLAlchemy (1.3.23 -> 1.4.39), and flask-sqlalchemy (2.4.4 -> 2.5.1) the Query.all() method return type is a list of sqlalchemy.engine.row.Rows and before it was a list of tuples. For instance:
query = database.session.query(model)
query = query.outerjoin(another_model, some_field == another_field)
results = query.all()
# type(results[0]) -> sqlalchemy.engine.row.Row
if isinstance(results[0], (list, tuple)):
# Serialize as a list of rows
else:
# Serialize as a single row

sqlAlchemy to access blob via a hybrid propery?

I'm trying to add a block of text into a sqlAlchemy table, which I want to compress to save space with it. Looking through various answers I came up with what I think should be working, but is not. I'm working with a sqlite database.
Updated: Was pointed out I was attempting to use mysql on sqlite which I wasn't aware that was what was happening. I adjusted to use zlib instead and it works to a degree, which gives me a new error that I do not understand.
# proper imports and stuff to make this work
from sqlalchemy import func
class Data(Base):
__tablename__ = 'data'
# ...
text_blobbed = Column('text', BLOB)
#hybrid_property
def text(self):
# return func.decompress(self.text_blobbed)
return self.text_blobbed.decode("zlib")
#text.setter
def text(self, stuff):
# self.text_blobbed = func.compress(stuff)
self.text_blobbed = stuff.encode("zlib")
old error from func.
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such function: compress [SQL: ...... ]
I can now add in the text via Data.text = "a really big block of text"
But when I go to query for this like
session.query(Data.text).filter(Data.id.like(2)).first()
I get an error:
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Data.text_blobbed has an attribute 'decode'
Doing this is fine.
r = session.query(Data).filter(Data.id.like(2)).first()
print r.text
I've also looked at the text_blobbed which is a set(). And I can do this that works:
r = session.query(Data.text_blobbed.filter( ... ).first()[0].decode("zlib")
print r
But if I move that [0] into the hybrid_property for
...
return self.text_blobbed[0].decode("zlib")
and query:
r = session.query(Data.text).filter( ... ).first()
I get the error:
NotImplementedError: Operator 'getitem' is not supported on this expression
So, I'm a bit confused still.
I've been looking at these things:
SQLAlchemy - Writing a hybrid method for child count
mysql Compress() with sqlalchemy
SELECT UNCOMPRESS(text) FROM with sqlalchemy
http://docs.sqlalchemy.org/en/latest/orm/mapped_sql_expr.html?highlight=descriptor

How to pass apostrophe from python to psql?? 'DatabaseError' object has no attribute 'encode'

I am using locally host postgresql database 9.6. which connect like this
conn_string = """host='localhost'
dbname='contact_form'
user='{0}'
password='{1}'""".format(db_username,
db_password)
conLocal = psycopg2.connect(conn_string)
I have name = "Mark's Dane", which contain apostrophe
which I want to pass into database, so I have following query
query = """SELECT pet_name
FROM pet
WHERE name = '{0}'""".format(name)
Then I run following code:
pet_name = read_sql(query, conLocal)
However, I am getting following error :
'DatabaseError' object has no attribute 'encode'
Don't use format for interpolating values into SQL strings.
https://en.wikipedia.org/wiki/SQL_injection
http://bobby-tables.com
Instead, use the execute method, passing a tuple of parameters, e.g.
conn.execute("""SELECT pet_name
FROM pet
WHERE name = %s""", (name,))
Note the use of the 1-tuple (name,).
See the Python DBI-API documentation and the psycopg2 manual for more.

Pyramid / SQLAlchemy trouble with joined models

I'm really new to Python & as new to Pyramid (this is the first thing I've written in Python) and am having trouble with a database query...
I have the following models (relevant to my question anyway):
MetadataRef (contains info about a given metadata type)
Metadata (contains actual metadata) -- this is a child of MetadataRef
User (contains users) -- this is linked to metadata. MetadataRef.model = 'User' and metadata.model_id = user.id
I need access to name from MetadataRef and value from Metadata.
Here's my code:
class User(Base):
...
_meta = None
def meta(self):
if self._meta == None:
self._meta = {}
try:
for item in DBSession.query(MetadataRef.key, Metadata.value).\
outerjoin(MetadataRef.meta).\
filter(
Metadata.model_id == self.id,
MetadataRef.model == 'User'
):
self._meta[item.key] = item.value
except DBAPIError:
##TODO: actually do something with this
self._meta = {}
return self._meta
The query SQLAlchemy is generating does return what I need (close enough anyway -- it needs to query model_id as part of the ON clause rather than the WHERE, but that's minor and I'm pretty sure I can figure that out myself):
SELECT metadata_refs.`key` AS metadata_refs_key, metadata.value AS metadata_value
FROM metadata_refs LEFT OUTER JOIN metadata ON metadata_refs.id = metadata.metadata_ref_id
WHERE metadata.model_id = %s AND metadata_refs.model = %s
However, when I access the objects I get this error:
AttributeError: 'KeyedTuple' object has no attribute 'metadata_value'
This leads me to think there's some other way I need to access it, but I can't figure out how. I've tried both .value and .metadata_value. .key does work as expected.
Any ideas?
You're querying separate attributes ("ORM-enabled descriptors" in SA docs):
DBSession.query(MetadataRef.key, Metadata.value)
in this case the query returns not full ORM-mapped objects, but a KeyedTuple, which is a cross between a tuple and an object with attributes corresponding to the "labels" of the fields.
So, one way to access the data is by its index:
ref_key = item[0]
metadata_value = item[1]
Alternatively, to make SA to use a specific name for column, you may use Column.label() method:
for item in DBSession.query(MetadataRef.key.label('ref_key'), Metadata.value.label('meta_value'))...
self._meta[item.key] = item.meta_value
For debugging you can use Query.column_descriptions() method which will tell you the names of the columns returned by the query.

SQLAlchemy select only one column

Try to select one column from pg_shadow table the following way:
role_tbl = Table('pg_shadow', MetaData(engine), autoload=True)
db.query(role_tbl.c.passwd).filter_by(usename='name')
And get an error:
* AttributeError: 'NoneType' object has no attribute 'class_'
The error is the result of having no entity in the query:
The keyword expressions are extracted from the primary entity of the query, or the last entity that was the target of a call to Query.join().
where an entity is a mapped class, or the Table object, but you're querying a single column. The proper way to filter would be:
db.query(role_tbl.c.passwd).filter(role_tbl.c.usename == 'name')
In a more recent version of SQLAlchemy the error is:
NoInspectionAvailable: No inspection system is available for object of type <class 'NoneType'>
Try this one:
role_tbl.select([role_tbl.c.passwd]).where(username=='name').execute().fetchall()
Or probably there is no such column in this table.
You can check it by printing all columns
print role_tbl.columns
P.S.
And also you should use one instance of metadata: MetaData(engine) (it should store information about all tables)
To select only one column you can use Select.with_only_columns:
from sqlalchemy import MetaData, Table, Column, Text
meta = MetaData()
table = Table('user', meta,
Column("name", Text),
Column("full_name", Text))
stmt = (table.select()
.with_only_columns([table.c.name])
)
print(stmt)
# SELECT "user".name
# FROM "user"

Categories