资讯动态

Django ORM 基础与高级使用指南

发布时间:2026/9/12 7:09:32 来源:尧图企业网站定制
1. Django ORM 基础概念解析Django ORMObject-Relational Mapping是Django框架中最强大的功能之一它让我们可以用Python面向对象的方式来操作数据库而不用直接编写SQL语句。作为一个使用Django开发多年的老手我可以负责任地说掌握ORM是高效使用Django的关键。ORM的核心思想是将数据库表映射为Python类表中的行对应类的实例字段则对应实例属性。这种抽象让我们可以用更符合Python思维的方式来处理数据同时避免了不同数据库系统之间的语法差异。提示虽然ORM简化了数据库操作但理解背后的SQL执行原理对于优化查询性能至关重要。2. 模型定义与数据库迁移2.1 创建第一个模型在Django中定义模型非常简单我们只需要继承django.db.models.Model类。假设我们要创建一个博客应用定义Post模型from django.db import models class Post(models.Model): title models.CharField(max_length200) content models.TextField() created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) is_published models.BooleanField(defaultFalse) def __str__(self): return self.title这里有几个关键点需要注意CharField用于存储较短文本必须指定max_lengthTextField适合长文本内容auto_now_add会在对象首次创建时自动设置当前时间auto_now会在每次保存对象时更新时间__str__方法定义了对象的字符串表示在admin界面很有用2.2 数据库迁移定义好模型后需要生成并应用迁移python manage.py makemigrations python manage.py migrate迁移是Django同步模型变更到数据库的方式。makemigrations命令会检测模型变化并生成迁移文件migrate命令则实际执行这些变更。注意在生产环境中执行迁移前一定要先在测试环境验证并备份数据库。3. 基础CRUD操作3.1 创建记录创建新记录有几种方式# 方法1create post Post.objects.create( title我的第一篇博客, content这是内容..., is_publishedTrue ) # 方法2先实例化再保存 post Post( title我的第二篇博客, content更多内容... ) post.save()create方法会立即保存到数据库而第二种方式允许我们在保存前进行更多操作。3.2 查询记录Django ORM提供了丰富的查询API# 获取所有已发布的文章 published_posts Post.objects.filter(is_publishedTrue) # 获取单个对象 post Post.objects.get(id1) # 限制查询数量 recent_posts Post.objects.all()[:5] # 复杂查询 from django.db.models import Q posts Post.objects.filter( Q(title__containsDjango) | Q(content__containsORM) )查询方法说明filter()返回满足条件的QuerySetget()返回单个对象如果没有或找到多个会抛出异常all()返回所有记录可以使用双下划线语法进行各种字段查找如contains, startswith等3.3 更新记录更新记录也有多种方式# 方法1直接修改属性后保存 post Post.objects.get(id1) post.title 修改后的标题 post.save() # 方法2使用update方法批量更新 Post.objects.filter(is_publishedFalse).update(is_publishedTrue)提示批量更新时使用update()比先查询再逐个保存效率高得多因为它只生成一条SQL语句。3.4 删除记录删除操作同样简单# 删除单个对象 post Post.objects.get(id1) post.delete() # 批量删除 Post.objects.filter(is_publishedFalse).delete()4. 高级查询技巧4.1 聚合与注解Django ORM支持各种聚合操作from django.db.models import Count, Avg, Max # 统计已发布文章数量 published_count Post.objects.filter(is_publishedTrue).count() # 按发布日期分组统计 from django.db.models.functions import TruncDate daily_counts Post.objects.annotate( dateTruncDate(created_at) ).values(date).annotate(countCount(id))4.2 F()表达式F()表达式允许引用模型字段的值非常适合需要基于当前值更新的场景from django.db.models import F # 增加阅读量 Post.objects.filter(id1).update(viewsF(views) 1)4.3 预加载关联数据使用select_related和prefetch_related可以优化关联查询# 假设Post有一个外键author指向User模型 # select_related用于一对一或外键关系 posts Post.objects.select_related(author).all() # prefetch_related用于多对多或反向关系 from django.contrib.auth.models import User user User.objects.prefetch_related(post_set).get(id1)5. 模型关系处理5.1 一对一关系class Profile(models.Model): user models.OneToOneField(User, on_deletemodels.CASCADE) bio models.TextField()5.2 一对多关系class Comment(models.Model): post models.ForeignKey(Post, on_deletemodels.CASCADE) content models.TextField()5.3 多对多关系class Tag(models.Model): name models.CharField(max_length50) posts models.ManyToManyField(Post)关系字段的on_delete参数非常重要它决定了当被引用对象删除时的行为常见选项有CASCADE级联删除PROTECT阻止删除SET_NULL设置为NULL需要字段允许nullSET_DEFAULT设置为默认值6. 性能优化技巧6.1 避免N1查询问题这是ORM中最常见的性能陷阱# 不好的写法会导致N1查询 for post in Post.objects.all(): print(post.author.username) # 每次循环都会查询数据库 # 好的写法使用select_related for post in Post.objects.select_related(author).all(): print(post.author.username) # 只执行一次查询6.2 使用iterator()处理大量数据当需要处理大量记录时使用iterator()可以节省内存for post in Post.objects.all().iterator(): process_post(post)6.3 只查询需要的字段使用only()或defer()可以减少数据传输量# 只获取title和created_at字段 posts Post.objects.only(title, created_at) # 获取除content外的所有字段 posts Post.objects.defer(content)7. 常见问题与解决方案7.1 查询结果缓存QuerySet是惰性的只有在求值时才会执行查询但结果会被缓存queryset Post.objects.all() print(queryset) # 第一次求值执行查询 print(queryset) # 使用缓存不执行查询如果需要强制刷新查询可以重新获取QuerySet。7.2 批量操作对于大量数据操作使用bulk_create和bulk_update# 批量创建 posts [Post(titlefPost {i}) for i in range(1000)] Post.objects.bulk_create(posts) # 批量更新 posts Post.objects.all() for post in posts: post.title fUpdated {post.title} Post.objects.bulk_update(posts, [title])7.3 事务处理使用transaction.atomic确保操作原子性from django.db import transaction try: with transaction.atomic(): post Post.objects.create(title重要文章) # 其他数据库操作... except Exception as e: print(操作失败:, e)8. 自定义模型方法与管理器8.1 添加模型方法可以在模型中添加业务逻辑方法class Post(models.Model): # ... 字段定义 ... def publish(self): self.is_published True self.save() def get_summary(self, length100): return self.content[:length] ... if len(self.content) length else self.content8.2 自定义管理器管理器是模型进行数据库查询的接口可以自定义class PublishedManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(is_publishedTrue) class Post(models.Model): # ... 字段定义 ... objects models.Manager() # 默认管理器 published PublishedManager() # 自定义管理器这样可以使用Post.published.all()直接获取已发布的文章。9. 信号系统Django的信号系统允许在模型操作前后执行自定义代码from django.db.models.signals import pre_save, post_save from django.dispatch import receiver receiver(pre_save, senderPost) def pre_save_post(sender, instance, **kwargs): print(f即将保存文章: {instance.title}) receiver(post_save, senderPost) def post_save_post(sender, instance, created, **kwargs): if created: print(f新文章创建: {instance.title}) else: print(f文章更新: {instance.title})信号常用于日志记录、缓存失效、触发异步任务等场景。10. 数据库索引优化合理的索引可以大幅提高查询性能class Post(models.Model): title models.CharField(max_length200, db_indexTrue) content models.TextField() created_at models.DateTimeField(auto_now_addTrue, db_indexTrue) is_published models.BooleanField(defaultFalse) class Meta: indexes [ models.Index(fields[title, created_at]), ]索引使用原则为经常用于查询条件的字段添加索引为排序字段添加索引多字段联合索引要考虑查询模式索引会降低写入速度不宜过度使用11. 模型继承策略Django支持三种模型继承方式11.1 抽象基类class BaseModel(models.Model): created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue) class Meta: abstract True class Post(BaseModel): title models.CharField(max_length200)抽象基类不会创建数据库表它的字段会被添加到子类中。11.2 多表继承class Place(models.Model): name models.CharField(max_length50) address models.CharField(max_length80) class Restaurant(Place): serves_hot_dogs models.BooleanField(defaultFalse) serves_pizza models.BooleanField(defaultFalse)这种方式会为每个模型创建数据库表并通过OneToOneField关联。11.3 代理模型class Post(models.Model): title models.CharField(max_length200) content models.TextField() class PublishedPost(Post): class Meta: proxy True def get_queryset(self): return super().get_queryset().filter(is_publishedTrue)代理模型操作同一张数据库表但可以添加不同的行为。12. 数据库路由与多数据库支持对于大型项目可能需要使用多个数据库# settings.py DATABASE_ROUTERS [path.to.MyRouter] # routers.py class MyRouter: def db_for_read(self, model, **hints): if model._meta.app_label analytics: return analytics_db return None def db_for_write(self, model, **hints): if model._meta.app_label analytics: return analytics_db return None然后在查询时可以通过using方法指定数据库Post.objects.using(analytics_db).all()13. 原生SQL查询虽然ORM很强大但有时需要直接执行SQLfrom django.db import connection # 执行自定义SQL with connection.cursor() as cursor: cursor.execute(SELECT * FROM blog_post WHERE title LIKE %s, [%Django%]) rows cursor.fetchall() # 使用raw方法 posts Post.objects.raw(SELECT * FROM blog_post WHERE id %s, [10])注意使用原生SQL时要特别注意SQL注入风险永远不要直接拼接用户输入。14. 测试与调试技巧14.1 查看生成的SQL# 打印QuerySet对应的SQL print(Post.objects.filter(is_publishedTrue).query) # 使用django-debug-toolbar # settings.py INSTALLED_APPS [debug_toolbar] MIDDLEWARE [debug_toolbar.middleware.DebugToolbarMiddleware]14.2 编写模型测试from django.test import TestCase from .models import Post class PostModelTest(TestCase): def setUp(self): Post.objects.create(title测试文章, content测试内容) def test_post_creation(self): post Post.objects.get(title测试文章) self.assertEqual(post.content, 测试内容)15. 实际项目经验分享在实际项目中我发现以下几点特别重要保持模型精简模型应该只包含数据相关的逻辑业务逻辑应该放在其他地方如表单、视图或服务层。合理使用select_related和prefetch_related这是优化ORM查询最有效的手段之一但要注意不要过度预加载不需要的数据。定期检查慢查询使用django-debug-toolbar或数据库的慢查询日志来识别性能瓶颈。考虑使用ORM扩展像django-model-utils和django-extensions这样的第三方包提供了许多有用的功能。文档化模型关系复杂的模型关系应该用注释清楚地说明特别是涉及多态关系或自定义管理器时。版本控制迁移文件迁移文件应该和其他代码一样纳入版本控制并小心处理团队协作时的合并冲突。考虑使用UUID作为主键对于分布式系统或需要隐藏记录数量的场景UUID比自增ID更合适。合理使用信号信号很强大但过度使用会使代码难以追踪考虑使用显式服务调用代替某些信号。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价