django-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDjango 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
undefinedbash
undefinedInstall 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
undefinedpython manage.py runserver
undefinedModels
模型
python
undefinedpython
undefinedmyapp/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}'undefinedfrom 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}'undefinedViews
视图
python
undefinedpython
undefinedmyapp/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)undefinedclass 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)undefinedURLs
URL 配置
python
undefinedpython
undefinedmyproject/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'),
]
undefinedfrom 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'),
]
undefinedForms
表单
python
undefinedpython
undefinedmyapp/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 slugclass CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
widgets = {
'content': forms.Textarea(attrs={'rows': 3}),
}
undefinedfrom 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 slugclass CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
widgets = {
'content': forms.Textarea(attrs={'rows': 3}),
}
undefinedDjango REST Framework
Django REST Framework
python
undefinedpython
undefinedInstall: 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)),
]
undefinedfrom rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
undefinedAdmin Interface
管理后台界面
python
undefinedpython
undefinedmyapp/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']
undefinedfrom 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']
undefinedAuthentication
认证系统
python
undefinedpython
undefinedviews.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'),
]
undefinedfrom 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'),
]
undefinedTesting
测试
python
undefinedpython
undefinedmyapp/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)undefinedfrom 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)undefinedBest 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
资源
- Django Docs: https://docs.djangoproject.com/
- Django REST Framework: https://www.django-rest-framework.org/
- Django Packages: https://djangopackages.org/
- Django 官方文档:https://docs.djangoproject.com/
- Django REST Framework:https://www.django-rest-framework.org/
- Django 第三方包平台:https://djangopackages.org/