Django Hello World —— 你的第一个 Django 项目
目标
创建 Django 项目与第一个应用,理解:
- Django 项目 vs 应用的区别
urls.py 路由与 views.py 视图的关系
- 模板渲染与动态数据传递
manage.py 命令行工具
完整步骤
步骤 1:创建项目
django-admin startproject mysite
cd mysite
生成的结构:
mysite/
├── manage.py # Django 命令行工具
└── mysite/
├── __init__.py
├── settings.py # 全局配置
├── urls.py # 根 URL 路由
├── asgi.py # ASGI 入口
└── wsgi.py # WSGI 入口
步骤 2:创建应用
python manage.py startapp hello
hello/
├── __init__.py
├── admin.py # 管理后台注册
├── apps.py # 应用配置
├── models.py # 数据库模型
├── views.py # 视图函数/类
├── urls.py # 应用路由(需手动创建)
├── tests.py # 测试
└── migrations/ # 数据库迁移
步骤 3:编写视图
# hello/views.py
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.views import View
from datetime import datetime
# 方式一:函数视图(最简单)
def index(request):
"""首页 —— 返回纯文本"""
return HttpResponse("""
<h1>🚀 Hello, Django!</h1>
<ul>
<li><a href='/hello/'>/hello/</a> — 问候</li>
<li><a href='/hello/?name=Vibe'>/hello/?name=Vibe</a> — 带参数</li>
<li><a href='/api/status/'>/api/status/</a> — JSON 接口</li>
</ul>
""")
def hello(request):
"""查询参数问候"""
name = request.GET.get("name", "World")
return HttpResponse(f"Hello, {name}!")
def status(request):
"""JSON 响应"""
return JsonResponse({
"status": "ok",
"framework": "Django",
"version": "5.0",
"server_time": datetime.now().isoformat(),
})
# 方式二:类视图(适合 RESTful)
class HelloView(View):
def get(self, request):
name = request.GET.get("name", "World")
return JsonResponse({"message": f"Hello, {name}!", "method": "GET"})
def post(self, request):
import json
try:
data = json.loads(request.body)
name = data.get("name", "World")
except json.JSONDecodeError:
name = "World"
return JsonResponse({"message": f"Hello, {name}!", "method": "POST"}, status=201)
步骤 4:配置路由
# hello/urls.py(新建此文件)
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("hello/", views.hello, name="hello"),
path("api/status/", views.status, name="status"),
# 类视图用 as_view()
path("api/hello/", views.HelloView.as_view(), name="hello_api"),
]
# mysite/urls.py(修改根路由,include 应用路由)
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("hello.urls")), # 引入应用路由
]
步骤 5:注册应用
# mysite/settings.py
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"hello", # ← 添加此行
]
步骤 6:运行
python manage.py runserver
预期输出
GET /api/status/ 返回:
{
"status": "ok",
"framework": "Django",
"version": "5.0",
"server_time": "2025-01-15T10:30:00.123456"
}
POST /api/hello/ 返回:
{"message": "Hello, Django!", "method": "POST"}
Django 请求处理流程
浏览器请求
↓
URL Dispatcher(urls.py)—— 匹配路由
↓
View(views.py) —— 处理业务逻辑
↓
Model(models.py)—— 操作数据库(可选)
↓
Template(templates/)—— 渲染 HTML(可选)
↓
HttpResponse —— 返回给浏览器
关键要点
| 概念 |
说明 |
| 项目 vs 应用 |
项目 = 整个网站;应用 = 一个功能模块(如博客、用户) |
urlpatterns |
URL 路由列表,从上到下匹配,先匹配先服务 |
path(str, view, name) |
路由定义:URL模式 → 视图 → 命名(用于反向解析) |
include() |
将子路由表插入到当前路由,实现模块化 |
request.GET |
查询参数字典(QueryDict) |
JsonResponse |
自动 JSON 序列化 + Content-Type: application/json |
Django REST Framework — 构建生产级 RESTful API
目标
使用 Django REST Framework (DRF) 构建完整的 RESTful API,实现对"文章"资源的 CRUD:
- 理解 Model → Serializer → ViewSet → Router 的 DRF 管线
- 掌握序列化器(Serializer)的验证与输出控制
- 掌握 ViewSet 与 GenericAPIView 的使用
- 实现分页、过滤、搜索、排序
完整代码
步骤 1:安装 DRF
pip install djangorestframework django-filter
步骤 2:配置
# mysite/settings.py
INSTALLED_APPS = [
# ... Django 默认应用
"rest_framework",
"django_filters",
"blog", # 我们的应用
]
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 10,
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework.filters.SearchFilter",
"rest_framework.filters.OrderingFilter",
],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticatedOrReadOnly",
],
}
步骤 3:定义模型
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
"""文章分类"""
name = models.CharField("分类名", max_length=100, unique=True)
slug = models.SlugField(max_length=100, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = "categories"
def __str__(self):
return self.name
class Post(models.Model):
"""博客文章"""
STATUS_CHOICES = [
("draft", "草稿"),
("published", "已发布"),
("archived", "已归档"),
]
title = models.CharField("标题", max_length=200)
slug = models.SlugField(max_length=200, unique=True)
content = models.TextField("内容")
status = models.CharField("状态", max_length=20, choices=STATUS_CHOICES, default="draft")
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name="posts")
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
views_count = models.IntegerField("浏览量", default=0)
created_at = models.DateTimeField("创建时间", auto_now_add=True)
updated_at = models.DateTimeField("更新时间", auto_now=True)
class Meta:
ordering = ["-created_at"]
def __str__(self):
return self.title
步骤 4:编写序列化器
# blog/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import Post, Category
class CategorySerializer(serializers.ModelSerializer):
post_count = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ["id", "name", "slug", "post_count", "created_at"]
read_only_fields = ["id", "created_at"]
def get_post_count(self, obj):
return obj.posts.count()
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "email"]
class PostListSerializer(serializers.ModelSerializer):
"""列表用 —— 只返回摘要,不包含全文"""
author = UserSerializer(read_only=True)
category_name = serializers.CharField(source="category.name", read_only=True)
class Meta:
model = Post
fields = ["id", "title", "slug", "status", "author", "category_name",
"views_count", "created_at"]
read_only_fields = ["id", "author", "views_count", "created_at"]
class PostDetailSerializer(serializers.ModelSerializer):
"""详情用 —— 返回完整内容"""
author = UserSerializer(read_only=True)
category = CategorySerializer(read_only=True)
category_id = serializers.PrimaryKeyRelatedField(
queryset=Category.objects.all(), source="category", write_only=True
)
class Meta:
model = Post
fields = "__all__"
read_only_fields = ["id", "author", "views_count", "slug", "created_at", "updated_at"]
def validate_title(self, value):
"""自定义字段级验证"""
if len(value) < 3:
raise serializers.ValidationError("标题至少需要3个字符")
return value
步骤 5:编写 ViewSet
# blog/views.py
from rest_framework import viewsets, permissions, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from .models import Post, Category
from .serializers import (
PostListSerializer, PostDetailSerializer, CategorySerializer
)
class PostViewSet(viewsets.ModelViewSet):
"""文章 CRUD ViewSet"""
queryset = Post.objects.select_related("author", "category").all()
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ["status", "category__slug"]
search_fields = ["title", "content"]
ordering_fields = ["created_at", "views_count", "title"]
def get_serializer_class(self):
if self.action == "list":
return PostListSerializer
return PostDetailSerializer
def get_permissions(self):
if self.action in ["create", "update", "partial_update", "destroy"]:
return [permissions.IsAuthenticated()]
return [permissions.AllowAny()]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# 自定义 action
@action(detail=True, methods=["post"])
def increment_views(self, request, pk=None):
"""自定义动作:增加浏览量"""
post = self.get_object()
post.views_count += 1
post.save(update_fields=["views_count"])
return Response({"views_count": post.views_count})
@action(detail=False, methods=["get"])
def my_posts(self, request):
"""获取当前用户的文章"""
posts = self.queryset.filter(author=request.user)
serializer = self.get_serializer(posts, many=True)
return Response(serializer.data)
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
"""分类 —— 只读"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
lookup_field = "slug" # 用 slug 而非 id 查找
步骤 6:配置路由
# blog/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r"posts", views.PostViewSet, basename="post")
router.register(r"categories", views.CategoryViewSet, basename="category")
urlpatterns = [
path("api/", include(router.urls)),
]
# mysite/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("blog.urls")),
]
步骤 7:迁移与运行
python manage.py makemigrations blog
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
API 端点一览
| 方法 |
URL |
说明 |
| GET |
/api/posts/ |
文章列表(分页+过滤+搜索) |
| POST |
/api/posts/ |
创建文章(需认证) |
| GET |
/api/posts/{id}/ |
文章详情 |
| PUT |
/api/posts/{id}/ |
全量更新(需认证) |
| PATCH |
/api/posts/{id}/ |
部分更新(需认证) |
| DELETE |
/api/posts/{id}/ |
删除(需认证) |
| POST |
/api/posts/{id}/increment_views/ |
自增浏览量 |
| GET |
/api/posts/my_posts/ |
我的文章(需认证) |
| GET |
/api/categories/ |
分类列表 |
| GET |
/api/categories/{slug}/ |
分类详情 |
测试命令
curl http:
curl "http://127.0.0.1:8000/api/posts/?status=published&search=Django&ordering=-views_count"
curl -X POST http:
-H "Content-Type: application/json" \
-u admin:password \
-d '{"title":"Hello DRF","content":"# 我的第一篇文章","status":"published","category_id":1}'
关键要点
| 概念 |
说明 |
ModelSerializer |
根据模型自动生成序列化字段,减少样板代码 |
ModelViewSet |
一次性提供 list/create/retrieve/update/destroy 全套 |
@action |
在 ViewSet 上添加自定义端点 |
select_related |
预加载外键关联,避免 N+1 查询 |
lookup_field |
将默认的 pk 查找改为其他字段(如 slug) |
DefaultRouter |
自动按 REST 惯例生成 URL 路由 |