Django Field Lookups Reference
Query database with powerful lookup expressions
🔍 What are Field Lookups?
Field lookups are special keywords used in Django ORM queries to filter database records. They follow the field__lookup syntax and enable complex queries like contains, greater than, or case-insensitive matching without writing raw SQL.
# Basic field lookup examples
User.objects.filter(age__gte=18) # Age >= 18
Post.objects.filter(title__icontains='django') # Case-insensitive
Product.objects.filter(price__lt=100) # Price < 100
Lookup Categories
Exact Match
Exact and case-insensitive matching
filter(name__exact='John')
filter(email__iexact='[email protected]')
Comparisons
Greater than, less than operations
filter(age__gt=18)
filter(price__lte=50)
String Patterns
Contains, starts with, ends with
filter(title__contains='Django')
filter(name__startswith='A')
Date Lookups
Date and time filtering
filter(created__year=2024)
filter(date__month=1)
🔹 Exact Match Lookups
Exact match lookups find records where field values match exactly or case-insensitively. The default lookup is 'exact' when no lookup is specified.
# models.py
from django.db import models
class User(models.Model):
username = models.CharField(max_length=100)
email = models.EmailField()
is_active = models.BooleanField(default=True)
# Exact match (case-sensitive)
User.objects.filter(username__exact='john')
User.objects.filter(username='john') # Same as above
# Case-insensitive exact match
User.objects.filter(email__iexact='[email protected]')
# Matches: [email protected], [email protected], [email protected]
# Boolean exact match
User.objects.filter(is_active__exact=True)
User.objects.filter(is_active=True) # Same as above
When to Use:
- exact - Case-sensitive exact match (default)
- iexact - Case-insensitive exact match (emails, usernames)
🔹 Comparison Lookups
Comparison lookups filter records using mathematical comparisons like greater than, less than, and range checks for numeric and date fields.
# models.py
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField()
# Greater than
Product.objects.filter(price__gt=50) # price > 50
# Greater than or equal
Product.objects.filter(stock__gte=10) # stock >= 10
# Less than
Product.objects.filter(price__lt=100) # price < 100
# Less than or equal
Product.objects.filter(stock__lte=5) # stock <= 5
# Range (inclusive)
Product.objects.filter(price__range=(20, 50)) # 20 <= price <= 50
# Combining comparisons
Product.objects.filter(price__gte=10, price__lte=100)
# Same as: Product.objects.filter(price__range=(10, 100))
Example Query Results:
price__gt=50 → Products with price > $50
stock__lte=5 → Products with 5 or fewer items
price__range=(20, 50) → Products between $20-$50
🔹 String Pattern Lookups
String pattern lookups search for partial matches in text fields using contains, starts with, ends with, and their case-insensitive variants.
# models.py
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.CharField(max_length=100)
# Contains (case-sensitive)
Article.objects.filter(title__contains='Django')
# Matches: "Django Tutorial", "Learning Django"
# Case-insensitive contains
Article.objects.filter(title__icontains='python')
# Matches: "Python", "PYTHON", "python basics"
# Starts with
Article.objects.filter(author__startswith='John')
# Matches: "John Smith", "Johnny"
# Case-insensitive starts with
Article.objects.filter(title__istartswith='how')
# Matches: "How to...", "HOW TO...", "how to..."
# Ends with
Article.objects.filter(title__endswith='Guide')
# Matches: "Beginner's Guide", "Complete Guide"
# Case-insensitive ends with
Article.objects.filter(title__iendswith='tutorial')
# Matches: "Django Tutorial", "PYTHON TUTORIAL"
Pattern Lookup Types:
- contains / icontains - Substring anywhere in text
- startswith / istartswith - Text begins with pattern
- endswith / iendswith - Text ends with pattern
- Prefix with 'i' for case-insensitive matching
🔹 Date and Time Lookups
Date lookups extract specific parts of datetime fields like year, month, day, hour for filtering records by time periods and components.
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
published_date = models.DateField()
# Year lookup
Post.objects.filter(created_at__year=2024)
# Month lookup (1-12)
Post.objects.filter(created_at__month=1) # January
# Day lookup
Post.objects.filter(created_at__day=15)
# Week day (1=Sunday, 7=Saturday)
Post.objects.filter(created_at__week_day=2) # Monday
# Date exact
Post.objects.filter(published_date__date='2024-01-15')
# Time lookups
Post.objects.filter(created_at__hour=14) # 2 PM
Post.objects.filter(created_at__minute=30)
Post.objects.filter(created_at__second=0)
# Combining date lookups
Post.objects.filter(
created_at__year=2024,
created_at__month=1,
created_at__day__gte=15
)
# Practical examples
# Posts from this year
Post.objects.filter(created_at__year=2024)
# Posts from January
Post.objects.filter(created_at__month=1)
# Posts created on Mondays
Post.objects.filter(created_at__week_day=2)
# Posts from Q1 2024
Post.objects.filter(
created_at__year=2024,
created_at__month__lte=3
)
🔹 Null and Boolean Lookups
Special lookups for checking null values and boolean fields. Use isnull to find empty fields and direct comparison for boolean values.
# models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(null=True, blank=True)
avatar = models.ImageField(null=True, blank=True)
is_verified = models.BooleanField(default=False)
# Check for null values
Profile.objects.filter(bio__isnull=True) # No bio
Profile.objects.filter(bio__isnull=False) # Has bio
# Check for empty or null
Profile.objects.filter(avatar__isnull=True) # No avatar
# Boolean lookups
Profile.objects.filter(is_verified=True) # Verified users
Profile.objects.filter(is_verified=False) # Unverified users
# Combining null checks
Profile.objects.filter(
bio__isnull=False,
is_verified=True
) # Verified users with bio
Important Notes:
- Use isnull=True to find NULL database values
- Empty strings ('') are NOT null
- For boolean fields, use direct comparison: field=True
🔹 In and Range Lookups
In lookup checks if field value exists in a list. Range lookup filters values between two boundaries inclusively for efficient multi-value queries.
# models.py
class Order(models.Model):
status = models.CharField(max_length=20)
total = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.IntegerField()
# In lookup - check if value in list
Order.objects.filter(status__in=['pending', 'processing', 'shipped'])
# In with numbers
Order.objects.filter(quantity__in=[1, 5, 10, 20])
# Range lookup (inclusive)
Order.objects.filter(total__range=(50, 200)) # 50 <= total <= 200
# Date range
from datetime import date
start_date = date(2024, 1, 1)
end_date = date(2024, 12, 31)
Order.objects.filter(created_at__range=(start_date, end_date))
# Exclude with in
Order.objects.exclude(status__in=['cancelled', 'refunded'])
# Practical examples
# Active orders
Order.objects.filter(status__in=['pending', 'processing'])
# Medium-sized orders
Order.objects.filter(quantity__range=(5, 20))
# Orders from Q1
Order.objects.filter(
created_at__range=('2024-01-01', '2024-03-31')
)
🔹 Regex Lookups
Regular expression lookups enable complex pattern matching using regex syntax. Useful for advanced text searches and validation in database queries.
# models.py
class Customer(models.Model):
name = models.CharField(max_length=100)
phone = models.CharField(max_length=20)
email = models.EmailField()
# Regex (case-sensitive)
Customer.objects.filter(phone__regex=r'^\d{3}-\d{3}-\d{4}$')
# Matches: 123-456-7890
# Case-insensitive regex
Customer.objects.filter(email__iregex=r'^[a-z]+@gmail\.com$')
# Matches: [email protected], [email protected]
# Find names with numbers
Customer.objects.filter(name__regex=r'\d')
# Find emails from specific domains
Customer.objects.filter(email__iregex=r'@(gmail|yahoo|hotmail)\.com$')
# Phone numbers starting with area code
Customer.objects.filter(phone__regex=r'^(555|666)')
# Names with special characters
Customer.objects.filter(name__regex=r'[^a-zA-Z\s]')
Common Regex Patterns:
- ^\d{3}$ - Exactly 3 digits
- ^[A-Z] - Starts with uppercase letter
- \d+ - One or more digits
- [a-z]+@ - Lowercase letters before @
🔹 Related Field Lookups
Span relationships using double underscores to query related models. Access foreign key and many-to-many fields through relationship chains.
# models.py
class Author(models.Model):
name = models.CharField(max_length=100)
country = models.CharField(max_length=50)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
published_date = models.DateField()
price = models.DecimalField(max_digits=10, decimal_places=2)
# Query through foreign key
Book.objects.filter(author__name='John Doe')
# Multiple levels deep
Book.objects.filter(author__country='USA')
# Combining related lookups
Book.objects.filter(
author__name__icontains='smith',
price__lt=30
)
# Related field with date lookup
Book.objects.filter(
author__name='Jane Doe',
published_date__year=2024
)
# Reverse relationship
Author.objects.filter(book__title__icontains='Django')
Author.objects.filter(book__price__gte=50)
# Complex related queries
# Books by US authors published in 2024
Book.objects.filter(
author__country='USA',
published_date__year=2024
)
# Authors with expensive books
Author.objects.filter(book__price__gte=100).distinct()
# Books by authors whose name starts with 'J'
Book.objects.filter(author__name__istartswith='j')
🔹 Combining Lookups with Q Objects
Q objects enable complex queries with OR logic and negation. Combine multiple conditions using & (AND), | (OR), and ~ (NOT) operators.
from django.db.models import Q
# OR queries
Product.objects.filter(
Q(name__icontains='phone') | Q(name__icontains='tablet')
)
# AND with OR
Product.objects.filter(
Q(price__lt=100) & (Q(category='Electronics') | Q(category='Gadgets'))
)
# NOT queries
Product.objects.filter(~Q(status='discontinued'))
# Complex combinations
Product.objects.filter(
Q(price__gte=50) & Q(price__lte=200) |
Q(on_sale=True)
)
# Nested Q objects
Product.objects.filter(
Q(name__icontains='laptop') &
(Q(brand='Dell') | Q(brand='HP')) &
Q(price__lt=1000)
)
# Practical examples
# Search products
search_term = 'phone'
Product.objects.filter(
Q(name__icontains=search_term) |
Q(description__icontains=search_term)
)
# Active users from specific countries
User.objects.filter(
Q(is_active=True) &
Q(country__in=['USA', 'Canada', 'UK'])
)
🔹 Field Lookup Summary
Lookup Reference:
- exact / iexact - Exact match (case-sensitive/insensitive)
- contains / icontains - Substring match
- startswith / istartswith - Starts with pattern
- endswith / iendswith - Ends with pattern
- gt / gte / lt / lte - Comparison operators
- in - Value in list
- range - Between two values (inclusive)
- isnull - Check for NULL values
- regex / iregex - Regular expression match
- year / month / day - Date component extraction