1. Django ORM 单表操作基础概念
Django ORM(Object-Relational Mapping)是Django框架中最强大的功能之一,它允许开发者使用Python类和方法来操作数据库,而无需直接编写SQL语句。单表操作是ORM中最基础也是最常用的功能,掌握好单表操作是使用Django ORM的第一步。
在Django中,每个模型类对应数据库中的一张表。当我们定义一个模型类时,Django会自动为我们创建对应的数据库表结构。例如,我们定义一个简单的Book模型:
from django.db import models class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) publish_date = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2) def __str__(self): return self.title这个简单的模型类定义了四个字段:title(书名)、author(作者)、publish_date(出版日期)和price(价格)。Django会自动为这个模型创建对应的数据库表,表名默认为"appname_book"(假设这个模型在名为"books"的app中)。
注意:在Django中,每个模型类必须继承自django.db.models.Model类,这是Django ORM工作的基础。
2. 单表CRUD操作详解
2.1 创建记录(Create)
在Django ORM中,创建新记录有几种不同的方式:
- 使用create()方法:
Book.objects.create( title="Python编程从入门到实践", author="Eric Matthes", publish_date="2020-05-01", price=89.00 )- 先创建模型实例,再保存:
new_book = Book( title="流畅的Python", author="Luciano Ramalho", publish_date="2017-05-01", price=109.00 ) new_book.save()- 使用get_or_create()方法(避免重复创建):
book, created = Book.objects.get_or_create( title="Python编程从入门到实践", defaults={ 'author': 'Eric Matthes', 'publish_date': '2020-05-01', 'price': 89.00 } )提示:get_or_create()方法返回一个元组,第一个元素是获取或创建的对象,第二个元素是布尔值,表示对象是否是新创建的。
2.2 查询记录(Read)
Django ORM提供了丰富的查询API,以下是一些常用的查询方法:
- 获取所有记录:
all_books = Book.objects.all()- 获取单条记录(通过主键):
book = Book.objects.get(pk=1)- 条件查询:
# 价格大于100的书 expensive_books = Book.objects.filter(price__gt=100) # 作者是"Eric Matthes"的书 eric_books = Book.objects.filter(author="Eric Matthes") # 2020年出版的书 books_2020 = Book.objects.filter(publish_date__year=2020)- 链式查询:
# 2020年出版且价格大于80的书 books = Book.objects.filter(publish_date__year=2020).filter(price__gt=80)- 排序:
# 按价格升序 books_asc = Book.objects.all().order_by('price') # 按价格降序 books_desc = Book.objects.all().order_by('-price')2.3 更新记录(Update)
更新记录也有几种方式:
- 先获取对象再修改属性并保存:
book = Book.objects.get(pk=1) book.price = 99.00 book.save()- 使用update()方法批量更新:
# 将所有Eric Matthes的书价格设为95 Book.objects.filter(author="Eric Matthes").update(price=95)注意:update()方法直接生成SQL语句执行更新,不会调用模型的save()方法,也不会触发pre_save或post_save信号。
2.4 删除记录(Delete)
删除记录同样有多种方式:
- 删除单个对象:
book = Book.objects.get(pk=1) book.delete()- 批量删除:
# 删除所有价格低于50的书 Book.objects.filter(price__lt=50).delete()- 删除所有记录:
Book.objects.all().delete()警告:delete()操作是不可逆的,执行前务必确认数据备份或确认操作无误。
3. 高级查询技巧
3.1 字段查询条件
Django ORM提供了丰富的字段查询条件:
- exact:精确匹配(默认)
Book.objects.filter(title__exact="Python编程")- iexact:不区分大小写的精确匹配
Book.objects.filter(title__iexact="python编程")- contains:包含
Book.objects.filter(title__contains="Python")- icontains:不区分大小写的包含
Book.objects.filter(title__icontains="python")- in:在某个列表中
Book.objects.filter(price__in=[89, 99, 109])- gt/gte/lt/lte:大于/大于等于/小于/小于等于
Book.objects.filter(price__gt=100)- startswith/istartswith:以...开头
Book.objects.filter(title__startswith="Python")- endswith/iendswith:以...结尾
Book.objects.filter(title__endswith="实践")- range:范围查询
import datetime start_date = datetime.date(2020, 1, 1) end_date = datetime.date(2020, 12, 31) Book.objects.filter(publish_date__range=(start_date, end_date))3.2 聚合与分组
Django ORM支持各种聚合操作:
- 计数:
from django.db.models import Count Book.objects.aggregate(total=Count('id'))- 平均值:
from django.db.models import Avg Book.objects.aggregate(avg_price=Avg('price'))- 最大值/最小值:
from django.db.models import Max, Min Book.objects.aggregate( max_price=Max('price'), min_price=Min('price') )- 分组统计:
from django.db.models import Count # 按作者统计书籍数量 Book.objects.values('author').annotate(book_count=Count('id'))3.3 F表达式和Q对象
- F表达式:用于在查询中引用字段的值
from django.db.models import F # 将所有书的价格增加10元 Book.objects.all().update(price=F('price') + 10)- Q对象:用于构建复杂的查询条件
from django.db.models import Q # 查找作者是Eric Matthes或价格大于100的书 Book.objects.filter(Q(author="Eric Matthes") | Q(price__gt=100)) # 查找书名包含"Python"但不包含"入门"的书 Book.objects.filter( Q(title__contains="Python") & ~Q(title__contains="入门") )4. 性能优化与常见问题
4.1 查询性能优化
- 使用select_related和prefetch_related(虽然本文讲单表,但这是ORM重要概念)
# 假设Book有一个外键publisher books = Book.objects.select_related('publisher').all()- 使用only和defer控制查询字段
# 只查询title和author字段 books = Book.objects.only('title', 'author') # 查询除price外的所有字段 books = Book.objects.defer('price')- 使用values和values_list获取特定字段
# 返回字典列表 books = Book.objects.values('title', 'author') # 返回元组列表 books = Book.objects.values_list('title', 'author')- 避免N+1查询问题
# 不好的做法(会导致N+1查询) for book in Book.objects.all(): print(book.publisher.name) # 每次循环都会查询一次publisher # 好的做法 for book in Book.objects.select_related('publisher').all(): print(book.publisher.name) # 只查询一次4.2 常见问题与解决方案
- DoesNotExist异常
try: book = Book.objects.get(pk=1000) # 假设不存在 except Book.DoesNotExist: print("书籍不存在")- MultipleObjectsReturned异常
try: book = Book.objects.get(title="Python编程") # 假设有多本同名书 except Book.MultipleObjectsReturned: print("找到多本同名书籍")- 批量操作性能问题
# 不好的做法(每次循环都执行一次SQL) for i in range(1000): Book.objects.create(title=f"Book {i}", author="Author", price=10) # 好的做法(使用bulk_create) books = [Book(title=f"Book {i}", author="Author", price=10) for i in range(1000)] Book.objects.bulk_create(books)- 事务处理
from django.db import transaction try: with transaction.atomic(): # 一系列数据库操作 book1 = Book.objects.create(title="Book 1", price=10) book2 = Book.objects.create(title="Book 2", price=20) # 如果这里出错,前面的创建操作也会回滚 except Exception as e: print(f"操作失败: {e}")5. 实际应用案例
5.1 书籍管理系统基础功能实现
假设我们要实现一个简单的书籍管理系统,以下是一些基础功能的实现:
- 添加新书
def add_book(title, author, publish_date, price): try: book = Book.objects.create( title=title, author=author, publish_date=publish_date, price=price ) return True, "添加成功" except Exception as e: return False, f"添加失败: {e}"- 搜索书籍
def search_books(keyword=None, author=None, min_price=None, max_price=None): query = Q() if keyword: query &= Q(title__icontains=keyword) if author: query &= Q(author__icontains=author) if min_price is not None: query &= Q(price__gte=min_price) if max_price is not None: query &= Q(price__lte=max_price) return Book.objects.filter(query).order_by('-publish_date')- 更新书籍价格
def update_book_price(book_id, new_price): try: book = Book.objects.get(pk=book_id) book.price = new_price book.save() return True, "更新成功" except Book.DoesNotExist: return False, "书籍不存在" except Exception as e: return False, f"更新失败: {e}"- 删除书籍
def delete_book(book_id): try: book = Book.objects.get(pk=book_id) book.delete() return True, "删除成功" except Book.DoesNotExist: return False, "书籍不存在" except Exception as e: return False, f"删除失败: {e}"5.2 数据统计功能
- 书籍价格统计
def get_price_stats(): from django.db.models import Avg, Max, Min, Count return Book.objects.aggregate( total=Count('id'), avg_price=Avg('price'), max_price=Max('price'), min_price=Min('price') )- 按作者统计
def get_author_stats(): from django.db.models import Count, Avg return Book.objects.values('author').annotate( book_count=Count('id'), avg_price=Avg('price') ).order_by('-book_count')- 年度出版统计
def get_yearly_stats(): from django.db.models import Count from django.db.models.functions import ExtractYear return Book.objects.annotate( year=ExtractYear('publish_date') ).values('year').annotate( count=Count('id') ).order_by('year')6. 最佳实践与经验分享
6.1 模型设计建议
- 为常用查询字段添加db_index
class Book(models.Model): title = models.CharField(max_length=100, db_index=True) author = models.CharField(max_length=50, db_index=True) # 其他字段...- 为模型添加Meta选项
class Book(models.Model): # 字段定义... class Meta: ordering = ['-publish_date'] # 默认排序 verbose_name = '图书' # 在admin中显示的名称 verbose_name_plural = '图书' # 复数形式 indexes = [ models.Index(fields=['author', 'publish_date']), ]- 使用choices限制字段可选值
class Book(models.Model): CATEGORY_CHOICES = [ ('TECH', '技术'), ('LIT', '文学'), ('HIS', '历史'), ] category = models.CharField( max_length=4, choices=CATEGORY_CHOICES, default='TECH' ) # 其他字段...6.2 查询优化经验
- 使用explain()分析查询性能
# 查看查询执行计划 print(Book.objects.filter(price__gt=100).explain())- 避免在循环中执行查询
# 不好的做法 authors = ['Author1', 'Author2', 'Author3'] books = [] for author in authors: books.extend(Book.objects.filter(author=author)) # 好的做法 books = Book.objects.filter(author__in=authors)- 使用iterator()处理大量数据
# 处理大量数据时节省内存 for book in Book.objects.all().iterator(): process_book(book)- 使用count()代替len(queryset)
# 不好的做法(会加载所有数据到内存) count = len(Book.objects.all()) # 好的做法(使用数据库的count函数) count = Book.objects.count()6.3 调试技巧
- 查看生成的SQL语句
# 方法1:使用query属性 queryset = Book.objects.filter(price__gt=100) print(queryset.query) # 方法2:使用connection.queries from django.db import connection queryset = Book.objects.filter(price__gt=100) list(queryset) # 执行查询 print(connection.queries)- 使用django-debug-toolbar
# 安装后可以直观查看查询性能 # pip install django-debug-toolbar- 日志记录数据库查询
# 在settings.py中添加 LOGGING = { 'version': 1, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'level': 'DEBUG', 'handlers': ['console'], }, }, }