Relationships — Aquilia Documentation
Comprehensive guide and documentation for Relationships in the Aquilia framework. View API reference, examples, and implementation patterns.
Docs / Models / Defining Relationships Defining Relationships Aquilia ORM supports first-class relational fields including Many-to-One, One-to-One, and Many-to-Many configurations. ForeignKey (Many-to-One) Declares a many-to-one relationship. Requires the target model (either class reference or forward reference string) and on_delete behavior: from aquilia.models.fields_module import ForeignKey class Post(Model): # Class reference author = ForeignKey(User, on_delete="CASCADE", related_name="posts") # Or string reference (prevents circular imports) category = ForeignKey("Category", on_delete="SET_NULL", null=True) On-Delete Actions Supported database-level delete cascades: "CASCADE": Cascades the deletion of the referenced row to this row. "SET_NULL": Sets the foreign key column to NULL (requires null=True). "RESTRICT": Rejects parent deletion if dependent children rows exist. "SET_DEFAULT": Sets the column to its configured default value. "DO_NOTHING": No database-level action is taken (raw foreign key remains unchanged). OneToOneField Similar to ForeignKey, but enforces a UNIQUE constraint on the foreign key column, establishing a strict 1-to-1 link: from aquilia.models.fields_module import OneToOneField class Profile(Model): user = OneToOneField(User, on_delete="CASCADE", related_name="profile") ManyToManyField Configures a many-to-many relationship. Automatically generates an intermediary junction table: from aquilia.models.fields_module import ManyToManyField class Article(Model): tags = ManyToManyField("Tag", related_name="articles") GenericForeignKey A polymorphic relation to any registered model — Django's "virtual field" pattern. Unlike ForeignKey, it doesn't own a database column of its own: you declare two real columns yourself (a model-label column and a stringified-PK column), and GenericForeignKey resolves between them. from aquilia.models import Model, AutoField, CharField, GenericForeignKey class Comment(Model): id = AutoField(primary_key=True) body = CharField(max_length=1000) content_type = CharField(max_length=255) # e.g. "User", "Post", "Ticket" object_id = CharField(max_length=255) # stringified PK -- works for int or UUID PKs target = GenericForeignKey("content_type", "object_id") post = await Post.get(pk=1) comment = Comment(body="Nice post!") Comment.target.attach(comment, post) # sets content_type="Post", object_id=str(post.pk) await comment.save() # ... later, after loading a row back from the DB: reloaded = await Comment.get(pk=comment.pk) target = await Comment.target.resolve(reloaded) # -> the Post instance, or None Why an explicit async method, not a transparent attribute: Aquilia is async-native — there's no way to do a lazy synchronous DB fetch on plain attribute access the way Django's sync ORM can. Resolution is always await field.resolve(instance). Why no ContentType model: Django's GenericForeignKey looks up a content_type_id against a database-backed ContentType table. Aquilia reuses the already-existing, in-memory ModelRegistry.get(label) lookup — the same primitive ForeignKey already uses for string-based relation resolution — so no extra table, migration, or registry sync step is needed. Not a Field subclass — the metaclass's column-collection scan skips it entirely, so it owns no schema column and doesn't appear in generated CREATE TABLE DDL. An unset target resolves to None, not an error. QuerySet API Hydration Primitives )
Go to Homepage