Coming from Django¶
hare's models, querysets and migrations have the shape Django's have, so most of what a Django developer knows carries over. This page lists what is named differently, what behaves the same, and where hare gives another result. The rest of the documentation describes hare on its own.
What is named differently¶
| Django | hare |
|---|---|
from django.db import models, models.Model |
from hare import fields, from hare.models import Model |
models.ForeignKey, models.OneToOneField, models.ManyToManyField |
fields.ForeignKeyField, fields.OneToOneField, fields.ManyToManyField — see Relations |
GenericForeignKey of contenttypes |
fields.GenericForeignKeyField |
Meta.db_table, Meta.app_label, Meta.db_table_comment |
Meta.table, Meta.app, Meta.table_description — see Meta options |
Meta.unique_together |
UniqueConstraint(fields=(...)) in Meta.constraints |
settings.DATABASES, DATABASE_ROUTERS |
the connections and routers of Hare.init() |
transaction.atomic(), transaction.on_commit() |
Transactions.atomic(), Transactions.on_commit() — see Transactions |
ATOMIC_REQUESTS |
atomic_requests of the web framework integration — see Web frameworks |
manage.py makemigrations, migrate, sqlmigrate, squashmigrations, inspectdb |
hare makemigrations, hare migrate, hare sqlmigrate, hare squashmigrations, hare inspectdb — see The hare command |
django.utils.timezone.override() |
Timezone.override() (from hare.time import Timezone) |
Every query and every write is a coroutine: await Book.objects.filter(...), async for book in ...,
await book.save(). on_delete defaults to CASCADE.
What behaves the same¶
- A router chain: the first router returning an alias wins, and a read that decides a write
(
get_or_create(),update_or_create(),select_for_update()) runs on the write connection. on_commit()called after the transaction ended runs the callback at once.- A field path may end on a relation or on
pk;relation=Noneon a to-many relation isrelation__isnull=True; a lookup on a relation's own name compares its key. - The implicit
GROUP BYofvalues().annotate(), and an aggregate annotated beforevalues()being computed per model row. update_fields=["author"]writesauthor_id; aForeignKeyFieldindexes its key column;add(),remove()andset()of a many-to-many relation take instances or key values.Index(fields=("-created_at",))is a descending key; a constraint condition is aQ.- A sliced queryset can't be filtered, reordered or made
distinct(); it can be aggregated. distinct()ordered by a column it doesn't select selects that column too, so a selected value can come several times — seevalues()andvalues_list().
Where hare gives another result¶
exclude()across a to-many relation. The conditions of oneexclude()call refer to the same related row, as infilter():exclude(books__name="a", books__rating=5)keeps an author with one book named "a" and another rated 5. Django drops that author too; in hare that takes two calls —.exclude(books__name="a").exclude(books__rating=5). Seeexclude().filter()andannotate()in any order.annotate(n=Count("books")).filter(books__price__gt=6)reuses the aggregate'sJOINand counts only the matching books — the same asfilter(...).annotate(...). In Django the order changes the count. See Aggregates and to-many filters.- Aggregates over two to-many relations. A sum or count that a second to-many
JOINwould inflate raisesQueryErrorinstead of returning the inflated number Django returns. get()on aunion(). It takes conditions and applies them to every branch; Django refuses them.