django-expert

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Django Expert

Django 专家指南

Expert guidance for Django - high-level Python web framework for building secure, scalable web applications with batteries included.
为你提供Django的专业指导——这是一款功能完备的高级Python Web框架,用于构建安全、可扩展的Web应用。

Core Concepts

核心概念

Django Architecture

Django 架构

  • MVT (Model-View-Template) pattern
  • ORM (Object-Relational Mapping)
  • Admin interface
  • Authentication system
  • URL routing
  • Template engine
  • Forms and validation
  • MVT(模型-视图-模板)模式
  • ORM(对象关系映射)
  • 管理后台界面
  • 认证系统
  • URL路由
  • 模板引擎
  • 表单与验证

Key Components

关键组件

  • Models (database tables)
  • Views (business logic)
  • Templates (presentation)
  • URLs (routing)
  • Forms (user input)
  • Middleware (request/response processing)
  • Models(数据库表)
  • Views(业务逻辑)
  • Templates(视图层)
  • URLs(路由)
  • Forms(用户输入)
  • Middleware(请求/响应处理)

Project Setup

项目搭建

bash
undefined
bash
undefined

Install Django

Install Django

pip install django
pip install django

Create project

Create project

django-admin startproject myproject cd myproject
django-admin startproject myproject cd myproject

Create app

Create app

python manage.py startapp myapp
python manage.py startapp myapp

Run migrations

Run migrations

python manage.py migrate
python manage.py migrate

Create superuser

Create superuser

python manage.py createsuperuser
python manage.py createsuperuser

Run server

Run server

python manage.py runserver
undefined
python manage.py runserver
undefined

Models

模型

python
undefined
python
undefined

myapp/models.py

myapp/models.py

from django.db import models from django.contrib.auth.models import User from django.utils import timezone
class Post(models.Model): STATUS_CHOICES = [ ('draft', 'Draft'), ('published', 'Published'), ]
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
content = models.TextField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(null=True, blank=True)

class Meta:
    ordering = ['-created_at']
    indexes = [
        models.Index(fields=['-created_at']),
        models.Index(fields=['slug']),
    ]

def __str__(self):
    return self.title

def save(self, *args, **kwargs):
    if self.status == 'published' and not self.published_at:
        self.published_at = timezone.now()
    super().save(*args, **kwargs)
class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True)
class Meta:
    ordering = ['created_at']

def __str__(self):
    return f'Comment by {self.author} on {self.post}'
undefined
from django.db import models from django.contrib.auth.models import User from django.utils import timezone
class Post(models.Model): STATUS_CHOICES = [ ('draft', 'Draft'), ('published', 'Published'), ]
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
content = models.TextField()
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(null=True, blank=True)

class Meta:
    ordering = ['-created_at']
    indexes = [
        models.Index(fields=['-created_at']),
        models.Index(fields=['slug']),
    ]

def __str__(self):
    return self.title

def save(self, *args, **kwargs):
    if self.status == 'published' and not self.published_at:
        self.published_at = timezone.now()
    super().save(*args, **kwargs)
class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True)
class Meta:
    ordering = ['created_at']

def __str__(self):
    return f'Comment by {self.author} on {self.post}'
undefined

Views

视图

python
undefined
python
undefined

myapp/views.py

myapp/views.py

from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.views.generic import ListView, DetailView, CreateView from django.contrib import messages from .models import Post, Comment from .forms import PostForm, CommentForm
from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.views.generic import ListView, DetailView, CreateView from django.contrib import messages from .models import Post, Comment from .forms import PostForm, CommentForm

Function-based view

基于函数的视图

def post_list(request): posts = Post.objects.filter(status='published').select_related('author') return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, slug): post = get_object_or_404(Post, slug=slug, status='published') comments = post.comments.select_related('author')
if request.method == 'POST':
    form = CommentForm(request.POST)
    if form.is_valid():
        comment = form.save(commit=False)
        comment.post = post
        comment.author = request.user
        comment.save()
        messages.success(request, 'Comment added successfully')
        return redirect('post_detail', slug=slug)
else:
    form = CommentForm()

return render(request, 'blog/post_detail.html', {
    'post': post,
    'comments': comments,
    'form': form
})
def post_list(request): posts = Post.objects.filter(status='published').select_related('author') return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, slug): post = get_object_or_404(Post, slug=slug, status='published') comments = post.comments.select_related('author')
if request.method == 'POST':
    form = CommentForm(request.POST)
    if form.is_valid():
        comment = form.save(commit=False)
        comment.post = post
        comment.author = request.user
        comment.save()
        messages.success(request, 'Comment added successfully')
        return redirect('post_detail', slug=slug)
else:
    form = CommentForm()

return render(request, 'blog/post_detail.html', {
    'post': post,
    'comments': comments,
    'form': form
})

Class-based views

基于类的视图

class PostListView(ListView): model = Post template_name = 'blog/post_list.html' context_object_name = 'posts' paginate_by = 10
def get_queryset(self):
    return Post.objects.filter(status='published').select_related('author')
class PostDetailView(DetailView): model = Post template_name = 'blog/post_detail.html' context_object_name = 'post'
def get_queryset(self):
    return Post.objects.filter(status='published')
class PostCreateView(CreateView): model = Post form_class = PostForm template_name = 'blog/post_form.html'
def form_valid(self, form):
    form.instance.author = self.request.user
    return super().form_valid(form)
undefined
class PostListView(ListView): model = Post template_name = 'blog/post_list.html' context_object_name = 'posts' paginate_by = 10
def get_queryset(self):
    return Post.objects.filter(status='published').select_related('author')
class PostDetailView(DetailView): model = Post template_name = 'blog/post_detail.html' context_object_name = 'post'
def get_queryset(self):
    return Post.objects.filter(status='published')
class PostCreateView(CreateView): model = Post form_class = PostForm template_name = 'blog/post_form.html'
def form_valid(self, form):
    form.instance.author = self.request.user
    return super().form_valid(form)
undefined

URLs

URL 配置

python
undefined
python
undefined

myproject/urls.py

myproject/urls.py

from django.contrib import admin from django.urls import path, include
urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), ]
from django.contrib import admin from django.urls import path, include
urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), ]

myapp/urls.py

myapp/urls.py

from django.urls import path from . import views
app_name = 'blog'
urlpatterns = [ path('', views.PostListView.as_view(), name='post_list'), path('post/slug:slug/', views.post_detail, name='post_detail'), path('post/create/', views.PostCreateView.as_view(), name='post_create'), ]
undefined
from django.urls import path from . import views
app_name = 'blog'
urlpatterns = [ path('', views.PostListView.as_view(), name='post_list'), path('post/slug:slug/', views.post_detail, name='post_detail'), path('post/create/', views.PostCreateView.as_view(), name='post_create'), ]
undefined

Forms

表单

python
undefined
python
undefined

myapp/forms.py

myapp/forms.py

from django import forms from .models import Post, Comment
class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'slug', 'content', 'status'] widgets = { 'content': forms.Textarea(attrs={'rows': 10}), }
def clean_slug(self):
    slug = self.cleaned_data['slug']
    if Post.objects.filter(slug=slug).exists():
        raise forms.ValidationError('Slug already exists')
    return slug
class CommentForm(forms.ModelForm): class Meta: model = Comment fields = ['content'] widgets = { 'content': forms.Textarea(attrs={'rows': 3}), }
undefined
from django import forms from .models import Post, Comment
class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'slug', 'content', 'status'] widgets = { 'content': forms.Textarea(attrs={'rows': 10}), }
def clean_slug(self):
    slug = self.cleaned_data['slug']
    if Post.objects.filter(slug=slug).exists():
        raise forms.ValidationError('Slug already exists')
    return slug
class CommentForm(forms.ModelForm): class Meta: model = Comment fields = ['content'] widgets = { 'content': forms.Textarea(attrs={'rows': 3}), }
undefined

Django REST Framework

Django REST Framework

python
undefined
python
undefined

Install: pip install djangorestframework

Install: pip install djangorestframework

settings.py

settings.py

INSTALLED_APPS = [ ... 'rest_framework', ]
INSTALLED_APPS = [ ... 'rest_framework', ]

serializers.py

serializers.py

from rest_framework import serializers from .models import Post, Comment
class CommentSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField()
class Meta:
    model = Comment
    fields = ['id', 'author', 'content', 'created_at']
class PostSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField() comments = CommentSerializer(many=True, read_only=True)
class Meta:
    model = Post
    fields = ['id', 'title', 'slug', 'author', 'content',
              'status', 'created_at', 'comments']
from rest_framework import serializers from .models import Post, Comment
class CommentSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField()
class Meta:
    model = Comment
    fields = ['id', 'author', 'content', 'created_at']
class PostSerializer(serializers.ModelSerializer): author = serializers.StringRelatedField() comments = CommentSerializer(many=True, read_only=True)
class Meta:
    model = Post
    fields = ['id', 'title', 'slug', 'author', 'content',
              'status', 'created_at', 'comments']

views.py (API)

views.py (API)

from rest_framework import viewsets, permissions from rest_framework.decorators import action from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.filter(status='published') serializer_class = PostSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
    serializer.save(author=self.request.user)

@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
    post = self.get_object()
    post.status = 'published'
    post.save()
    return Response({'status': 'published'})
from rest_framework import viewsets, permissions from rest_framework.decorators import action from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet): queryset = Post.objects.filter(status='published') serializer_class = PostSerializer permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
    serializer.save(author=self.request.user)

@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
    post = self.get_object()
    post.status = 'published'
    post.save()
    return Response({'status': 'published'})

urls.py (API)

urls.py (API)

from rest_framework.routers import DefaultRouter
router = DefaultRouter() router.register(r'posts', PostViewSet)
urlpatterns = [ path('api/', include(router.urls)), ]
undefined
from rest_framework.routers import DefaultRouter
router = DefaultRouter() router.register(r'posts', PostViewSet)
urlpatterns = [ path('api/', include(router.urls)), ]
undefined

Admin Interface

管理后台界面

python
undefined
python
undefined

myapp/admin.py

myapp/admin.py

from django.contrib import admin from .models import Post, Comment
@admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'author', 'status', 'created_at'] list_filter = ['status', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields = {'slug': ('title',)} date_hierarchy = 'created_at'
actions = ['make_published']

def make_published(self, request, queryset):
    queryset.update(status='published')
make_published.short_description = "Mark selected as published"
@admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ['post', 'author', 'created_at'] list_filter = ['created_at'] search_fields = ['content']
undefined
from django.contrib import admin from .models import Post, Comment
@admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'author', 'status', 'created_at'] list_filter = ['status', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields = {'slug': ('title',)} date_hierarchy = 'created_at'
actions = ['make_published']

def make_published(self, request, queryset):
    queryset.update(status='published')
make_published.short_description = "Mark selected as published"
@admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ['post', 'author', 'created_at'] list_filter = ['created_at'] search_fields = ['content']
undefined

Authentication

认证系统

python
undefined
python
undefined

views.py

views.py

from django.contrib.auth import login, logout from django.contrib.auth.forms import UserCreationForm
def signup(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) return redirect('home') else: form = UserCreationForm() return render(request, 'registration/signup.html', {'form': form})
from django.contrib.auth import login, logout from django.contrib.auth.forms import UserCreationForm
def signup(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) return redirect('home') else: form = UserCreationForm() return render(request, 'registration/signup.html', {'form': form})

URLs

URLs

from django.contrib.auth import views as auth_views
urlpatterns = [ path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('signup/', signup, name='signup'), ]
undefined
from django.contrib.auth import views as auth_views
urlpatterns = [ path('login/', auth_views.LoginView.as_view(), name='login'), path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('signup/', signup, name='signup'), ]
undefined

Testing

测试

python
undefined
python
undefined

myapp/tests.py

myapp/tests.py

from django.test import TestCase, Client from django.contrib.auth.models import User from .models import Post
class PostModelTest(TestCase): def setUp(self): self.user = User.objects.create_user(username='test', password='test') self.post = Post.objects.create( title='Test Post', slug='test-post', author=self.user, content='Test content', status='published' )
def test_post_creation(self):
    self.assertEqual(self.post.title, 'Test Post')
    self.assertEqual(str(self.post), 'Test Post')

def test_post_slug_unique(self):
    with self.assertRaises(Exception):
        Post.objects.create(
            title='Another Post',
            slug='test-post',  # Duplicate slug
            author=self.user,
            content='Content'
        )
class PostViewTest(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create_user(username='test', password='test')
def test_post_list_view(self):
    response = self.client.get('/')
    self.assertEqual(response.status_code, 200)

def test_post_create_requires_auth(self):
    response = self.client.get('/post/create/')
    self.assertEqual(response.status_code, 302)  # Redirect to login

    self.client.login(username='test', password='test')
    response = self.client.get('/post/create/')
    self.assertEqual(response.status_code, 200)
undefined
from django.test import TestCase, Client from django.contrib.auth.models import User from .models import Post
class PostModelTest(TestCase): def setUp(self): self.user = User.objects.create_user(username='test', password='test') self.post = Post.objects.create( title='Test Post', slug='test-post', author=self.user, content='Test content', status='published' )
def test_post_creation(self):
    self.assertEqual(self.post.title, 'Test Post')
    self.assertEqual(str(self.post), 'Test Post')

def test_post_slug_unique(self):
    with self.assertRaises(Exception):
        Post.objects.create(
            title='Another Post',
            slug='test-post',  # Duplicate slug
            author=self.user,
            content='Content'
        )
class PostViewTest(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create_user(username='test', password='test')
def test_post_list_view(self):
    response = self.client.get('/')
    self.assertEqual(response.status_code, 200)

def test_post_create_requires_auth(self):
    response = self.client.get('/post/create/')
    self.assertEqual(response.status_code, 302)  # Redirect to login

    self.client.login(username='test', password='test')
    response = self.client.get('/post/create/')
    self.assertEqual(response.status_code, 200)
undefined

Best Practices

最佳实践

  • Use select_related/prefetch_related to avoid N+1 queries
  • Implement proper validation in forms
  • Use class-based views for common patterns
  • Protect against CSRF attacks (enabled by default)
  • Use Django's built-in authentication
  • Implement proper permission checks
  • Add database indexes for frequently queried fields
  • Use Django migrations for schema changes
  • 使用select_related/prefetch_related避免N+1查询
  • 在表单中实现恰当的验证
  • 针对常见模式使用基于类的视图
  • 防范CSRF攻击(默认已启用)
  • 使用Django内置的认证系统
  • 实现恰当的权限校验
  • 为频繁查询的字段添加数据库索引
  • 使用Django迁移管理数据库架构变更

Resources

资源