15

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.

calthoff
  • 386
  • 1
  • 4
  • 14

3 Answers3

43

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

Weston
  • 2,732
  • 1
  • 28
  • 34
coleifer
  • 24,887
  • 6
  • 60
  • 75
  • If I am using `db.atomic()` to "get or create" multiple related entries as part of a single transaction (i.e. **not** using the function `get_or_create()` for each entry), which in your opinion is better to use - `try/except`, or `exists()`? – ron_g Oct 22 '19 at 08:16
18

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
Weston
  • 2,732
  • 1
  • 28
  • 34
  • 2
    I was surprised by the downvotes, so I clarified my answer. My reading of the question suggested they wanted to do something with the object if it existed. Which was the question I had when I came across this question. – Weston Oct 21 '19 at 16:17
3

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)))
MarSoft
  • 3,555
  • 1
  • 33
  • 38
  • From where do you import `Clause` ? I cannot find it in the peewee source code. – Matthew Moisen Feb 18 '22 at 04:43
  • 1
    Notice this answer is more than 5 years old. Looks like `Clause` was removed or renamed since then. You may find this useful: https://github.com/coleifer/peewee/commit/2f0b3 - it is one of the latest commits mentioning `Clause` class as obsolete and proposing a replacement. – MarSoft Feb 19 '22 at 12:47
  • 1
    I stumbled upon the following which seems to work: `Parent.select().where(fn.EXISTS(subquery))`. It seems that Peewee is smart enough that you don't even need the `Param('1')` in your subquery. So `subquery = Child.select().where(Child.parent == Parent.id)` actually turns into a `select 1` when used in the exists for some reason. – Matthew Moisen Feb 20 '22 at 03:17