QuerySet API — Aquilia Documentation
Comprehensive guide and documentation for QuerySet API in the Aquilia framework. View API reference, examples, and implementation patterns.
Docs / Models / QuerySet API QuerySet API Immutable, clone-on-write async query builder. Chains return new QuerySet clones; terminal methods (all, first, get, count) execute SQL. Obtaining a QuerySet Access the model objects manager to start a chain: # Fresh QuerySet clone qs = User.objects.filter(active=True) # QuerySet is immutable: every chain returns a new clone q1 = User.objects.filter(active=True) q2 = q1.filter(age__gt=18) # q1 is unaffected q3 = q2.order("-created_at") # q2 is unaffected Chain Methods Method Description : m} ))} Terminal Methods (async) Method Returns Description ))} Lookups Filter using Django-style double-underscore suffixes: # Exact & Case-insensitive exact User.objects.filter(name__exact="Alice") User.objects.filter(name__iexact="alice") # Contained text User.objects.filter(email__icontains="co.com") # Range & IN checks User.objects.filter(age__range=(18, 30)) User.objects.filter(id__in=[1, 2, 3]) # Null check User.objects.filter(active__isnull=False) Q Node Composition Combine conditions using Q nodes and logical operators & (AND), | (OR), and ~ (NOT): from aquilia.models import Q # (active=True AND role="admin") OR email ends with @co.com qs = User.objects.filter( (Q(active=True) & Q(role="admin")) | Q(email__endswith="@co.com") ) # NOT suspended qs = User.objects.filter(~Q(suspended=True)) Raw WHERE / HAVING Clauses .where() and .having() accept a raw SQL fragment for cases the filter/lookup API doesn't cover. Always bind user-supplied values through ? placeholders — never string-interpolate them into the clause: # Positional placeholders qs = User.objects.where("age > ?", 18) # Named placeholders qs = User.objects.where( "status = :status AND role = :role", status="active", role="admin", ) # HAVING (use after group_by) qs = ( Order.objects .group_by("customer_id") .having("COUNT(*) > ?", 5) ) Guardrail, not the defense: both methods reject clauses containing an unparameterized DROP/ALTER/TRUNCATE/EXEC/EXECUTE/ DELETE/INSERT/UPDATE/MERGE keyword, a comment marker (--, /* */), or a bare ; — word-boundary matched, so a column named updated_at is not a false positive. This is a secondary safety net, not the actual injection defense: parameter binding is. A clause built by string-interpolating user input can still be unsafe even if it doesn't happen to contain a blocked keyword. # Rejected — SecurityFault, contains an unparameterized DML keyword await User.objects.where("id = 1; DELETE FROM users") # Rejected — comment marker await User.objects.where("id = 1 -- bypass rest of clause") # Fine — the keyword only appears inside an identifier await User.objects.where("updated_at > ?", cutoff) F Expressions Reference columns directly in SQL comparison or updates via F : from aquilia.models import F # Compare field values await User.objects.filter(login_count__gt=F("post_count")).all() # Atomic database increments await Product.objects.filter(id=42).update(stock=F("stock") - 1) Custom QuerySets Extend QuerySet to reuse domain queries: from aquilia.models import QuerySet, Manager class ArticleQuerySet(QuerySet): def published(self): return self.filter(status="published") def recent(self): return self.order("-published_at") class Article(Model): table = "articles" # Attach to objects descriptor objects = Manager.from_queryset(ArticleQuerySet)() # Usage posts = await Article.objects.published().recent().all() Fields Relationships )
Go to Homepage