How To’s

Joins

Use a join function in your Query. Example:

(Query(Contact)
    .select(Contact.id, Person.name)
    .left_join(Person, on=[Person.contact_id, Contact.id])
    .execute())

Currently, the supported joins are:

  • inner_join
  • left_join
  • left_outer_join
  • right_join
  • right_outer_join
  • full_join
  • full_outer_join

Validation

Estoult by default only validates the type of a field. You can easily extend this by adding a classmethod called validate to the schema which receives the arguments is_updating (whether or not it’s called on an update or insert), and row (the fields that are given, once they have been casted), or whatever else you want to call them.

from estoult import FieldError

class Person(db.Schema):
    __tablename__ = "people"

    id = Field(str)
    name = Field(str)
    dob = Field(str)
    country = Field(str)

    @classmethod
    def validate(cls, is_updating, row):
        if row["country"] == "France":
            # FieldErrors are generally what you want to raise
            # so they can be easy to catch on insert/update
            raise FieldError("No French allowed")

Query operators

Common query operators can be used as overloaded python operators in every Field/Clause. These are:

Operator Meaning
== x equals y
< x is less than y
<= x is less than or equal to y
> x is greater than y
>= x is greater than or equal to y
!= x is not equal to y
+ x plus y
- x minus y
* x multiplied by y
/ x divided by y
% x modulo y
>>> from estoult import Query, op
>>> query = Query(Meal).select()
>>> query.where(Meal.name != "Pizza")
>>> query.where(Meal.calories > 400)
>>> query.where(Meal.calories == (Meal.kilojules * 4.184))

Other operators are available as methods from the op module.:

Function Example
op.or_ .where(op.or_(Person.id == 1, Person.id == 2))
op.and_ .where(op.and_(Person.id == 1, Person.id == 2))
op.in_ .where(op.in_(Person.id, [1, 2, 3, 4]))
op.like .where(op.like(Person.name, "Astol%"))
op.ilike .where(op.ilike(Person.name, "%StOL%"))
op.not .where(op.not_(Person.name == "Name"))
op.is_null .where(op.is_null(Person.dob))
op.not_null .where(op.not_null(Person.dob))
>>> query = Query(Car).select()
# name = "Ferrari" OR engine = "GP2"
>>> query.where(op.or_(Car.name == "Ferrari", Car.engine == "GP2"))
# name like '%Renault%'
>>> query.where(op.like(Car.name, "%Renault%"))

Function operators

Function operators are imported with the fn module.

Function Example
fn.count .select(fn.count(Person.id))
fn.sum .select(fn.sum(Person.weight))
fn.avg .select(fn.avg(Person.age))
fn.ceil .where(fn.ceil(Person.height) == 180)
fn.distinct .select(fn.distinct(Person.email))
fn.concat .where(fn.concat(Person.first_name, "' '", Person.last_name) == "Carlos Sainz")
fn.alias .select(fn.alias(fn.sum(Person.weight), "weight"))
fn.cast .select(fn.cast(Person.dob, "datetime"))

Adding Ops/Fns

Estoult comes with the most important and commonly used functions/operators for SQL. However, Estoult is not an ORM and is inherently hackable which means you can easily add additional functionality if you need.

If you wanted to add the <-> operator from PostgreSQL’s pg_trgm extension, you would use the add_op from op anywhere Estoult is always imported from (most likely where your database object is).

from estoult import PostgreSQLDatabase, op

db = PostgreSQLDatabase(...)

# Add the <-> operator here and call it "trgm"
op.add_op("trgm", "<->")

Now we can use it anywhere:

from estoult import Query, op

# select * from customers order by name <-> 'glgamish' limit 10;
print(Query(Customer).select()
    .order_by(op.trgm(cls.name, "glgamish"))
    .limit(10)
    .execute())

The same can be done for the fn module using add_fn.

Display generated query

You can print any un-executed Query to display the generated SQL.

>>> print(Query(Animal).select().where(Animal.name == "Red Panda"))
select * from animals where animals.name = "Red Panda"

To format the query parameters, Estoult uses the mogrify function for PostgreSQL and just runs it for the other sources. This means it will fail if there is a syntax error in the SQL. To see it unformatted you will need to use repr as well.