目录

Python Django 框架入门

学习目标:掌握 Django 的核心概念,能够创建包含模型、视图、模板的完整 Web 应用,理解 MTV 架构模式。


1. Django 简介

Django 是 Python 最流行的全栈 Web 框架,遵循 " batteries included “(自带电池)哲学,提供从数据库到管理后台的完整解决方案。

1.1 核心特性

  • ORM:对象关系映射,无需手写 SQL
  • 管理后台:自动生成的 admin 界面
  • 表单处理:数据验证和 HTML 表单生成
  • 认证系统:用户认证、权限管理
  • 模板引擎:强大的 HTML 模板系统
  • 安全特性:CSRF 保护、SQL 注入防护等

1.2 MTV 架构

Django 使用 MTV(Model-Template-View)架构:

用户请求 → URLconf → View → Model(数据库)
                              ↓
                        Template(HTML)
                              ↓
                        响应用户
组件 职责 类比 MVC
Model 数据结构和业务逻辑 Model
Template 页面展示逻辑 View
View 处理请求,协调 Model 和 Template Controller

2. 快速开始

2.1 安装与创建项目

# 安装 Django
pip install django

# 创建项目
django-admin startproject mysite

# 目录结构
mysite/
    manage.py          ← 命令行工具
    mysite/            ← 项目配置
        __init__.py
        settings.py    ← 配置文件
        urls.py        ← URL 声明
        asgi.py        ← ASGI 入口
        wsgi.py        ← WSGI 入口

# 启动开发服务器
cd mysite
python manage.py runserver

2.2 创建应用

# 创建应用
python manage.py startapp blog

# 应用目录结构
blog/
    __init__.py
    admin.py         ← 后台配置
    apps.py          ← 应用配置
    models.py        ← 数据模型
    views.py         ← 视图函数
    tests.py         ← 测试
    migrations/      ← 数据库迁移
        __init__.py
# mysite/settings.py - 注册应用
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # ← 添加自定义应用
]

3. 模型(Model)

3.1 定义模型

# blog/models.py
from django.db import models

class Article(models.Model):
    """文章模型"""
    title = models.CharField('标题', max_length=200)
    content = models.TextField('内容')
    author = models.CharField('作者', max_length=50)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('更新时间', auto_now=True)
    is_published = models.BooleanField('已发布', default=False)
    
    class Meta:
        db_table = 'articles'
        ordering = ['-created_at']
        verbose_name = '文章'
        verbose_name_plural = '文章'
    
    def __str__(self):
        return self.title

class Comment(models.Model):
    """评论模型"""
    article = models.ForeignKey(
        Article, 
        on_delete=models.CASCADE,
        related_name='comments',
        verbose_name='文章'
    )
    author = models.CharField('作者', max_length=50)
    content = models.TextField('内容')
    created_at = models.DateTimeField('创建时间', auto_now_add=True)
    
    class Meta:
        ordering = ['-created_at']

3.2 常用字段类型

字段类型 说明 示例
CharField 短文本 title = CharField(max_length=100)
TextField 长文本 content = TextField()
IntegerField 整数 age = IntegerField()
DecimalField 精确小数 price = DecimalField(max_digits=10, decimal_places=2)
DateTimeField 日期时间 created = DateTimeField(auto_now_add=True)
BooleanField 布尔值 is_active = BooleanField(default=True)
ForeignKey 外键 author = ForeignKey(User, on_delete=CASCADE)
ManyToManyField 多对多 tags = ManyToManyField(Tag)
OneToOneField 一对一 profile = OneToOneField(Profile)

3.3 数据库迁移

# 生成迁移文件
python manage.py makemigrations

# 查看迁移 SQL
python manage.py sqlmigrate blog 0001

# 执行迁移
python manage.py migrate

# 创建超级用户
python manage.py createsuperuser

3.4 ORM 操作

# shell 中练习: python manage.py shell
from blog.models import Article

# 创建
article = Article.objects.create(
    title='Django 入门',
    content='Django 是 Python 最好的 Web 框架...',
    author='张三'
)

# 查询全部
articles = Article.objects.all()

# 条件查询
published = Article.objects.filter(is_published=True)
recent = Article.objects.filter(created_at__year=2024)

# 获取单个
article = Article.objects.get(id=1)

# 模糊查询
results = Article.objects.filter(title__contains='Django')

# 排序
articles = Article.objects.order_by('-created_at')

# 分页
page = Article.objects.all()[0:10]  # 前 10 条

# 更新
article.title = '新标题'
article.save()

# 批量更新
Article.objects.filter(is_published=False).update(is_published=True)

# 删除
article.delete()

# 计数
count = Article.objects.count()

# 链式调用
recent_published = Article.objects.filter(
    is_published=True
).order_by('-created_at')[:5]

4. 视图(View)

4.1 函数视图

# blog/views.py
from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse
from .models import Article

def article_list(request):
    """文章列表"""
    articles = Article.objects.filter(
        is_published=True
    ).order_by('-created_at')
    
    return render(request, 'blog/article_list.html', {
        'articles': articles
    })

def article_detail(request, article_id):
    """文章详情"""
    article = get_object_or_404(Article, id=article_id)
    
    return render(request, 'blog/article_detail.html', {
        'article': article
    })

def article_api(request):
    """文章 API"""
    articles = Article.objects.filter(is_published=True)
    data = [{
        'id': a.id,
        'title': a.title,
        'author': a.author,
        'created_at': a.created_at.isoformat()
    } for a in articles]
    
    return JsonResponse({'articles': data})

4.2 类视图

from django.views import View
from django.views.generic import ListView, DetailView
from .models import Article

class ArticleListView(ListView):
    """文章列表视图(通用类视图)"""
    model = Article
    template_name = 'blog/article_list.html'
    context_object_name = 'articles'
    
    def get_queryset(self):
        return Article.objects.filter(is_published=True)

class ArticleDetailView(DetailView):
    """文章详情视图"""
    model = Article
    template_name = 'blog/article_detail.html'
    context_object_name = 'article'
    pk_url_kwarg = 'article_id'

class ArticleAPIView(View):
    """API 类视图"""
    
    def get(self, request):
        articles = Article.objects.all()
        data = [{'id': a.id, 'title': a.title} for a in articles]
        return JsonResponse({'articles': data})
    
    def post(self, request):
        # 处理 POST 请求
        return JsonResponse({'status': 'created'})

5. URL 配置

# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.article_list, name='article_list'),
    path('article/<int:article_id>/', views.article_detail, name='article_detail'),
    path('api/articles/', views.article_api, name='article_api'),
]

# mysite/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]

6. 模板(Template)

6.1 模板语法

<!-- blog/templates/blog/article_list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>文章列表</title>
</head>
<body>
    <h1>最新文章</h1>
    
    <!-- 循环 -->
    {% for article in articles %}
        <article>
            <h2>
                <a href="{% url 'blog:article_detail' article.id %}">
                    {{ article.title }}
                </a>
            </h2>
            <p>作者: {{ article.author }}</p>
            <p>发布时间: {{ article.created_at|date:"Y-m-d H:i" }}</p>
            <p>{{ article.content|truncatewords:30 }}</p>
        </article>
    {% empty %}
        <p>暂无文章</p>
    {% endfor %}
    
    <!-- 条件判断 -->
    {% if articles %}
        <p>共 {{ articles|length }} 篇文章</p>
    {% else %}
        <p>文章列表为空</p>
    {% endif %}
</body>
</html>

6.2 模板继承

<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}我的博客{% endblock %}</title>
</head>
<body>
    <nav>
        <a href="{% url 'blog:article_list' %}">首页</a>
    </nav>
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <footer>
        <p> 2024 我的博客</p>
    </footer>
</body>
</html>

<!-- blog/templates/blog/article_list.html -->
{% extends 'base.html' %}

{% block title %}文章列表 - 我的博客{% endblock %}

{% block content %}
    <h1>文章列表</h1>
    {% for article in articles %}
        <article>
            <h2>{{ article.title }}</h2>
            <p>{{ article.content|truncatewords:50 }}</p>
        </article>
    {% endfor %}
{% endblock %}

6.3 常用模板标签和过滤器

<!-- 变量 -->
{{ variable }}
{{ variable|default:"默认值" }}

<!-- 过滤器 -->
{{ name|upper }}
{{ content|truncatewords:50 }}
{{ date|date:"Y-m-d" }}
{{ items|length }}
{{ html_content|safe }}

<!-- 逻辑控制 -->
{% if user.is_authenticated %}
    <p>欢迎, {{ user.username }}</p>
{% else %}
    <p>请登录</p>
{% endif %}

<!-- 循环 -->
{% for item in items %}
    {{ forloop.counter }}. {{ item }}
{% endfor %}

<!-- 包含模板 -->
{% include "partial/header.html" %}

<!-- 静态文件 -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">

7. 管理后台

# blog/admin.py
from django.contrib import admin
from .models import Article, Comment

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    """文章后台管理"""
    list_display = ['title', 'author', 'is_published', 'created_at']
    list_filter = ['is_published', 'created_at']
    search_fields = ['title', 'content']
    date_hierarchy = 'created_at'
    ordering = ['-created_at']
    
    fieldsets = (
        ('基本信息', {
            'fields': ('title', 'author')
        }),
        ('内容', {
            'fields': ('content',)
        }),
        ('状态', {
            'fields': ('is_published',)
        })
    )

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ['article', 'author', 'created_at']
    list_filter = ['created_at']

8. 表单处理

# blog/forms.py
from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):
    """文章表单"""
    class Meta:
        model = Article
        fields = ['title', 'content', 'author', 'is_published']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'rows': 10}),
        }

# blog/views.py
from django.shortcuts import redirect
from .forms import ArticleForm

def article_create(request):
    """创建文章"""
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save()
            return redirect('blog:article_detail', article_id=article.id)
    else:
        form = ArticleForm()
    
    return render(request, 'blog/article_form.html', {
        'form': form
    })
<!-- blog/templates/blog/article_form.html -->
{% extends 'base.html' %}

{% block content %}
    <h1>写文章</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">保存</button>
    </form>
{% endblock %}

9. 小结

组件 文件 作用
Model models.py 定义数据结构
View views.py 处理请求逻辑
Template templates/ 渲染 HTML
URL urls.py 路由映射
Admin admin.py 后台管理配置
Form forms.py 数据验证和表单

10. 练习题

  1. 创建一个完整的 Django 博客应用,包含文章的增删改查功能。
  2. 为博客添加分类(Category)模型,实现文章分类功能。
  3. 使用 Django 的认证系统,实现用户注册和登录。
  4. 自定义管理后台,为文章列表添加批量发布功能。

下节预告:我们将学习 Flask,这个轻量灵活的微框架,理解其与 Django 不同的设计理念。