Expression Syntax
Expression syntax lets you write permission checks inline without defining rule functions.
What is Expression Syntax?
Instead of creating a rule function:
@rule
def is_author(user, post):
return post.author == user
@guard('is_author', model=Post)
def edit_post(request, pk):
...
You can write the check directly:
@guard('obj.author == user', model=Post)
def edit_post(request, pk):
...
When to Use Expressions
Use expressions when:
- The check is simple (one or two conditions)
- The rule is used in only one place
- You want quick prototyping
Use named rules when:
- The logic is complex
- The same check is used in multiple views
- You want to test rules in isolation
Attribute Access
Object Attributes
Access object fields with obj.:
@guard('obj.status == "published"', model=Post)
def view_post(request, pk):
...
@guard('obj.is_active', model=User)
def view_profile(request, user_id):
...
Nested Attributes
Chain attributes with dots:
# obj.author.name
@guard('obj.author.name == "admin"', model=Post)
# obj.category.parent.slug
@guard('obj.category.parent.slug == "tech"', model=Article)
User Attributes
Access user fields with user.:
@guard('user.is_staff')
def admin_view(request):
...
@guard('user.profile.is_verified')
def verified_only(request):
...
The user Reference
Use user alone to reference the entire user object:
@guard('obj.author == user', model=Post)
def edit_post(request, pk):
...
@guard('obj.owner == user', model=Document)
def view_document(request, pk):
...
Comparison Operators
| Operator | Description | Example |
|---|---|---|
== |
Equal | obj.status == "active" |
!= |
Not equal | obj.status != "deleted" |
> |
Greater than | obj.priority > 5 |
< |
Less than | obj.count < 100 |
>= |
Greater or equal | obj.age >= 18 |
<= |
Less or equal | obj.price <= 1000 |
Examples
# String comparison
@guard('obj.status == "published"', model=Post)
# Number comparison
@guard('obj.view_count > 1000', model=Article)
# User field comparison
@guard('obj.author.id == user.id', model=Post)
Boolean Operators
and
Both conditions must be true:
@guard('user.is_authenticated and user.is_active')
def dashboard(request):
...
@guard('obj.author == user and obj.status == "draft"', model=Post)
def edit_draft(request, pk):
...
or
At least one condition must be true:
@guard('user.is_staff or user.is_superuser')
def admin_area(request):
...
@guard('obj.author == user or obj.is_public', model=Document)
def view_document(request, pk):
...
not
Negates a condition:
@guard('not user.is_banned')
def post_comment(request):
...
@guard('user.is_staff and not obj.is_locked', model=Post)
def edit_post(request, pk):
...
Operator Precedence
Precedence from highest to lowest:
notandor
This means a or b and c is evaluated as a or (b and c).
Use parentheses to change precedence:
# Without parentheses: is_admin or (is_author and is_active)
@guard('is_admin or is_author and is_active')
# With parentheses: (is_admin or is_author) and is_active
@guard('(is_admin or is_author) and is_active')
List Membership with in
Check if a value is in a list:
@guard('obj.status in ["draft", "review"]', model=Post)
def edit_pending(request, pk):
...
@guard('obj.priority in [1, 2, 3]', model=Task)
def handle_urgent(request, pk):
...
@guard('user.role in ["admin", "moderator", "editor"]')
def manage_content(request):
...
Empty Lists
# Always false - nothing is in an empty list
@guard('obj.status in []', model=Post)
Null Comparison
Check for null/None values:
@guard('obj.deleted_at == null', model=Post)
def view_post(request, pk):
...
@guard('obj.parent != null', model=Category)
def view_subcategory(request, pk):
...
You can use null or None:
# These are equivalent
@guard('obj.deleted_at == null', model=Post)
@guard('obj.deleted_at == None', model=Post)
Literal Values
Strings
Use double quotes:
@guard('obj.status == "published"', model=Post)
@guard('obj.type == "premium"', model=Account)
Numbers
Integers and floats:
@guard('obj.count > 100', model=Post)
@guard('obj.price <= 99.99', model=Product)
@guard('obj.discount >= 0.5', model=Coupon)
Negative numbers:
@guard('obj.balance > -100', model=Account)
Booleans
Use true/false or True/False:
@guard('obj.is_active == true', model=User)
@guard('obj.is_featured == True', model=Post)
@guard('user.is_verified == false')
Mixing Rules and Expressions
You can use named rules inside expressions:
@rule
def is_admin(user, obj=None):
return user.is_superuser
# Mix rule with expression
@guard('is_admin or obj.author == user', model=Post)
def edit_post(request, pk):
...
Complex Examples
Blog Post Access
# View: published or author
@guard('obj.status == "published" or obj.author == user', model=Post)
# Edit: author and not locked
@guard('obj.author == user and not obj.is_locked', model=Post)
# Delete: admin or (author and draft)
@guard('user.is_superuser or (obj.author == user and obj.status == "draft")', model=Post)
E-commerce
# View order: owner or staff
@guard('obj.customer == user or user.is_staff', model=Order)
# Cancel order: owner and pending
@guard('obj.customer == user and obj.status in ["pending", "processing"]', model=Order)
# Refund: staff and not already refunded
@guard('user.is_staff and obj.refund_status == null', model=Order)
Team Collaboration
# View project: member or public
@guard('obj.is_public or user in obj.members', model=Project)
# Edit project: owner or admin role
@guard('obj.owner == user or user.role == "admin"', model=Project)
Error Messages
When an expression has a syntax error, Django Shield shows the error location:
@guard('obj.status == == "draft"', model=Post) # Syntax error
ExpressionSyntaxError: Unexpected token '=='
obj.status == == "draft"
^
When evaluation fails:
@guard('nonexistent_rule', model=Post)
ExpressionEvaluationError: Rule not found: Rule 'nonexistent_rule' is not registered