Home >Backend Development >Python Tutorial >Understanding Django ORM
Object-relational mapping(ORM) is a feature in Django that allows us to interact with databases using Python code without writing SQL queries. The ORM translates CRUD operations into SQL under the hood, enabling easy creation, retrieval, updating, and deletion of database objects.
In Django, a model class represents a database table, and an instance of that class represents a record in the table.
Every model has at least one Manager, which is called objects. We can retrieve records from the database through this manager, resulting in a QuerySet.
QuerySets are lazy, meaning that results aren't fetched until explicitly requested.
Common QuerySet methods
filter(): Retrieve records matching certain criteria.
all(): Retrieve all records.
order_by(): Order records based on specific fields.
distinct(): Return unique records.
annotate(): Add aggregate values to each record.
aggregate(): Calculate a value from a queryset.
defer(): Load only some fields of a model, deferring others.
Advanced ORM Features
Q and F Objects allow for complex queries and efficient database-level operations. We can use 'Q' for queries that involve OR conditions, while 'F' allows you to reference model fields directly in queries.
from django.db.models import Q, F # Using Q to filter published posts or created after a specific date posts = Post.objects.filter(Q(status='published') | Q(created_at__gte='2024-01-01')) # Using F to compare fields within a model (e.g., for a discount calculation) class Product(models.Model): name = models.CharField(max_length=100) price = models.DecimalField(max_digits=10, decimal_places=2) discounted_price = models.DecimalField(max_digits=10, decimal_places=2) # Retrieve products where discounted price is less than price discounted_products = Product.objects.filter(discounted_price__lt=F('price'))
Query Expressions (referencing model fields) and Database Functions (applying SQL-like functions) both allow us to perform operations at the database level instead of pulling data into Python for processing. This helps optimize queries and reduce database load.
from django.db.models import Count, Max # Count the number of posts for each status status_count = Post.objects.values('status').annotate(count=Count('id')) # Get the latest created post latest_post = Post.objects.aggregate(latest=Max('created_at'))
Custom managers let us add extra manager methods or modify the QuerySet the manager initially returns.
class PublishedManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(status='published') class Post(models.Model): title = models.CharField(max_length=255) content = models.TextField() status = models.CharField(max_length=50) created_at = models.DateTimeField(auto_now_add=True) objects = models.Manager() # Default manager published = PublishedManager() # Custom manager for published posts # Use the custom manager to get published posts published_posts = Post.published.all()
ContentType is a model which is useful for creating generic relationships between models without specifying them with direct foreign keys. Common use cases include comments or tags that need to be attached to different types of models.
from django.contrib.contenttypes.models import ContentType # Example model for comments class Comment(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') text = models.TextField() # Creating a comment for a Post instance post = Post.objects.get(id=1) comment = Comment.objects.create( content_type=ContentType.objects.get_for_model(Post), object_id=post.id, text='Great post!' )
Transactions bundle database operations as a single unit ensuring data consistency. We can use the @transaction.atomic decorator or the transaction.atomic() context manager to wrap code in a transaction block.
from django.db import transaction # Using a transaction block with transaction.atomic(): post = Post.objects.create(title='New Post', content='Content here...', status='published') # Any other database operations will be part of this transaction
Django allows executing raw SQL queries for complex query where you need flexibility. However, it should be used with caution.
from django.db import connection def get_published_posts(): with connection.cursor() as cursor: cursor.execute("SELECT * FROM blog_post WHERE status = %s", ['published']) rows = cursor.fetchall() return rows
Django's ORM simplifies database interactions by providing a high-level API for working with models, managers, and queries. Understanding and utilizing these features can greatly enhance your productivity and the performance of your applications.
The above is the detailed content of Understanding Django ORM. For more information, please follow other related articles on the PHP Chinese website!