Django has tight integration between relational databases, HTML, and REST APIs. By defining one model, you get a web interface to modify it, a database interface to query or modify it, and HTML interfaces (forms) to query/modify it. This is fantastic for rapid development.
So, if you're in that tightly-coupled ecosystem and legitimately making use of it, then bear those pros in mind.
I want to talk about when you're not using Django's Admin interface, and HTML forms and templates. This is much more likely for any large application.
Related Article: https://calpaterson.com/activerecord.html
Django's ORM follows
I want to talk about Django's ORM and my explain my thoughts on why I think it should be avoided.
Django goes out of its way to hide SQL, database principles and Python's standard interface to databases. This means less transferable knowledge is learnt, and actual understanding about databases is harder to come by.
SQLAlchemy maps much more directly onto
Let's do a simple join:
It's not obvious at all that a join is happening.
SELECT book.*, count(chapter.id) FROM book JOIN chapter using (book_id);
From anywhere you can call the database:
That is actually pretty bad, but further this demonstrates that the database connection, cursor, session, transaction are all hidden away.
with Session(engine) as session: session.add(book) session.commit()
Further reading: https://docs.sqlalchemy.org/en/14/orm/session_transaction.html?highlight=savepoint#session-level-vs-engine-level-transaction-control
By default, Django uses autocommit where every statement is immediately committed. This is against the PEP 249 standard and usually not what you want.
Complex applications often perform complex queries and mutations that must be kept consistent, and thus
To use a transaction:
with transaction.atomic(durable=True): obj1.save() obj2.save()
with session.begin(): session.add(obj1) session.add(obj2)
Further reading: https://docs.sqlalchemy.org/en/14/changelog/migration_20.html#library-level-but-not-driver-level-autocommit-removed-from-both-core-and-orm
- Model fields can define
blankwhich is a client-side validation.
- Model fields can define
verbose_namewhich is for displaying to users.
There are a lot of rules on when a query is actually executed. I'll grant they're pretty straightforward, but there are edge cases to ensure multiple queries are not executed by accident.
results = Book.objects.all() if len(results) > 2: print(results)
In comparison, with SQLAlchemy there is an explicit execute:
results = session.execute(select(Book))
SQLAlchemy has strong support for niche features in comparison to Django.
Though in Django 3.2, they finally added
transaction(durable=True), the whole transaction system is opaque.
Django forces you to define your models in the root of an "app", while with a good code structure the database models will be hidden away.