NoORM

PyWeb-IL #75

Yep, a clickbait title

Since ORMs are perfectly fine for 95.523% of use cases.

(Yep, that's the accurate stat).

(Not made up, I swear :-P)

Not Always ORM

or

Not Always Just ORM

are better titles for this talk.

ORMs/ODMs Are Great For Most Cases

Abstract different DB Dialects

Some also treats save and updates the same way.

Ease M2M relations.

Stay in your Language's Domain


            SELECT b.name FROM books b
              JOIN authors a ON b.author_id = a.id
              WHERE b.title = 'NoORM'
            

Vs.


            book = Book.objects.select_related().filter(title='NoORM')
            # book.author
            

Self Documenting Structure


            class Book(Base):

              __tablename__ = 'books'

              id = Column(Integer, primary_key=True)
              title = Column(String)
              isbn = Column(String(13))
              author = relationship("Author", backref="book")
            

Provides a "contract" for developers.

IDEs can auto-complete.

No need to consult db structure.

Application Level Validation


              class Article(models.Model):
                  ...
                  def clean(self):
                      # Don't allow draft entries to have a pub_date.
                      if self.status == 'draft' and self.pub_date:
                          raise ValidationError({
                            'pub_date': _('Draft entries may not have a publication date.')
                          })
                      ...
            

Business Logic


              class Article(models.Model):
                  ...
                  def publish(self):
                    self.status = 'published'
                    self.pub_date = datetime.now()
                    ...
            

Query Building and Escaping

Chaining (vs. hacks to build the SQL statement)


            qs = Article.objects.all()

            if start:
              qs = qs.filter(pub_date__gte=start)

            if search:
              qs = qs.filter(title__icontains=search)

            if sort_by in ('title', 'pub_date'):
              qs = qs.order_by(sort_by)
            ...
            

This is something Non-Relational DBs got right.

Usually one provides a dict, which is easy to construct.

Migration

Schema & Data

But

What if you fall in the other 4.477% ?

Problematic Performance Wise

  • Complex code running in background.
  • Greedy by default (all columns).
  • Does not JOIN by default, more queries on attribute access.

Although
Diesel was faster than pure rust-postgres.

Leaky abstraction

ORMs try to map in memory data structures to relational database.

That's a hard problem.

The Vietnam of Computer Science.

Multi Apps and Languages

  • No single source of truth.
  • App level validation and defaults.
  • Even if same language, some may be async, can't re-use.
  • Data in a complex app may comes from multiple sources, while needing the same business logic.

DB specific features

  • PostgreSQL table inheritance.
  • PostgreSQL 10 IDENTITY column.
  • Faster batch insert - COPY FROM.

Translating from DB lingo to ORM

You finally got the complex CTE query right.

Translating to ORM is not always easy.

So, what can we do?

You've outgrown the use cases which ORM fits perfectly:

  • No silver bullet.
  • Awareness to ORM pitfalls.
  • Try to mix and match according to each project needs.
  • Drop to raw SQL when needed.

Separate in-memory from DB

While providing de/serialization and validation:

Bonus: easier unit testing without DB mocks.

Separate in-memory from DB

  • Thus utilize sync/async db access as needed.
  • But, manually keep DB and memory schema in sync.
Yup we're screwed

Thank you

Meir Kriheli / @mkriheli