I am using the Peewee library in Python and I want to check if a query exists. I do not want to create a record if it doesn't exist, so I don't want to use get_or_create. There must be a better solution than to use try/except with get but I don't see anything. Please let me know if there is a better way. Thanks.
You can use .exists():
query = User.select().where(User.username == 'charlie')
if query.exists():
# A user named "charlie" exists.
cool()
http://docs.peewee-orm.com/en/latest/peewee/api.html?highlight=exists#SelectBase.exists
If you just need to check existence use the accepted answer.
If you are going to use the record if it exists you can make use of Model.get_or_none() as this removes the need to use a try/catch and will not create a record if the record doesn't exist.
class User(peewee.Model):
username = peewee.CharField(unique=True)
user = User.get_or_none(username='charlie')
if user is not None:
# found user, do something with it
pass
Alternatively, if you want to check if e.g. some other table refers this record, you can use WHERE EXISTS (subquery) clause. It is not supported natively by PeeWee, but it can be easily constructed:
subquery = Child.select(Param('1')).where(Child.parent == Parent.id)
parents_with_children = Parent.select().where(
Clause(SQL('EXISTS'), subquery))
It is equivalent to the following SQL:
SELECT * FROM parent
WHERE EXISTS (SELECT 1 FROM child
WHERE child.parent_id = parent.id);
Here I used SELECT 1 for subquery to avoid fetching unneeded information (like child.id). Not sure if such optimization is actually required.
UPD (Feb 2022)
After more than 5 years of peewee evolution, it looks like the Clause class is gone.
The following code may work (I didn't have a chance to test it though):
subquery = Child.select(Param('1')).where(Child.parent == Parent.id)
parents_with_children = Parent.select().where(
NodeList((SQL('EXISTS'), subquery)))
Related
I have a Flask application which uses Flask-SQLAlchemy to connect to a MySQL database.
I would like to be able to check whether a row is present in a table. How would I modify a query like so to check the row exists:
db.session.query(User).filter_by(name='John Smith')
I found a solution on this question which uses SQLAlchemy but does not seem to fit with the way Flask-SQLAlchemy works:
from sqlalchemy.sql import exists
print session.query(exists().where(User.email == '...')).scalar()
Thanks.
Since you only want to see if the user exists, you don't want to query the entire object. Only query the id, it exists if the scalar return is not None.
exists = db.session.query(User.id).filter_by(name='davidism').first() is not None
SELECT user.id AS user_id
FROM user
WHERE user.name = ?
If you know name (or whatever field you're querying) is unique, you can use scalar instead of first.
The second query you showed also works fine, Flask-SQLAlchemy does nothing to prevent any type of query that SQLAlchemy can make. This returns False or True instead of None or an id like above, but it is slightly more expensive because it uses a subquery.
exists = db.session.query(db.exists().where(User.name == 'davidism')).scalar()
SELECT EXISTS (SELECT *
FROM user
WHERE user.name = ?) AS anon_1
bool(User.query.filter_by(name='John Smith').first())
It will return False if objects with this name doesn't exist and True if it exists.
Wrap a .exists() query in another session.query() with a scalar() call at the end. SQLAlchemy will produce an optimized EXISTS query that returns True or False.
exists = db.session.query(
db.session.query(User).filter_by(name='John Smith').exists()
).scalar()
SELECT EXISTS (SELECT 1
FROM user
WHERE user.name = ?) AS anon_1
While it's potentially more expensive due to the subquery, it's more clear about what's being queried. It may also be preferable over db.exists().where(...) because it selects a constant instead of the full row.
Think there is a typo in davidism's answer, this works for me:
exists = db.session.query(**User**).filter_by(name='davidism').scalar() is not None
I have an SQLAlchemy mapped class MyClass, and two aliases for it. I can eager-load a relationship MyClass.relationship on each alias separately using selectinload() like so:
alias_1, alias_2 = aliased(MyClass), aliased(MyClass)
q = session.query(alias_1, alias_2).options(
selectinload(alias_1.relationship),
selectinload(alias_2.relationship))
However, this results in 2 separate SQL queries on MyClass.relationship (in addition to the main query on MyClass, but this is irrelevant to the question). Since these 2 queries on MyClass.relationship are to the same table, I think that it should be possible to merge the primary keys generated within the IN clause in these queries, and just run 1 query on MyClass.relationship.
My best guess for how to do this is:
alias_1, alias_2 = aliased(MyClass), aliased(MyClass)
q = session.query(alias_1, alias_2).options(
selectinload(MyClass.relationship))
But it clearly didn't work:
sqlalchemy.exc.ArgumentError: Mapped attribute "MyClass.relationship" does not apply to any of the root entities in this query, e.g. aliased(MyClass), aliased(MyClass). Please specify the full path from one of the root entities to the target attribute.
Is there a way to do this in SQLAlchemy?
So, this is exactly the same issue we had. This docs explains how to do it.
You need to add selectin_polymorphic. For anyone else if you are using with_polymorphic in your select then remove it.
from sqlalchemy.orm import selectin_polymorphic
query = session.query(MyClass).options(
selectin_polymorphic(MyClass, [alias_1, alias_2]),
selectinload(MyClass.relationship)
)
I'm inserting/updating objects into a MySQL database using the peewee ORM for Python. I have a model like this:
class Person(Model):
person_id = CharField(primary_key=True)
name = CharField()
I create the objects/rows with a loop, and each time through the loop have a dictionary like:
pd = {"name":"Alice","person_id":"A123456"}
Then I try creating an object and saving it.
po = Person()
for key,value in pd.items():
setattr(po,key,value)
po.save()
This takes a while to execute, and runs without errors, but it doesn't save anything to the database -- no records are created.
This works:
Person.create(**pd)
But also throws an error (and terminates the script) when the primary key already exists. From reading the manual, I thought save() was the function I needed -- that peewee would perform the update or insert as required.
Not sure what I need to do here -- try getting each record first? Catch errors and try updating a record if it can't be created? I'm new to peewee, and would normally just write INSERT ... ON DUPLICATE KEY UPDATE or even REPLACE.
Person.save(force_insert=True)
It's documented: http://docs.peewee-orm.com/en/latest/peewee/models.html#non-integer-primary-keys-composite-keys-and-other-tricks
I've had a chance to re-test my answer, and I think it should be replaced. Here's the pattern I can now recommend; first, use get_or_create() on the model, which will create the database row if it doesn't exist. Then, if it is not created (object is retrieved from db instead), set all the attributes from the data dictionary and save the object.
po, created = Person.get_or_create(person_id=pd["person_id"],defaults=pd)
if created is False:
for key in pd:
setattr(fa,key,pd[key])
po.save()
As before, I should mention that these are two distinct transactions, so this should not be used with multi-user databases requiring a true upsert in one transaction.
I think you might try get_or_create()? http://peewee.readthedocs.org/en/latest/peewee/querying.html#get-or-create
You may do something like:
po = Person()
for key,value in pd.items():
setattr(po,key,value)
updated = po.save()
if not updated:
po.save(force_insert=True)
Hi I am using Flask Peewee and trying to update merchant_details model but it is not working.
Following is the error I am getting:
AttributeError: 'SelectQuery' object has no attribute 'update'
mdetails = merchant_details.filter(merchant_details.merchant_id==session['userid']).update(
merchant_name=request.form['merchantname'],
first_name=request.form['firstname'],
last_name=request.form['lastname'],
)
Please Help!
First, it looks like you are using pre-2.0 syntax (the filter method is now deprecated). I'd recommend looking at the docs for info on the latest version.
Typically, you do not "update a query". The two main ways of accomplishing this is are...
1.) Use a query to retrieve an object, then use the save() method to update the object. For example...
mdetails = MerchantDetails.select().where(MerchantDetails.id == 42).get()
mdetails.name = 'new name'
mdetails.save() # Will do the SQL update query.
2.) Use a SQL update statement...
q = MerchantDetails.update(name='new name')
.where(MerchantDetails.id == 42)
q.execute() # Will do the SQL update query.
Both of these, in essence, accomplish the same thing. The first will make two queries o the database (one to SELECT the record, another to UPDATE the record), while the second will only use one SQL call (to UPDATE the record).
I got the solution
mdetails = merchant_details.update(
merchant_name=request.form['merchantname'],
first_name=request.form['firstname'],
last_name=request.form['lastname'],
street_1=request.form['street1'],
street_2=request.form['street2'],
state=request.form['state'],
city=request.form['city'],
phone=request.form['phone'],
zipcode=request.form['zip'],
).where(merchant_details.merchant_id==session['userid'])
mdetails.execute()
Anyways Thanks Mark
I searched for this solution too and thanks to #Mark and #Rohit I changed my code (peeweee with PostgreSQL) and is working.
To add a small improve it seems the update will be executed even if you will not use the variable. For me is simpler and a cleaner code:
merchant_details.update(
merchant_name=request.form['merchantname'],
first_name=request.form['firstname'],
last_name=request.form['lastname'],
street_1=request.form['street1'],
street_2=request.form['street2'],
state=request.form['state'],
city=request.form['city'],
phone=request.form['phone'],
zipcode=request.form['zip'],
).where(merchant_details.merchant_id==session['userid']).execute()
I am using SQLAlchemy + Pyramid to operate on my database. However, there are some optional tables which are not always expected to be present in the DB. So while querying them I try to catch such cases with the NoSuchTableError
try:
x = session.query(ABC.name.label('sig_name'),func.count('*').label('count_')).join(DEF).join(MNO).filter(MNO.relevance >= relevance_threshold).group_by(DEF.signature).order_by(desc('count_')).all()[:val]
except NoSuchTableError:
x = [-1,]
But on executing this statement, I get a ProgrammingError
ProgrammingError: (ProgrammingError) (1146, "Table 'db.mno' doesn't exist")
Why does SQLAlchemy raise the more general ProgrammingError instead of the more specific NoSuchTableError? And if this is indeed expected behaviour, how do I ensure the app displays correct information depending on whether tables are present/absent?
EDIT1
Since this is part of my webapp, the model of DB is in models.py (under my pyramid webapp). I do have a setting in my .ini file that asks user to select whether additional tables are available or not. But not trusting the user, I want to be able to check for myself (in the views) whether table exists or not. The contentious table is something like (in models.py)
class MNO(Base):
__tablename__="mno"
id=Column(Integer,primary_key=True,autoincrement=True)
sid=Column(Integer)
cid=Column(mysql.MSInteger(unsigned=True))
affectability=Column(Integer)
cvss_base=Column(Float)
relevance=Column(Float)
__table_args__=(ForeignKeyConstraint(['sid','cid',],['def.sid','def.cid',]),UniqueConstraint('sid','cid'),)
How and Where should the check be made so that a variable can be set (preferably during app setup) which tells me whether the tables are present or not?
Note: In this case I would have to try if...else rather than 'ask for forgiveness'
According to the sqlalchemy docs, a NoSuchTableError is only thrown when "SQLAlchemy [is] asked to load a table's definition from the database, but the table doesn't exist." You could try loading a table's definition, catching the error there, and doing your query otherwise.
If you want to do things via "asking for forgiveness":
try:
table = Table(table_name, MetaData(engine))
except NoSuchTableError:
pass
Alternatively, you could just check whether the table exists:
Edit:
Better yet, why don't you use the has_table method:
if engine.dialect.has_table(connection, table_name):
#do your crazy query
Why don't you use Inspector to grab the table names first?
Maybe something like this:
from sqlalchemy import create_engine
from sqlalchemy.engine import reflection
#whatever code you already have
engine = create_engine('...')
insp = reflection.Inspector.from_engine(engine)
table_name = 'foo'
table_names = insp.get_table_names()
if table_name in table_names:
x = session.query(ABC.name.label('sig_name'),func.count('*').label('count_')).join(DEF).join(MNO).filter(MNO.relevance >= relevance_threshold).group_by(DEF.signature).order_by(desc('count_')).all()[:val]