Django ORM 完全指南:模型与查询
Django ORM(Object-Relational Mapping)是 Django 框架的核心功能之一,它允许开发者使用 Python 对象操作数据库,无需编写原生 SQL 语句。本文全面讲解 Django ORM 的使用方法。
字段类型一览
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
bio = models.TextField(blank=True)
birth_date = models.DateField(null=True)
website = models.URLField(blank=True)
is_active = models.BooleanField(default=True)
score = models.IntegerField(default=0)
balance = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
avatar = models.ImageField(upload_to="avatars/", null=True)
slug = models.SlugField(unique=True)
关系字段
class Article(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="articles")
# ForeignKey: 一对多,Author 删除时 Article 也删除
# related_name 让反向查询: author.articles.all()
category = models.ForeignKey("Category", on_delete=models.SET_NULL, null=True)
# SET_NULL: Category 删除时,Article.category 设为 NULL
tags = models.ManyToManyField("Tag", related_name="articles", blank=True)
# ManyToManyField: 多对多关系
class Meta:
ordering = ["-created_at"]
indexes = [
models.Index(fields=["-created_at"]),
models.Index(fields=["author", "-created_at"]),
]
基本 CRUD 操作
# 创建
author = Author.objects.create(name="张三", email="zhangsan@example.com")
article = Article(title="Django教程", content="内容...", author=author)
article.save()
读取(QuerySet)
all_authors = Author.objects.all()
author = Author.objects.get(id=1) # 不存在抛异常
first_three = Author.objects.all()[:3]
active = Author.objects.filter(is_active=True)
active_count = Author.objects.filter(is_active=True).count()
更新
author.name = "李四"
author.save()
批量更新(更高效)
Author.objects.filter(is_active=False).update(is_active=True)
删除
article.delete()
Author.objects.filter(is_active=False).delete()高级查询方法
from django.db.models import Q, F, Count, Sum, Avg, Max, Min
链式过滤
articles = Article.objects.filter(
author__name="张三",
created_at__year=2024,
views__gte=100,
).exclude(category__name="未分类").order_by("-created_at")
Q 对象(OR 查询)
results = Article.objects.filter(
Q(title__contains="Django") | Q(title__contains="Python"),
Q(views__gte=100) & ~Q(status="draft")
)
F 表达式(字段间比较)
Article.objects.all().update(views=F("views") * 2)
聚合查询
stats = Article.objects.aggregate(
total_views=Sum("views"),
avg_views=Avg("views"),
max_views=Max("views"),
article_count=Count("id")
)
stats = {total_views: 50000, avg_views: 2500, max_views: 10000, article_count: 20}
分组查询(annotate)
author_stats = Author.objects.annotate(
article_count=Count("articles"),
total_views=Sum("articles__views")
).order_by("-article_count")性能优化:预加载
# select_related: 一对多/一对一,SQL JOIN
articles = Article.objects.select_related("author", "category").all()
for article in articles:
# 不再产生额外查询
print(article.author.name, article.category.name)
prefetch_related: 多对多/反向 ForeignKey
articles = Article.objects.prefetch_related("tags").all()
for article in articles:
for tag in article.tags.all():
print(tag.name)常见问题
- Q: get() 和 filter().first() 有什么区别?
A:get() 只能返回一条记录,多条或零条都会抛异常;filter().first() 返回第一条或 None,更安全。 - Q: 如何执行原生 SQL?
A:使用 Model.objects.raw() 或 connection.cursor()。但应优先使用 ORM,仅在复杂查询无法用 ORM 表达时才使用原生 SQL。 - Q: objects.all() 和 objects.filter() 有性能差异吗?
A:在数据库层面相同,filter() 只是加了一个 WHERE 1=1 的条件。 - Q: 如何处理数据库事务?
A:使用 transaction.atomic() 装饰器或上下文管理器:from django.db import transaction@transaction.atomic
def transfer_money(from_id, to_id, amount):
with transaction.atomic():
pass - Django 模型字段参考
- Django 数据库性能优化:N+1 查询问题分析与解决