# 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 安装与创建项目

```bash
# 安装 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 创建应用

```bash
# 创建应用
python manage.py startapp blog

# 应用目录结构
blog/
    __init__.py
    admin.py         ← 后台配置
    apps.py          ← 应用配置
    models.py        ← 数据模型
    views.py         ← 视图函数
    tests.py         ← 测试
    migrations/      ← 数据库迁移
        __init__.py
```

```python
# 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 定义模型

```python
# 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 数据库迁移

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

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

# 执行迁移
python manage.py migrate

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

### 3.4 ORM 操作

```python
# 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 函数视图

```python
# 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 类视图

```python
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 配置

```python
# 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 模板语法

```html
<!-- 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 模板继承

```html
<!-- 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 常用模板标签和过滤器

```html
<!-- 变量 -->
{{ 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. 管理后台

```python
# 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. 表单处理

```python
# 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
    })
```

```html
<!-- 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 不同的设计理念。

