1. Django模板语法深度解析
作为全栈开发的核心环节,Django模板系统承担着前后端数据交互的桥梁作用。我在实际项目中最常使用的模板语法可以归纳为三类基础结构:
1.1 变量渲染与过滤器
变量渲染是模板中最基础的功能,通过{{ variable }}语法实现。但很多人不知道的是,Django默认开启了自动HTML转义机制,这是防范XSS攻击的重要措施。当需要显示原生HTML时,必须显式使用safe过滤器:
{{ user_input|safe }} <!-- 慎用!需确保内容可信 -->常用的内置过滤器还有:
date:"Y-m-d":日期格式化truncatechars:50:字符串截断filesizeformat:文件大小人性化显示
经验之谈:在商业项目中,我建议自定义过滤器而非在模板中写复杂逻辑。比如创建
custom_filters.py:
@register.filter def highlight_search(text, query): return text.replace(query, f'<span class="highlight">{query}</span>')1.2 逻辑控制标签
{% if %}和{% for %}是最常用的逻辑标签。需要特别注意:
判断相等时使用
==而非=:{% if user.role == 'admin' %}遍历查询集时添加
{% empty %}处理:{% for item in items %} {{ item.name }} {% empty %} <p>暂无数据</p> {% endfor %}循环计数器技巧:
{% for obj in objs %} {% cycle 'row1' 'row2' as rowclass %} <tr class="{{ rowclass }}">...</tr> {% endfor %}
1.3 模板继承体系
合理的模板继承能减少70%的重复代码。我的标准做法是:
创建
base.html作为根模板:<!DOCTYPE html> <html> <head> <title>{% block title %}默认标题{% endblock %}</title> {% block css %}{% endblock %} </head> <body> {% block content %}{% endblock %} {% block js %}{% endblock %} </body> </html>子模板通过
{% extends %}继承:{% extends "base.html" %} {% block title %}子页面标题{% endblock %} {% block content %} <h1>自定义内容区</h1> {% endblock %}
避坑指南:避免继承链超过3层,否则会显著增加维护成本。我曾接手过一个5层继承的项目,调试模板耗时增加3倍。
2. 请求处理机制剖析
2.1 HttpRequest对象详解
Django将HTTP请求封装为HttpRequest对象,常用属性有:
| 属性 | 类型 | 说明 |
|---|---|---|
| method | str | GET/POST/PUT等 |
| GET | QueryDict | URL参数 |
| POST | QueryDict | 表单数据 |
| FILES | MultiValueDict | 上传文件 |
| user | User | 认证用户 |
| path | str | 不含域名的路径 |
实际开发中几个实用技巧:
- 获取客户端IP:
request.META.get('REMOTE_ADDR') - 判断AJAX请求:
request.headers.get('X-Requested-With') == 'XMLHttpRequest' - 获取Content-Type:
request.content_type
2.2 多场景参数获取
GET参数处理
def search(request): query = request.GET.get('q', '') # 第二个参数为默认值 page = int(request.GET.get('page', 1)) # 建议添加参数校验 if page < 1: page = 1POST表单处理
def register(request): if request.method == 'POST': username = request.POST['username'] # 直接访问可能引发KeyError password = request.POST.get('password', '') # 更安全的做法 try: email = request.POST['email'] except KeyError: return HttpResponseBadRequest("Missing email")JSON数据处理
import json def api_endpoint(request): if request.content_type == 'application/json': try: data = json.loads(request.body) param1 = data.get('param1') except json.JSONDecodeError: return JsonResponse({'error': 'Invalid JSON'}, status=400)2.3 文件上传实战
文件上传需要特别注意:
- 表单必须设置
enctype="multipart/form-data" - 视图函数中通过
request.FILES获取文件
def upload_avatar(request): if request.method == 'POST': file = request.FILES.get('avatar') if not file: return HttpResponseBadRequest("No file uploaded") # 验证文件类型 allowed_types = ['image/jpeg', 'image/png'] if file.content_type not in allowed_types: return HttpResponseBadRequest("Invalid file type") # 限制文件大小(5MB) if file.size > 5*1024*1024: return HttpResponseBadRequest("File too large") # 保存文件 with open(f'avatars/{request.user.id}.jpg', 'wb+') as f: for chunk in file.chunks(): f.write(chunk) return HttpResponse("Upload success")3. 响应生成高级技巧
3.1 HttpResponse的灵活运用
除了基础的字符串响应,Django提供了多种响应类型:
from django.http import ( HttpResponse, JsonResponse, HttpResponseRedirect, HttpResponseNotFound ) # 普通文本响应 def text_response(request): return HttpResponse("Hello World", content_type="text/plain") # JSON响应(两种方式) def json_response1(request): data = {'status': 'success', 'code': 200} return JsonResponse(data) def json_response2(request): data = ['apple', 'banana', 'orange'] return HttpResponse( json.dumps(data), content_type="application/json" ) # 重定向 def redirect_view(request): return HttpResponseRedirect('/new-url/') # 404响应 def not_found(request): return HttpResponseNotFound('<h1>Page not found</h1>')3.2 流式响应优化性能
对于大文件下载或实时生成的内容,使用StreamingHttpResponse能显著降低内存占用:
from django.http import StreamingHttpResponse import csv def large_csv_export(request): def data_generator(): # 生成CSV头部 yield 'name,age,email\n' # 分批生成数据 for i in range(100000): yield f'user{i},{20+i%30},user{i}@example.com\n' response = StreamingHttpResponse( data_generator(), content_type='text/csv' ) response['Content-Disposition'] = 'attachment; filename="users.csv"' return response3.3 自定义响应头实战
通过response.headers可以设置各种HTTP头:
def set_headers(request): response = HttpResponse() response['X-Custom-Header'] = 'MyValue' response['Cache-Control'] = 'max-age=3600' response.headers['X-API-Version'] = '1.0' # Django 3.2+推荐方式 return response4. 常见问题排查手册
4.1 模板渲染问题
问题1:变量未显示
- 检查视图是否传递了该变量
- 确认变量名拼写一致(区分大小写)
- 使用
{% debug %}标签查看所有可用变量
问题2:静态文件404
- 确保
STATIC_URL配置正确 - 开发环境需要
django.contrib.staticfiles - 生产环境需要运行
collectstatic
4.2 请求参数问题
问题1:POST数据为空
- 检查表单是否有
method="POST" - 确认
enctype是否正确(文件上传需要multipart/form-data) - 查看中间件是否过滤了请求(如CSRF保护)
问题2:获取不到URL参数
- 检查URLconf是否捕获了参数
- 使用
request.GET.dict()打印所有参数 - 确认没有同名的GET/POST参数覆盖
4.3 响应异常问题
问题1:中文乱码
- 设置
response.charset = 'utf-8' - 确保数据库和模板都是UTF-8编码
- JSON响应使用
ensure_ascii=False:
return JsonResponse(data, json_dumps_params={'ensure_ascii': False})问题2:下载文件损坏
- 确认设置了正确的
Content-Type - 二进制文件必须使用
wb模式写入 - 大文件使用
StreamingHttpResponse
5. 性能优化实践
5.1 模板渲染优化
启用模板缓存:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [...], 'loaders': [ ('django.template.loaders.cached.Loader', [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ]), ], }, }, ]减少模板继承层级:每增加一层继承会增加约15%的渲染时间
慎用
{% include %}:频繁包含小模板会显著降低性能
5.2 请求处理优化
批量查询代替N+1查询:
# 错误做法(产生N+1查询) books = Book.objects.all() for book in books: print(book.author.name) # 每次循环都查询数据库 # 正确做法(使用select_related) books = Book.objects.select_related('author').all()合理使用缓存:
from django.core.cache import cache def get_popular_articles(): articles = cache.get('popular_articles') if not articles: articles = Article.objects.filter(...)[:10] cache.set('popular_articles', articles, timeout=3600) return articles异步任务处理: 对于耗时操作(如发送邮件、处理图片),使用Celery等异步任务队列:
@shared_task def process_uploaded_file(file_id): file = UploadedFile.objects.get(id=file_id) # 耗时处理逻辑... # 视图中调用 process_uploaded_file.delay(new_file.id)
6. 安全防护要点
6.1 XSS防护
自动转义机制:
- Django模板默认开启HTML转义
- 需要显示原始HTML时使用
|safe过滤器 - 或者标记内容为安全:
from django.utils.safestring import mark_safe message = mark_safe("<b>Safe HTML</b>")
CSP策略: 通过中间件设置内容安全策略:
MIDDLEWARE += ['csp.middleware.CSPMiddleware'] CSP_DEFAULT_SRC = ("'self'",) CSP_SCRIPT_SRC = ("'self'", "cdn.example.com")
6.2 CSRF防护
表单必须包含
{% csrf_token %}:<form method="post"> {% csrf_token %} <!-- 表单内容 --> </form>AJAX请求处理:
// 从cookie获取CSRF token function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent( cookie.substring(name.length + 1) ); break; } } } return cookieValue; } // 设置AJAX请求头 const csrftoken = getCookie('csrftoken'); fetch('/api/endpoint/', { method: 'POST', headers: { 'X-CSRFToken': csrftoken, 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
6.3 SQL注入防护
永远不要拼接SQL:
# 危险! query = f"SELECT * FROM users WHERE name = '{request.GET['name']}'" # 安全 User.objects.filter(name=request.GET['name'])使用ORM的安全方法:
filter()和exclude()get()和create()- 原始SQL查询必须使用参数化:
from django.db import connection with connection.cursor() as cursor: cursor.execute("SELECT * FROM users WHERE name = %s", [name]) row = cursor.fetchone()
7. 项目结构最佳实践
7.1 模板目录布局
推荐的项目模板结构:
templates/ ├── base/ # 基础模板 │ ├── base.html │ ├── head.html # 头部区块 │ └── footer.html ├── accounts/ # 应用相关模板 │ ├── login.html │ └── profile.html ├── blog/ │ ├── list.html │ └── detail.html └── includes/ # 公共组件 ├── navbar.html └── messages.html7.2 视图代码组织
基于类的视图(CBV):
from django.views.generic import ListView, DetailView class ArticleListView(ListView): model = Article template_name = 'blog/list.html' paginate_by = 10 context_object_name = 'articles' class ArticleDetailView(DetailView): model = Article template_name = 'blog/detail.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['related'] = Article.objects.filter( category=self.object.category ).exclude(id=self.object.id)[:5] return context视图混入(Mixins):
from django.contrib.auth.mixins import LoginRequiredMixin class ProtectedView(LoginRequiredMixin, TemplateView): login_url = '/login/' template_name = 'secret.html'自定义装饰器:
def staff_required(view_func): @wraps(view_func) def _wrapped_view(request, *args, **kwargs): if not request.user.is_staff: return HttpResponseForbidden() return view_func(request, *args, **kwargs) return _wrapped_view @staff_required def admin_dashboard(request): return render(request, 'admin/dashboard.html')
8. 调试技巧大全
8.1 模板调试
{% debug %}标签: 在模板中添加{% debug %}可以显示:- 所有可用变量
- 当前模板上下文
- 已加载的模板标签
Django Debug Toolbar: 安装配置后可以查看:
- SQL查询详情
- 模板渲染时间
- 缓存命中情况
- 请求/响应头信息
8.2 请求响应调试
打印请求信息:
def debug_view(request): print(f"Method: {request.method}") print(f"GET: {request.GET.dict()}") print(f"POST: {request.POST.dict()}") print(f"Headers: {dict(request.headers)}") return HttpResponse("Check console output")使用curl测试API:
# GET请求 curl http://localhost:8000/api/items/?q=test # POST JSON数据 curl -X POST -H "Content-Type: application/json" \ -d '{"key":"value"}' http://localhost:8000/api/create/ # 带认证头 curl -H "Authorization: Token abc123" http://localhost:8000/api/protected/
8.3 性能分析
SQL查询分析:
from django.db import connection def query_debug_view(request): # 重置查询记录 connection.queries_log.clear() # 执行查询操作 books = Book.objects.select_related('author').filter( publish_date__year=2023 ) # 输出SQL查询 for query in connection.queries: print(f"{query['time']}s: {query['sql']}") return render(request, 'debug.html', {'books': books})使用cProfile分析:
import cProfile from io import StringIO import pstats def profile_view(request): profiler = cProfile.Profile() profiler.enable() # 被分析的代码 result = some_expensive_operation() profiler.disable() stream = StringIO() stats = pstats.Stats(profiler, stream=stream) stats.sort_stats('cumulative') stats.print_stats() return HttpResponse(stream.getvalue())
9. 测试驱动开发实践
9.1 模板测试
from django.test import TestCase class TemplateTests(TestCase): def test_homepage_template(self): response = self.client.get('/') self.assertTemplateUsed(response, 'home.html') self.assertContains(response, 'Welcome') self.assertNotContains(response, 'Error') def test_context_data(self): response = self.client.get('/blog/') self.assertIn('articles', response.context) self.assertEqual(len(response.context['articles']), 10)9.2 请求响应测试
class ViewTests(TestCase): def test_redirect(self): response = self.client.get('/old-url/') self.assertRedirects(response, '/new-url/') def test_api_response(self): response = self.client.post( '/api/data/', data={'key': 'value'}, content_type='application/json' ) self.assertEqual(response.status_code, 200) self.assertJSONEqual( response.content, {'status': 'success'} ) def test_file_upload(self): with open('test.png', 'rb') as fp: response = self.client.post( '/upload/', {'file': fp} ) self.assertEqual(response.status_code, 200)9.3 性能测试
from django.test import TestCase from django.urls import reverse class PerformanceTests(TestCase): @classmethod def setUpTestData(cls): # 创建测试数据 for i in range(1000): Book.objects.create( title=f'Book {i}', author=Author.objects.create(name=f'Author {i}') ) def test_list_performance(self): with self.assertNumQueries(3): # 预期查询次数 response = self.client.get(reverse('book-list')) self.assertEqual(response.status_code, 200) def test_detail_performance(self): with self.assertNumQueries(2): book = Book.objects.first() response = self.client.get( reverse('book-detail', args=[book.pk]) ) self.assertEqual(response.status_code, 200)10. 进阶技巧与扩展
10.1 自定义模板标签
创建更强大的模板功能:
# blog/templatetags/blog_tags.py from django import template register = template.Library() @register.simple_tag(takes_context=True) def breadcrumb(context, *args): request = context['request'] path_parts = request.path.split('/')[1:-1] links = [] url_so_far = '/' for part in path_parts: url_so_far += part + '/' links.append((part.title(), url_so_far)) return { 'links': links, 'current': args[-1] if args else path_parts[-1].title() }模板中使用:
{% load blog_tags %} {% breadcrumb "Current Page" as bc %} <nav> {% for text, url in bc.links %} <a href="{{ url }}">{{ text }}</a> » {% endfor %} <span>{{ bc.current }}</span> </nav>10.2 中间件开发
创建自定义中间件处理请求/响应:
# middleware.py import time from django.utils.deprecation import MiddlewareMixin class TimingMiddleware(MiddlewareMixin): def process_request(self, request): request.start_time = time.time() def process_response(self, request, response): duration = time.time() - request.start_time response['X-Response-Time'] = f"{duration:.2f}s" # 记录慢请求 if duration > 2: print(f"Slow request: {request.path} took {duration:.2f}s") return response注册中间件:
MIDDLEWARE = [ ... 'myapp.middleware.TimingMiddleware', ... ]10.3 REST API集成
结合Django REST Framework构建API:
# serializers.py from rest_framework import serializers from .models import Article class ArticleSerializer(serializers.ModelSerializer): author_name = serializers.CharField( source='author.name', read_only=True ) class Meta: model = Article fields = ['id', 'title', 'content', 'author_name'] read_only_fields = ['id'] # views.py from rest_framework import generics from .models import Article from .serializers import ArticleSerializer class ArticleListCreateView(generics.ListCreateAPIView): queryset = Article.objects.select_related('author') serializer_class = ArticleSerializer def perform_create(self, serializer): serializer.save(author=self.request.user) class ArticleRetrieveUpdateView(generics.RetrieveUpdateAPIView): queryset = Article.objects.all() serializer_class = ArticleSerializer10.4 实时功能扩展
使用Channels实现WebSocket:
# consumers.py from channels.generic.websocket import AsyncJsonWebsocketConsumer class ChatConsumer(AsyncJsonWebsocketConsumer): async def connect(self): self.room_name = self.scope['url_route']['kwargs']['room_name'] self.room_group_name = f'chat_{self.room_name}' await self.channel_layer.group_add( self.room_group_name, self.channel_name ) await self.accept() async def disconnect(self, close_code): await self.channel_layer.group_discard( self.room_group_name, self.channel_name ) async def receive_json(self, content): await self.channel_layer.group_send( self.room_group_name, { 'type': 'chat.message', 'message': content['message'], 'username': content['username'] } ) async def chat_message(self, event): await self.send_json({ 'message': event['message'], 'username': event['username'] })路由配置:
# routing.py from django.urls import re_path from . import consumers websocket_urlpatterns = [ re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()), ]11. 项目实战:博客系统开发
11.1 模型设计
from django.db import models from django.contrib.auth.models import User from django.urls import reverse from django.utils.text import slugify class Category(models.Model): name = models.CharField(max_length=50) slug = models.SlugField(unique=True) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) def __str__(self): return self.name class Tag(models.Model): name = models.CharField(max_length=30) slug = models.SlugField(unique=True) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) def __str__(self): return self.name class Post(models.Model): STATUS_CHOICES = ( ('draft', 'Draft'), ('published', 'Published'), ) title = models.CharField(max_length=200) slug = models.SlugField(unique_for_date='publish_date') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() excerpt = models.CharField(max_length=300, blank=True) status = models.CharField( max_length=10, choices=STATUS_CHOICES, default='draft' ) publish_date = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) category = models.ForeignKey( Category, on_delete=models.SET_NULL, null=True, related_name='posts' ) tags = models.ManyToManyField(Tag, related_name='posts', blank=True) featured_image = models.ImageField( upload_to='post_images/', null=True, blank=True ) class Meta: ordering = ['-publish_date'] indexes = [ models.Index(fields=['-publish_date']), ] def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) if not self.excerpt and self.content: self.excerpt = self.content[:300] + '...' super().save(*args, **kwargs) def get_absolute_url(self): return reverse('post_detail', kwargs={ 'year': self.publish_date.year, 'month': self.publish_date.month, 'day': self.publish_date.day, 'slug': self.slug }) def __str__(self): return self.title11.2 视图逻辑
from django.views.generic import ListView, DetailView from django.shortcuts import get_object_or_404 from .models import Post, Category, Tag from django.db.models import Count class PostListView(ListView): model = Post template_name = 'blog/post_list.html' context_object_name = 'posts' paginate_by = 10 def get_queryset(self): queryset = super().get_queryset() queryset = queryset.filter(status='published').select_related( 'author', 'category' ).prefetch_related('tags') # 分类过滤 category_slug = self.kwargs.get('category_slug') if category_slug: category = get_object_or_404(Category, slug=category_slug) queryset = queryset.filter(category=category) # 标签过滤 tag_slug = self.kwargs.get('tag_slug') if tag_slug: tag = get_object_or_404(Tag, slug=tag_slug) queryset = queryset.filter(tags=tag) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 添加分类和标签云 context['categories'] = Category.objects.annotate( post_count=Count('posts') ).filter(post_count__gt=0) context['tags'] = Tag.objects.annotate( post_count=Count('posts') ).filter(post_count__gt=0) # 添加当前分类/标签信息 if 'category_slug' in self.kwargs: context['current_category'] = get_object_or_404( Category, slug=self.kwargs['category_slug'] ) if 'tag_slug' in self.kwargs: context['current_tag'] = get_object_or_404( Tag, slug=self.kwargs['tag_slug'] ) return context class PostDetailView(DetailView): model = Post template_name = 'blog/post_detail.html' context_object_name = 'post' def get_queryset(self): return super().get_queryset().filter( status='published' ).select_related( 'author', 'category' ).prefetch_related('tags') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 相关文章 post = self.object context['related_posts'] = Post.objects.filter( status='published', category=post.category ).exclude( id=post.id )[:5] return context11.3 模板实现
base.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{% block title %}My Blog{% endblock %}</title> <link rel="stylesheet" href="{% static 'css/style.css' %}"> {% block extra_css %}{% endblock %} </head> <body> <header> <h1><a href="{% url 'post_list' %}">My Django Blog</a></h1> <nav> <ul> <li><a href="{% url 'post_list' %}">Home</a></li> {% for category in categories %} <li> <a href="{% url 'post_list_by_category' category.slug %}"> {{ category.name }} </a> </li> {% endfor %} </ul> </nav> </header> <main> {% block content %}{% endblock %} </main> <aside> <h3>Tags</h3> <ul class="tag-cloud"> {% for tag in tags %} <li> <a href="{% url 'post_list_by_tag' tag.slug %}" style="font-size: {{ tag.post_count|add:10 }}px"> {{ tag.name }} </a> </li> {% endfor %} </ul> </aside> <footer> <p>© {% now "Y" %} My Django Blog</p> </footer> <script src="{% static 'js/main.js' %}"></script> {% block extra_js %}{% endblock %} </body> </html>post_detail.html:
{% extends "base.html" %} {% block title %}{{ post.title }}{% endblock %} {% block content %} <article> <header> <h1>{{ post.title }}</h1> <div class="meta"> <span>Published on {{ post.publish_date|date:"F j, Y" }}</span> <span>by {{ post.author.username }}</span> <span>in <a href="{% url 'post_list_by_category' post.category.slug %}"> {{ post.category.name }} </a> </span> </div> {% if post.featured_image %} <img src="{{ post.featured_image.url }}" alt="{{ post.title }}"> {% endif %} </header> <div class="content"> {{ post.content|linebreaks }} </div> <footer> <div class="tags"> {% for tag in post.tags.all %} <a href="{% url 'post_list_by_tag' tag.slug %}" class="tag"> {{ tag.name }} </a> {% endfor %} </div> </footer> </article> <section class="related"> <h2>Related Posts</h2> <ul> {% for post in related_posts %} <li> <a href="{{ post.get_absolute_url }}">{{ post.title }}</a> </li> {% endfor %} </ul> </section> {% endblock %}12. 部署上线指南
12.1 生产环境配置
settings.py关键配置:
DEBUG = False ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com'] # 安全配置 SECURE_SSL_REDIRECT = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SECURE_HSTS_SECONDS = 31536000 # 1 year SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True # 静态文件 STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] # 媒体文件 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'mydatabase', 'USER': 'mydatabaseuser', 'PASSWORD': 'mypassword', 'HOST': 'localhost', 'PORT': '5432', } } # 缓存 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', } } # 日志 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': '/var/log/django/debug.log', }, }, 'loggers': { 'django': { 'handlers': ['file'], 'level': 'DEBUG', 'propagate': True, }, }, }12.2 Gunicorn配置
gunicorn.conf.py:
bind = "0.0.0.0:8000" workers = 3 worker_class = "gthread" threads = 3 max_requests = 1000 max_requests_jitter = 50 timeout = 30 keepalive