Django ORM 详细教程

Django ORM 是 Django 框架内置的对象关系映射工具,它提供了一种 Pythonic 的方式来与数据库交互,而无需直接编写 SQL。本教程将全面介绍 Django ORM 的使用方法。

目录

  1. 模型定义
  2. 模型字段
  3. 模型关系
  4. 迁移操作
  5. 基本CRUD
  6. 查询集(QuerySet)
  7. 高级查询
  8. 聚合与注解
  9. 事务管理
  10. 性能优化
  11. 原生SQL

1. 模型定义

创建模型

from django.db import models
class Author(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    birth_date = models.DateField()
    bio = models.TextField(blank=True)

    def __str__(self):
        return self.name
class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publish_date = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    is_published = models.BooleanField(default=True)

    def __str__(self):
        return self.title

元选项

class Book(models.Model):
    # 字段定义...

    class Meta:
        ordering = ['-publish_date']  # 默认排序
        verbose_name = '图书'         # 单数名称
        verbose_name_plural = '图书'  # 复数名称
        indexes = [
            models.Index(fields=['title'], name='title_idx'),
        ]
        unique_together = [['title', 'author']]  # 联合唯一

2. 模型字段

常用字段类型

字段类型 描述
CharField 字符串字段,必须指定 max_length
TextField 大文本字段
IntegerField 整数
DecimalField 十进制小数,需指定 max_digits 和 decimal_places
BooleanField 布尔值
DateField 日期
DateTimeField 日期时间
EmailField 带有 Email 验证的 CharField
URLField 带有 URL 验证的 CharField
FileField 文件上传字段
ImageField 继承 FileField,验证是否为有效图像

字段选项

models.CharField(
    max_length=100,
    null=True,          # 允许数据库NULL
    blank=True,         # 允许表单验证为空
    default='unknown',  # 默认值
    choices=[           # 可选值
        ('M', 'Male'),
        ('F', 'Female'),
        ('O', 'Other')
    ],
    unique=True,        # 唯一值
    db_index=True,      # 创建索引
    verbose_name='名称' # 可读名称
)

3. 模型关系

一对一关系

class Profile(models.Model):
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        primary_key=True
    )
    address = models.CharField(max_length=100)
    phone = models.CharField(max_length=20)

一对多关系

class Publisher(models.Model):
    name = models.CharField(max_length=100)
class Book(models.Model):
    publisher = models.ForeignKey(
        Publisher,
        on_delete=models.CASCADE,
        related_name='books'
    )

多对多关系

class Student(models.Model):
    name = models.CharField(max_length=100)
    courses = models.ManyToManyField(
        'Course',
        through='Enrollment',
        through_fields=('student', 'course')
    )
class Course(models.Model):
    title = models.CharField(max_length=100)
class Enrollment(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    date_enrolled = models.DateField()
    grade = models.CharField(max_length=2, blank=True)

4. 迁移操作

创建迁移

python manage.py makemigrations

应用迁移

python manage.py migrate

迁移命令

命令 描述
showmigrations 显示所有迁移状态
sqlmigrate <app> <migration> 显示迁移的SQL语句
migrate <app> <migration> 迁移到特定版本
migrate --fake 标记迁移为已完成但不执行
migrate --plan 显示迁移计划

5. 基本CRUD

创建(Create)

# 方法1: create()
author = Author.objects.create(
    name='J.K. Rowling',
    email='jk@example.com',
    birth_date='1965-07-31'
)
# 方法2: 先创建对象再保存
book = Book(
    title='Harry Potter',
    author=author,
    publish_date='1997-06-26',
    price=19.99
)
book.save()
# 批量创建
Book.objects.bulk_create([
    Book(title='Book1', author=author, publish_date='2020-01-01', price=10),
    Book(title='Book2', author=author, publish_date='2020-02-01', price=15)
])

读取(Read)

# 获取单个对象
book = Book.objects.get(pk=1)  # 主键查询
book = Book.objects.get(title__exact='Harry Potter')
# 获取多个对象
books = Book.objects.all()
books = Book.objects.filter(price__gt=10)  # 价格大于10

更新(Update)

# 方法1: 修改属性后保存
book = Book.objects.get(pk=1)
book.price = 25.99
book.save()
# 方法2: update() (批量更新)
Book.objects.filter(publish_date__year=2020).update(price=20)
# 更新关联字段
author = Author.objects.get(name='J.K. Rowling')
Book.objects.filter(pk=1).update(author=author)

删除(Delete)

# 删除单个对象
book = Book.objects.get(pk=1)
book.delete()
# 批量删除
Book.objects.filter(price__lt=10).delete()

6. 查询集(QuerySet)

链式调用

books = Book.objects.filter(
    publish_date__year__gte=2000
).exclude(
    price__gt=30
).order_by(
    '-publish_date'
)

常用查询方法

方法 描述
all() 返回所有记录
filter(**kwargs) 返回满足条件的记录
exclude(**kwargs) 返回不满足条件的记录
order_by(*fields) 排序
values(*fields) 返回字典列表
values_list(*fields) 返回元组列表
distinct() 去重
select_related() 预取外键关联
prefetch_related() 预取多对多关联
annotate() 添加注解
aggregate() 聚合计算
count() 计数
exists() 判断是否存在
first() 第一条记录
last() 最后一条记录

延迟查询

# QuerySet是惰性的
books = Book.objects.all()  # 此时未执行查询
# 真正执行查询的情况
print(books)           # 1. 评估QuerySet
for book in books:     # 2. 迭代
    print(book)

if books:             # 3. 布尔测试
    print("有数据")

list(books)          # 4. 转换为列表

7. 高级查询

字段查找(Field Lookups)

# 精确匹配
Book.objects.filter(title__exact='Harry Potter')
Book.objects.filter(title='Harry Potter')  # 简写
# 包含
Book.objects.filter(title__contains='Potter')
# 开头/结尾
Book.objects.filter(title__startswith='Harry')
Book.objects.filter(title__endswith='Potter')
# 大小写不敏感
Book.objects.filter(title__iexact='harry potter')
# 空值
Book.objects.filter(price__isnull=True)
# 范围
Book.objects.filter(price__range=(10, 20))
# 日期查询
from datetime import date
Book.objects.filter(publish_date__year=2020)
Book.objects.filter(publish_date__gte=date(2020, 1, 1))
# 正则表达式
Book.objects.filter(title__regex=r'^(An?|The) ')

Q对象(复杂查询)

from django.db.models import Q
# OR 查询
Book.objects.filter(Q(price__lt=10) | Q(price__gt=20))
# AND 查询
Book.objects.filter(Q(title__contains='Potter') & Q(price__lt=20))
# NOT 查询
Book.objects.filter(~Q(publish_date__year=2020))

F对象(字段比较)

from django.db.models import F
# 比较同一模型的不同字段
Book.objects.filter(price__gt=F('discount_price'))
# 更新时使用字段值
Book.objects.update(price=F('price') * 1.1)  # 涨价10%

8. 聚合与注解

聚合函数

from django.db.models import Count, Avg, Max, Min, Sum
# 计算平均值
Book.objects.aggregate(Avg('price'))
# 多个聚合
Book.objects.aggregate(
    avg_price=Avg('price'),
    max_price=Max('price'),
    min_price=Min('price')
)
# 分组聚合
Author.objects.annotate(num_books=Count('book')).filter(num_books__gt=5)

注解(Annotations)

from django.db.models import Value
from django.db.models.functions import Concat
# 添加计算字段
books = Book.objects.annotate(
    price_with_tax=F('price') * 1.2,
    full_title=Concat('title', Value(' by '), 'author__name')
)
# 使用注解过滤
expensive_books = Book.objects.annotate(
    price_with_tax=F('price') * 1.2
).filter(
    price_with_tax__gt=30
)

9. 事务管理

自动提交模式(默认)

# 每个查询都在单独的事务中执行
book = Book.objects.get(pk=1)
book.price = 25.99
book.save()  # 自动提交

手动事务

from django.db import transaction
try:
    with transaction.atomic():
        # 这里的所有操作在一个事务中
        book1 = Book.objects.select_for_update().get(pk=1)
        book1.price += 10
        book1.save()

        book2 = Book.objects.get(pk=2)
        book2.price -= 5
        book2.save()
except Exception as e:
    # 发生异常时自动回滚
    print(f"Error: {e}")

保存点

with transaction.atomic():
    # 创建保存点
    sid = transaction.savepoint()

    try:
        # 操作1
        book1 = Book.objects.get(pk=1)
        book1.price += 10
        book1.save()

        # 操作2 (可能失败)
        book2 = Book.objects.get(pk=2)
        book2.price -= 15  # 可能导致价格为负
        book2.save()
    except:
        # 回滚到保存点
        transaction.savepoint_rollback(sid)
        raise

10. 性能优化

select_related (外键/一对一)

# 普通查询 (N+1问题)
books = Book.objects.all()
for book in books:
    print(book.author.name)  # 每次循环都会查询author
# 优化查询 (使用JOIN)
books = Book.objects.select_related('author').all()
for book in books:
    print(book.author.name)  # 已预取author

prefetch_related (多对多/一对多)

# 普通查询 (N+1问题)
authors = Author.objects.all()
for author in authors:
    print(author.book_set.all())  # 每次循环都会查询books
# 优化查询 (使用额外查询)
authors = Author.objects.prefetch_related('book_set').all()
for author in authors:
    print(author.book_set.all())  # 已预取books

只获取需要的字段

# 获取所有字段 (默认)
books = Book.objects.all()
# 只获取需要的字段
books = Book.objects.only('title', 'price')
books = Book.objects.defer('bio')  # 排除某些字段

批量操作

# 批量创建
Book.objects.bulk_create([
    Book(title='Book1', price=10),
    Book(title='Book2', price=15)
])
# 批量更新
books = Book.objects.filter(publish_date__year=2020)
for book in books:
    book.price *= 1.1
Book.objects.bulk_update(books, ['price'])

11. 原生SQL

执行原生查询

from django.db import connection
# 返回字典列表
books = Book.objects.raw('SELECT * FROM myapp_book WHERE price > %s', [10])
# 使用游标
with connection.cursor() as cursor:
    cursor.execute("UPDATE myapp_book SET price = price * 1.1 WHERE price < %s", [20])
    row = cursor.fetchone()

自定义SQL表达式

from django.db.models import Func
class UpperCase(Func):
    function = 'UPPER'
    template = '%(function)s(%(expressions)s)'
# 使用自定义表达式
Author.objects.annotate(
    upper_name=UpperCase('name')
)

总结

Django ORM 提供了强大而直观的数据库访问方式,通过本教程你应该掌握了:

  1. 如何定义模型和关系
  2. 基本的CRUD操作
  3. 复杂的查询方法
  4. 聚合与注解
  5. 事务管理
  6. 性能优化技巧 要深入学习 Django ORM,建议:
  7. 阅读 Django 官方文档的模型部分
  8. 实践各种查询场景
  9. 使用 Django Debug Toolbar 分析查询性能
  10. 学习如何为复杂查询添加数据库索引









results matching ""

    No results matching ""