面试题答案
一键面试数据库查询优化
- 使用select_related和prefetch_related:
select_related
用于处理外键关系,通过SQL的JOIN
操作一次性获取关联对象,减少数据库查询次数。例如,有一个Book
模型和Author
模型,Book
通过外键关联Author
:
from myapp.models import Book books = Book.objects.select_related('author').all() for book in books: print(book.author.name)
prefetch_related
适用于多对多或反向一对多关系,它通过多个查询获取数据,然后在Python层面进行合并。比如Book
和Genre
是多对多关系:
from myapp.models import Book books = Book.objects.prefetch_related('genres').all() for book in books: for genre in book.genres.all(): print(genre.name)
- 避免N+1查询问题:在循环中进行数据库查询会导致N+1查询问题(1次查询获取主数据,N次查询获取关联数据)。可以通过上述
select_related
和prefetch_related
解决,或者在必要时手动进行批量查询。例如,要获取多个Book
对象及其对应的Review
数量:from myapp.models import Book, Review book_ids = [1, 2, 3] reviews_count = {review.book_id: review.count() for review in Review.objects.filter(book_id__in = book_ids).values('book_id').annotate(count = Count('id'))} books = Book.objects.filter(id__in = book_ids) for book in books: print(f"Book {book.title} has {reviews_count.get(book.id, 0)} reviews")
- 索引优化:
- 为经常用于过滤、排序和连接的字段添加索引。例如,如果经常根据
publication_date
字段查询Book
:
from django.db import models class Book(models.Model): title = models.CharField(max_length = 200) publication_date = models.DateField() class Meta: indexes = [ models.Index(fields = ['publication_date']), ]
- 为经常用于过滤、排序和连接的字段添加索引。例如,如果经常根据
缓存机制应用
- Memcached:
- 安装和配置Memcached后,在Django中可以使用
django - cache
来集成。例如,缓存一个视图函数的响应:
from django.views.decorators.cache import cache_page @cache_page(60 * 15) # 缓存15分钟 def my_api_view(request): # 视图逻辑 pass
- 也可以在较低层次使用
cache
对象来缓存特定数据。比如缓存一个数据库查询结果:
from django.core.cache import cache def get_books(): key = 'all_books' books = cache.get(key) if not books: books = Book.objects.all() cache.set(key, books, 60 * 5) # 缓存5分钟 return books
- 安装和配置Memcached后,在Django中可以使用
- Redis:
- 安装
redis - py
库后,可以在Django中使用Redis作为缓存。例如,使用Redis缓存视图响应:
from django_redis.decorators import cache_page @cache_page(60 * 10, cache ='redis_cache') # 缓存10分钟,使用名为'redis_cache'的缓存配置 def my_api_view(request): # 视图逻辑 pass
- 利用Redis的高级数据结构进行缓存和数据处理。比如使用Redis的
Sorted Set
来缓存热门书籍排行榜:
import redis r = redis.Redis(host='localhost', port = 6379, db = 0) def update_book_rankings(book_id, score): r.zadd('book_rankings', {book_id: score}) def get_top_books(): top_book_ids = r.zrevrange('book_rankings', 0, 9) return Book.objects.filter(id__in = top_book_ids)
- 安装
视图函数优化
- 减少不必要的计算:避免在视图函数中进行复杂且重复的计算。例如,如果某些数据是固定的,可以将其提取到全局变量或者单独的模块中。
# 假设这是一个固定的配置 API_VERSION = 'v1' def my_api_view(request): # 视图逻辑 pass
- 异步处理:对于一些耗时的操作,可以使用异步任务队列(如Celery)。例如,在API处理用户注册时,发送欢迎邮件可能是耗时操作,可将其异步化:
- 定义任务:
from celery import shared_task @shared_task def send_welcome_email(user_email): # 发送邮件逻辑 pass
- 在视图函数中调用任务:
from django.http import JsonResponse from. tasks import send_welcome_email def register_user(request): if request.method == 'POST': # 处理用户注册逻辑 user_email = 'user@example.com' send_welcome_email.delay(user_email) return JsonResponse({'message': 'User registered successfully'})
- 使用视图集和路由器:在Django REST framework中,使用视图集和路由器可以简化代码,提高性能。例如:
这样可以自动生成多个API端点(如列表、详情、创建、更新、删除等),减少重复代码,提高开发效率和性能。from rest_framework.viewsets import ModelViewSet from rest_framework.routers import DefaultRouter from myapp.models import Book from myapp.serializers import BookSerializer class BookViewSet(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer router = DefaultRouter() router.register('books', BookViewSet)