Django 框架基础使用

项目创建

django-admin startproject命令创建新项目,并进入该文件夹。

1
2
django-admin startproject locallibrary
cd locallibrary

django-admin工具会创建如下所示的文件夹结构:

1
2
3
4
5
6
locallibrary/
    manage.py
    locallibrary/
        settings.py
        urls.py
        wsgi.py

locallibrary 项目的子文件夹是整个网站的进入点:
settings.py 包含所有的网站设置。这是可以注册所有创建的应用的地方,也是静态文件,数据库配置的地方,等等。
urls.py 定义了网站 url 到 view 的映射。虽然这里可以包含所有的 url,但是更常见的做法是把应用相关的 url 包含在相关应用中
wsgi.py 帮助 Django 应用和网络服务器间的通讯。你可以把这个当作模板。
manage.py脚本可以创建应用,和数据库通讯,启动开发用网络服务器。

创建catalog

1
python3 manage.py startapp catalog

绝大多数文件的命令和它们的目的有关(比如视图函数就是views.py,模型就是models.py,测试是tests.py,网站管理设置是admin.py,注册应用是apps.py)
一个migration文件夹,用来存储“migrations”——当你修改你的数据模型时,这个文件会自动升级你的数据库。
init.py — 一个空文件,Django/Python 会将这个文件作为Python 包并允许你在项目的其他部分使用它。

注册 catalog

打开项目设置文件 locallibrary/locallibrary/settings.py 找到 INSTALLED_APPS 列表里的定义。如下所示,在列表的最后添加新的一行。

1
2
3
4
5
6
7
8
9
10
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'catalog.apps.CatalogConfig',

]

配置数据库

settings.py 里面设置

1
2
3
4
5
6
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}

链接 url 映射器

打开locallibrary/locallibrary/urls.py 并注意指导文字解释了一些使用 URL 映射器的方法。

1
2
3
4
5
6
7
# URL 映射通过urlpatterns 变量管理,它是path() 函数的一个 Python 列表结构
urlpatterns = [
    path('admin/', admin.site.urls),
    # 它将带有 catalog/ 的请求转发到模块 catalog.urls (使用相对路径 URL /catalog/urls.py)
    path('catalog/', include('catalog.urls')),
    path('', RedirectView.as_view(url='/catalog/', permanent=True)),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

数据库迁移

1
2
python3 manage.py makemigrations
python3 manage.py migrate

ORM

在 Django 框架中,模型(Model) 是用于定义和管理应用程序的数据结构的核心组件。模型负责与数据库进行交互,定义数据的字段、行为以及数据之间的关系。通过使用 Django 的 ORM(对象关系映射),开发者可以方便地进行数据库操作,而无需编写复杂的 SQL 语句。

1. 模型的基本概念

Django 的模型是继承自 django.db.models.Model 的 Python 类。每个模型类对应数据库中的一张表,类的属性对应表中的字段。

1
2
3
4
5
6
7
8
from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    def __str__(self):
        return self.name

在这个示例中,Author 模型有两个字段:nameemail。Django 会自动为该模型创建一个名为 id 的主键字段,除非你手动指定其他主键。

2. 常用字段类型

Django 提供了丰富的字段类型来定义模型中的数据。以下是一些常用的字段类型:

  • CharField:用于存储短文本,需要指定 max_length 参数。
  • TextField:用于存储长文本。
  • IntegerField:用于存储整数。
  • FloatField:用于存储浮点数。
  • BooleanField:用于存储布尔值。
  • DateFieldDateTimeField:用于存储日期和日期时间。
  • EmailField:用于存储电子邮件地址。
  • URLField:用于存储 URL。
    1
    2
    3
    4
    5
    6
    7

    class BlogPost(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        published = models.BooleanField(default=False)
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)

3. 模型之间的关系

Django 支持多种模型之间的关系,包括一对多、多对多和一对一关系。

一对多关系(ForeignKey)

一个 Author 可以有多篇 BlogPost

1
2
3
4
5
6
7
class Author(models.Model):
    name = models.CharField(max_length=100)

class BlogPost(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    content = models.TextField()

多对多关系(ManyToManyField)

一个 BlogPost 可以有多个 Tag,一个 Tag 也可以属于多个 BlogPost

1
2
3
4
5
6
7
class Tag(models.Model):
    name = models.CharField(max_length=50)

class BlogPost(models.Model):
    tags = models.ManyToManyField(Tag)
    title = models.CharField(max_length=200)
    content = models.TextField()

一对一关系(OneToOneField)

一个 User 对应一个 Profile

1
2
3
4
5
from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField()

4. 元数据(Meta)

Meta 类是 Django 模型中的一个内部类,用于定义模型的元数据。通过 Meta 类,开发者可以设置模型的排序方式、数据库表名、权限、抽象基类等属性。
常见的类型

  • permissions:为模型定义自定义权限
  • abstract:指定该模型是否为抽象基类,抽象基类不会创建数据库表,但其字段会被子类继承
  • app_label:指定模型所属的应用标签,通常用于在模型定义不在其应用目录中时使用
  • db_table:指定数据库表中对应的表名
  • verbose_nameverbose_name_plural:为模型指定单数和复数的可读名称,通常用于 Django 管理后台的显示
  • unique_together:指定多个字段组合在一起时必须唯一,确保数据库层面的唯一性约束
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class BlogPost(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        class Meta:
            # 定义模型的排序,并使用 - 前缀表示降序
            ordering = ['-created_at']
            # 为模型指定单数和复数的可读名称,通常用于 Django 管理后台的显示。
            verbose_name = '博客文章'
            verbose_name_plural = '博客文章集'

5. 自定义方法和属性

模型类可以定义自定义方法和属性,以便在模板或视图中使用。

1
2
3
4
5
class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    def snippet(self):
        return self.content[:50] + '...'

在模板中,可以通过 {{ blogpost.snippet }} 来调用该方法。

6. 模型的迁移

每当模型发生变化时,需要创建和应用迁移来同步数据库结构。

创建迁移

1
python manage.py makemigrations

应用迁移

1
python manage.py migrate

7. 使用模型

通过 Django 的 ORM,可以轻松地对模型进行增删改查操作。

创建对象

1
author = Author.objects.create(name='张三', email='[email protected]')

查询对象

1
2
3
4
5
6
# 获取所有作者
authors = Author.objects.all()
# 过滤查询
zhangsan = Author.objects.get(name='张三')
# 使用双下划线进行跨表查询
blog_posts = BlogPost.objects.filter(author__name='张三')

更新对象

1
2
author.email = '[email protected]'
author.save()

删除对象

1
author.delete()

8. 管理器(Manager)

每个模型都有一个默认的管理器 objects,可以通过它进行数据库操作。你也可以自定义管理器来添加额外的查询方法。
实例

1
2
3
4
5
6
7
8
9
10
class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(published=True)

class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    published = models.BooleanField(default=False)
    objects = models.Manager()  # 默认管理器
    published_posts = PublishedManager()  # 自定义管理器

现在,可以通过 BlogPost.published_posts.all() 获取所有已发布的博客文章。

9. 表单和模型

Django 提供了 ModelForm,可以根据模型自动生成表单,简化表单处理。

1
2
3
4
5
6
7
from django import forms
from .models import BlogPost

class BlogPostForm(forms.ModelForm):
    class Meta:
        model = BlogPost
        fields = ['title', 'content', 'published']

10. Model Management

模型管理(Model Management) 是 Django 中用于创建、读取、更新和删除(CRUD)数据库记录的功能。通过 Django 的 ORM(对象关系映射),你可以轻松操作数据库,无需编写 SQL 语句。

1. 创建记录

通过实例化模型并调用 save() 方法:

1
2
3
4
5
6
7
8
from myapp.models import MyModelName

# 创建新实例
a_record = MyModelName(my_field_name="实例 #1")
# 保存到数据库
a_record.save()
# 自动赋予主键 `id`
print(a_record.id)  # 输出: 1

或者使用管理器的 create() 方法:

1
new_record = MyModelName.objects.create(my_field_name="实例 #2")

2. 查询记录

使用管理器的查询方法:

1
2
3
4
5
6
# 获取所有记录
all_records = MyModelName.objects.all()
# 获取特定记录
record = MyModelName.objects.get(id=1)
# 过滤查询
filtered = MyModelName.objects.filter(my_field_name="实例 #1")

3. 更新记录

修改字段后调用 save()

1
2
3
record = MyModelName.objects.get(id=1)
record.my_field_name = "新的实例名称"
record.save()

或使用 update() 方法批量更新:

1
MyModelName.objects.filter(my_field_name="旧名称").update(my_field_name="新名称")

4. 删除记录

使用 delete() 方法:

1
2
record = MyModelName.objects.get(id=1)
record.delete()

批量删除:

1
MyModelName.objects.filter(my_field_name="不需要的名称").delete()

5. 批量操作与事务

批量创建

1
2
3
4
5
6
records = [
    MyModelName(my_field_name="实例 #3"),
    MyModelName(my_field_name="实例 #4"),
]

MyModelName.objects.bulk_create(records)

事务管理确保多个操作的原子性:

1
2
3
4
5
6
7
8
from django.db import transaction

try:
    with transaction.atomic():
        record1 = MyModelName.objects.create(my_field_name="实例 #5")
        record2 = MyModelName.objects.create(my_field_name="实例 #6")
except Exception as e:
    print("事务失败:", e)

超级用户

我们创建的应用里面有个admin.py,初始内容是这样的:

1
2
from django.contrib import admin
# Register your models here.

我们导入模型之后进行注册

1
2
3
4
5
6
from .models import Author, Genre, Book, BookInstance

admin.site.register(Book)
admin.site.register(Author)
admin.site.register(Genre)
admin.site.register(BookInstance)

通过命令行创建超级用户:

1
2
python3 manage.py createsuperuser
python3 manage.py runserver

ModelAdmin

在管理界面去改变一个模型的展示方式

1
2
3
4
5
6
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
# 配置列表视图
    list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
    # 添加列表过滤器
    list_filter = ('status', 'due_back')

控制哪些字段被显示和布局
fields 属性列表只是要显示在表格上那些领域,如此才能。字段默认情况下垂直显示,但如果你进一步将它们分组在元组中(如上述“日期”字段中所示),则会水平显示。

1
2
3
class AuthorAdmin(admin.ModelAdmin):
list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]

视图管理

在 BookInstance模型中,我们有相关的书是什么(即信息 nameimprint 和 id),并且当将可用(statusdue_back)。我们可以通过将粗体文本添加到我们的BookInstanceAdmin类中来将其添加到不同的部分。

1
2
3
4
5
6
7
8
9
10
11
12
@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
list_filter = ('status', 'due_back')

fieldsets = (
(None, {
'fields': ('book','imprint', 'id')
}),
('Availability', {
'fields': ('status', 'due_back')
}),
)

内联编辑

1
2
3
4
5
6
7
class BooksInstanceInline(admin.TabularInline):
model = BookInstance

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'display_genre')
inlines = [BooksInstanceInline]

通用视图

  • 定义:预定义的类视图,简化常见的Web开发任务(如显示列表、详情、创建、更新、删除)。
  • 优势
    • 减少重复代码
    • 提高开发效率
    • 易于维护和扩展

常用通用视图类型

  1. ListView

    • 用途:显示对象列表
    • 关键属性
      • model: 模型类
      • template_name: 模板路径
      • context_object_name: 上下文变量名
      • paginate_by: 每页显示数量
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      from django.views.generic import ListView
      from .models import Article

      class ArticleListView(ListView):
      model = Article
      template_name = 'articles/article_list.html'
      context_object_name = 'articles'
      paginate_by = 10
  2. DetailView

    • 用途:显示单个对象详情
    • 关键属性
      • model
      • template_name
      • context_object_name
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      from django.views.generic import DetailView
      from .models import Article

      class ArticleDetailView(DetailView):
      model = Article
      template_name = 'articles/article_detail.html'
      context_object_name = 'article'
  3. CreateView

    • 用途:创建新对象,自动生成表单
    • 关键属性
      • model
      • form_classfields
      • template_name
      • success_url
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      from django.views.generic import CreateView
      from .models import Article
      from .forms import ArticleForm

      class ArticleCreateView(CreateView):
      model = Article
      form_class = ArticleForm
      template_name = 'articles/article_form.html'
      success_url = '/articles/'
  4. UpdateView

    • 用途:更新现有对象,自动生成表单
    • 关键属性:与 CreateView 类似
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      from django.views.generic import UpdateView
      from .models import Article
      from .forms import ArticleForm

      class ArticleUpdateView(UpdateView):
      model = Article
      form_class = ArticleForm
      template_name = 'articles/article_form.html'
      success_url = '/articles/'
  5. DeleteView

    • 用途:删除对象,显示确认页面
    • 关键属性
      • model
      • template_name
      • success_url
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      from django.views.generic import DeleteView
      from .models import Article
      from django.urls import reverse_lazy

      class ArticleDeleteView(DeleteView):
      model = Article
      template_name = 'articles/article_confirm_delete.html'
      success_url = reverse_lazy('article-list')

使用的基本步骤

  1. 定义模型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django.db import models

    class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
    return self.title
  2. 创建视图
    • 继承相应的通用视图类
    • 配置必要的属性
  3. 配置URL
    1
    2
    3
    4
    5
    6
    7
    from django.urls import path
    from .views import ArticleListView, ArticleDetailView

    urlpatterns = [
    path('', ArticleListView.as_view(), name='article-list'),
    path('<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),
    ]
  4. 创建模板
    • 根据 template_name 指定路径创建HTML文件
    • 例如:templates/articles/article_list.html

自定义通用视图

  • 覆盖类属性:如 template_namecontext_object_name
  • 添加方法
    • get_queryset(): 自定义查询集
    • form_valid(): 自定义表单处理
  • 使用 Mixins:添加额外功能(如权限控制)
    示例:自定义查询集
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from django.views.generic import ListView
    from .models import Article

    class PublishedArticleListView(ListView):
    model = Article
    template_name = 'articles/published_article_list.html'
    context_object_name = 'articles'

    def get_queryset(self):
    return Article.objects.filter(status='published').order_by('-created_at')

示例项目概览

  1. 模型定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django.db import models

    class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    published_date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
    return self.title
  2. 视图创建
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
    from .models import Post
    from django.urls import reverse_lazy

    class PostListView(ListView):
    model = Post
    template_name = 'posts/post_list.html'
    context_object_name = 'posts'
    paginate_by = 5

    class PostDetailView(DetailView):
    model = Post
    template_name = 'posts/post_detail.html'
    context_object_name = 'post'

    class PostCreateView(CreateView):
    model = Post
    fields = ['title', 'content']
    template_name = 'posts/post_form.html'
    success_url = reverse_lazy('post-list')

    class PostUpdateView(UpdateView):
    model = Post
    fields = ['title', 'content']
    template_name = 'posts/post_form.html'
    success_url = reverse_lazy('post-list')

    class PostDeleteView(DeleteView):
    model = Post
    template_name = 'posts/post_confirm_delete.html'
    success_url = reverse_lazy('post-list')
  3. URL配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from django.urls import path
    from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView

    urlpatterns = [
    path('', PostListView.as_view(), name='post-list'),
    path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
    path('post/new/', PostCreateView.as_view(), name='post-create'),
    path('post/<int:pk>/edit/', PostUpdateView.as_view(), name='post-update'),
    path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
    ]
  4. 模板创建
    • templates/posts/post_list.html
    • templates/posts/post_detail.html
    • templates/posts/post_form.html
    • templates/posts/post_confirm_delete.html

总结

  • 通用视图:简化CRUD操作,减少代码量,提高开发效率。
  • 使用步骤
    1. 定义模型
    2. 创建通用视图
    3. 配置URL
    4. 创建模板
  • 自定义:通过覆盖属性和方法,结合Mixins,满足特定需求。
    Django 会话(Sessions)简明笔记

会话

  • 定义:会话用于在多个请求之间存储和传递用户特定的数据,如登录状态、用户偏好等。
  • 作用:保持用户状态,实现用户认证、购物车等功能。

工作原理

  1. 会话中间件
    • SessionMiddleware 负责在每个请求和响应中添加会话支持。
    • 确保 request 对象具备 session 属性。
  2. 会话存储
    • 默认使用数据库(django.contrib.sessions.backends.db)。
    • 其他选项:
      • 缓存django.contrib.sessions.backends.cache
      • 文件系统django.contrib.sessions.backends.file
      • 加密 cookiedjango.contrib.sessions.backends.signed_cookies
      • 缓存数据库django.contrib.sessions.backends.cached_db
  3. 会话键
    • Django 生成一个唯一的会话键(session key)并存储在客户端的浏览器中,通常作为 cookie。

配置会话

  • 启用会话中间件
    settings.py 中确保 MIDDLEWARE 包含 'django.contrib.sessions.middleware.SessionMiddleware'
    1
    2
    3
    4
    5
    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    # 其他中间件
    ]
  • 设置会话存储后端
    1
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 默认
  • 会话过期设置
    • 全局过期
      1
      SESSION_COOKIE_AGE = 1209600  # 两周,单位秒
    • 浏览器关闭时过期
      1
      SESSION_EXPIRE_AT_BROWSER_CLOSE = True
  • 安全设置
    1
    2
    3
    SESSION_COOKIE_SECURE = True  # 仅通过 HTTPS 传输
    SESSION_COOKIE_HTTPONLY = True # 防止 JavaScript 访问
    SESSION_COOKIE_SAMESITE = 'Lax' # 防止跨站请求伪造

使用会话

  • 设置会话数据
    1
    2
    3
    4
    def set_session(request):
    request.session['username'] = '张三'
    request.session['is_logged_in'] = True
    return HttpResponse("会话已设置")
  • 获取会话数据
    1
    2
    3
    4
    def get_session(request):
    username = request.session.get('username', '匿名用户')
    is_logged_in = request.session.get('is_logged_in', False)
    return HttpResponse(f"用户名: {username}, 登录状态: {is_logged_in}")
  • 删除会话数据
    1
    2
    3
    4
    5
    6
    def delete_session(request):
    try:
    del request.session['username']
    except KeyError:
    pass
    return HttpResponse("会话数据已删除")
  • 清除全部会话数据
    1
    2
    3
    def clear_session(request):
    request.session.flush()
    return HttpResponse("所有会话数据已清除")

会话示例

视图示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from django.shortcuts import render, redirect
from django.http import HttpResponse

def login_view(request):
if request.method == 'POST':
# 假设验证成功
request.session['user_id'] = user.id
request.session['username'] = user.username
return redirect('home')
return render(request, 'login.html')

def home_view(request):
if 'username' in request.session:
username = request.session['username']
return HttpResponse(f"欢迎, {username}!")
return HttpResponse("你还没有登录。")

会话安全性考虑

  • 使用 HTTPS:确保 SESSION_COOKIE_SECURE = True,防止会话 cookie 在不安全的连接上传输。
  • 防止会话固定攻击
    • 登录后重新生成会话键:
      1
      2
      3
      4
      5
      6
      7
      from django.contrib.auth import login

      def login_user(request):
      user = authenticate(request, username='张三', password='密码')
      if user:
      login(request, user) # 自动调用 session.flush()
      return redirect('home')
  • 限制会话寿命:通过设置 SESSION_COOKIE_AGESESSION_EXPIRE_AT_BROWSER_CLOSE 控制会话过期时间。

常用会话方法

  • **request.session.save()**:
    手动保存会话数据。
  • **request.session.modified = True**:
    标记会话已修改,确保保存更改。
  • **request.session.clear_expired()**:
    清除过期的会话数据(通常由 Django 自动处理)。

高级配置

  • 自定义会话存储
    可以创建自定义的会话引擎,满足特定需求。
  • 信号
    Django 提供会话相关信号,如 session_pre_savesession_save,用于在会话保存前后执行自定义操作。

总结

  • 会话:在 Django 中用于在多个请求间存储用户数据,保持用户状态。
  • 配置:通过中间件和 settings.py 进行配置,确保安全性和性能。
  • 使用:通过 request.session 对象进行数据的设置、获取和删除。
  • 安全:确保使用 HTTPS,配置安全的 cookie 设置,防止常见的会话攻击。

认证

  • 定义:认证系统用于验证用户身份,确保用户是他们声称的身份。
  • 作用:管理用户登录、登出、注册,以及权限控制,保护网站资源不被未授权访问。

Django 认证系统概述

  • 内置功能:Django 提供了一个强大的内置认证系统,包括用户模型、认证视图、表单和装饰器。
  • 主要组件
    • 用户模型(User Model):存储用户信息,如用户名、密码、电子邮件等。
    • 认证视图:处理登录、登出和注册。
    • 权限和组:管理用户权限和组织用户组。
    • 装饰器和混合类:保护视图,限制访问权限。

配置认证系统

  1. 确保安装应用
    settings.py 中,确保以下应用已添加到 INSTALLED_APPS
    1
    2
    3
    4
    5
    6
    7
    8
    9
    INSTALLED_APPS = [
    ...
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    ...
    ]
  2. 中间件设置
    确保 AuthenticationMiddlewareSessionMiddleware 已启用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
  3. URL 配置
    在项目的 urls.py 中包含认证视图:
    1
    2
    3
    4
    5
    6
    7
    from django.contrib import admin
    from django.urls import path, include

    urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
    ]

用户模型(User Model)

  • 默认用户模型:Django 提供了一个默认的 User 模型,位于 django.contrib.auth.models
  • 自定义用户模型
    • 推荐:在项目开始时自定义用户模型,以满足特定需求。
    • 方法
      1
      2
      3
      4
      5
      from django.contrib.auth.models import AbstractUser
      from django.db import models

      class CustomUser(AbstractUser):
      bio = models.TextField(blank=True, null=True)
    • 配置:在 settings.py 中设置:
      1
      AUTH_USER_MODEL = 'yourapp.CustomUser'

认证视图

Django 提供了一组通用视图来处理认证相关操作:

  1. 登录视图(LoginView)
    • 路径/accounts/login/
    • 模板registration/login.html
    • 示例 URL 配置
      1
      2
      3
      4
      5
      6
      from django.urls import path
      from django.contrib.auth import views as auth_views

      urlpatterns = [
      path('login/', auth_views.LoginView.as_view(), name='login'),
      ]
  2. 登出视图(LogoutView)
    • 路径/accounts/logout/
    • 模板registration/logged_out.html
    • 示例 URL 配置
      1
      2
      3
      urlpatterns = [
      path('logout/', auth_views.LogoutView.as_view(), name='logout'),
      ]
  3. 密码重置视图
    • 路径:多个视图处理密码重置流程
    • 模板registration/password_reset_form.html

用户注册

Django 不提供内置的注册视图,需要自行创建:

  1. 创建表单
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django import forms
    from django.contrib.auth.models import User

    class SignUpForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)

    class Meta:
    model = User
    fields = ['username', 'email', 'password']
  2. 创建视图
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    from django.shortcuts import render, redirect
    from django.contrib.auth import login, authenticate
    from .forms import SignUpForm

    def signup_view(request):
    if request.method == 'POST':
    form = SignUpForm(request.POST)
    if form.is_valid():
    user = form.save(commit=False)
    user.set_password(form.cleaned_data['password'])
    user.save()
    login(request, user)
    return redirect('home')
    else:
    form = SignUpForm()
    return render(request, 'registration/signup.html', {'form': form})
  3. 配置 URL
    1
    2
    3
    urlpatterns = [
    path('signup/', signup_view, name='signup'),
    ]
  4. 创建模板
    • templates/registration/signup.html

保护视图

  • 基于装饰器
    • @login_required:限制视图仅对登录用户可见。
      1
      2
      3
      4
      5
      from django.contrib.auth.decorators import login_required

      @login_required
      def my_view(request):
      ...
  • 基于类的视图混合类
    • LoginRequiredMixin:用于类视图。
      1
      2
      3
      4
      5
      from django.contrib.auth.mixins import LoginRequiredMixin
      from django.views.generic import TemplateView

      class ProtectedView(LoginRequiredMixin, TemplateView):
      template_name = 'protected.html'

权限和组

  • 权限
    • 每个模型默认有 add, change, delete, view 权限。
    • 自定义权限
      1
      2
      3
      4
      class Meta:
      permissions = [
      ("can_publish", "Can publish articles"),
      ]
    • 用于将权限分组,便于管理多个用户的权限。
    • 示例
      1
      2
      3
      4
      5
      from django.contrib.auth.models import Group, Permission

      group = Group.objects.create(name='Editors')
      permission = Permission.objects.get(codename='can_publish')
      group.permissions.add(permission)

扩展用户功能

  • 用户资料
    • 使用一对一关系扩展用户模型。
      1
      2
      3
      4
      5
      6
      from django.contrib.auth.models import User
      from django.db import models

      class Profile(models.Model):
      user = models.OneToOneField(User, on_delete=models.CASCADE)
      bio = models.TextField(blank=True)
  • 信号
    • 自动创建或保存用户资料。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      from django.db.models.signals import post_save
      from django.dispatch import receiver

      @receiver(post_save, sender=User)
      def create_user_profile(sender, instance, created, **kwargs):
      if created:
      Profile.objects.create(user=instance)

      @receiver(post_save, sender=User)
      def save_user_profile(sender, instance, **kwargs):
      instance.profile.save()

安全性考虑

  • 密码哈希
    • Django 使用强哈希算法(如 PBKDF2)存储密码,确保密码安全。
  • CSRF 保护
    • 确保在表单中使用 {% csrf_token %} 以防止跨站请求伪造攻击。
  • 会话安全
    • 配置 SESSION_COOKIE_SECURE, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SAMESITE 等设置。
  • 强密码策略
    • 配置 AUTH_PASSWORD_VALIDATORS 以强制执行密码复杂性要求。

常用方法和属性

  • 用户认证
    • authenticate():验证用户名和密码。
      1
      2
      3
      4
      5
      from django.contrib.auth import authenticate

      user = authenticate(username='john', password='secret')
      if user is not None:
      # 用户认证成功
    • login():登录用户。
      1
      2
      3
      from django.contrib.auth import login

      login(request, user)
    • logout():登出用户。
      1
      2
      3
      from django.contrib.auth import logout

      logout(request)
  • 检查用户状态
    • request.user.is_authenticated:判断用户是否已登录。
    • request.user.is_staff:判断用户是否为工作人员。
    • request.user.is_superuser:判断用户是否为超级用户。

示例代码

登录视图示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib.auth import authenticate, login
from django.shortcuts import render, redirect

def login_view(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('home')
else:
return render(request, 'registration/login.html', {'error': '无效的凭据'})
return render(request, 'registration/login.html')

保护视图示例

1
2
3
4
5
6
from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
def dashboard(request):
return render(request, 'dashboard.html')

总结

  • Django 认证系统:提供全面的用户认证和权限管理功能,简化用户管理和安全控制。
  • 主要步骤
    1. 配置认证系统(添加中间件和应用)。
    2. 使用内置视图或自定义视图处理登录、登出、注册。
    3. 保护视图,限制访问权限。
    4. 管理用户权限和组,确保资源安全。
    5. 扩展用户模型以满足项目需求。
  • 安全性:遵循最佳实践,确保用户数据和会话的安全。

Django 表单(Forms)

  • 定义:Django 表单用于处理用户输入的数据,简化表单的创建、验证和处理过程。
  • 作用
    • 创建和渲染 HTML 表单
    • 验证用户输入的数据
    • 处理表单提交,保存数据到数据库

表单的类型

  1. 普通表单(Forms)

    • 不直接关联数据库模型
    • 适用于需要自定义字段或不需要保存到数据库的数据
  2. 模型表单(ModelForms)

    • 直接基于 Django 模型创建表单
    • 自动生成与模型字段对应的表单字段
    • 简化数据保存到数据库的过程

创建表单

1. 普通表单

  • 步骤
    1. forms.py 中定义表单类,继承自 forms.Form
    2. 定义表单字段
  • 示例
    1
    2
    3
    4
    5
    6
    7
    # myapp/forms.py
    from django import forms

    class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, label='你的名字')
    email = forms.EmailField(label='电子邮件')
    message = forms.CharField(widget=forms.Textarea, label='留言内容')

2. 模型表单

  • 步骤
    1. forms.py 中定义表单类,继承自 forms.ModelForm
    2. 指定关联的模型和要包含的字段
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    # myapp/forms.py
    from django import forms
    from .models import Article

    class ArticleForm(forms.ModelForm):
    class Meta:
    model = Article
    fields = ['title', 'content']

渲染表单到模板

  • 步骤
    1. 在视图中创建表单实例并传递给模板
    2. 在模板中渲染表单
  • 视图示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # myapp/views.py
    from django.shortcuts import render, redirect
    from .forms import ContactForm

    def contact_view(request):
    if request.method == 'POST':
    form = ContactForm(request.POST)
    if form.is_valid():
    # 处理表单数据
    name = form.cleaned_data['name']
    email = form.cleaned_data['email']
    message = form.cleaned_data['message']
    # 例如,发送邮件或保存到数据库
    return redirect('success')
    else:
    form = ContactForm()
    return render(request, 'contact.html', {'form': form})
  • 模板示例 (templates/contact.html):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!DOCTYPE html>
    <html>
    <head>
    <title>联系表单</title>
    </head>
    <body>
    <h1>联系我们</h1>
    <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">提交</button>
    </form>
    </body>
    </html>

处理表单提交

  • 验证数据
    • 使用 form.is_valid() 方法验证用户输入的数据
    • 访问 form.cleaned_data 获取清洗后的数据
  • 示例
    1
    2
    3
    4
    5
    if form.is_valid():
    name = form.cleaned_data['name']
    email = form.cleaned_data['email']
    message = form.cleaned_data['message']
    # 处理数据,如保存或发送邮件

显示表单错误

  • 模板中自动显示错误
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <form method="post">
    {% csrf_token %}
    {{ form.non_field_errors }}
    <div>
    {{ form.name.label_tag }}
    {{ form.name }}
    {{ form.name.errors }}
    </div>
    <div>
    {{ form.email.label_tag }}
    {{ form.email }}
    {{ form.email.errors }}
    </div>
    <div>
    {{ form.message.label_tag }}
    {{ form.message }}
    {{ form.message.errors }}
    </div>
    <button type="submit">提交</button>
    </form>

使用 ModelForm 简化数据保存

  • 视图示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # myapp/views.py
    from django.shortcuts import render, redirect
    from .forms import ArticleForm

    def create_article(request):
    if request.method == 'POST':
    form = ArticleForm(request.POST)
    if form.is_valid():
    form.save() # 自动保存到数据库
    return redirect('article-list')
    else:
    form = ArticleForm()
    return render(request, 'create_article.html', {'form': form})
  • 模板示例 (templates/create_article.html):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!DOCTYPE html>
    <html>
    <head>
    <title>创建文章</title>
    </head>
    <body>
    <h1>创建新文章</h1>
    <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">保存</button>
    </form>
    </body>
    </html>

自定义表单字段和验证

  • 添加自定义验证
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # myapp/forms.py
    from django import forms

    class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)

    def clean_email(self):
    email = self.cleaned_data.get('email')
    if not email.endswith('@example.com'):
    raise forms.ValidationError("只能使用 @example.com 的电子邮件地址。")
    return email
  • 覆盖字段属性
    1
    2
    3
    4
    5
    6
    7
    8
    class ArticleForm(forms.ModelForm):
    class Meta:
    model = Article
    fields = ['title', 'content']
    widgets = {
    'title': forms.TextInput(attrs={'class': 'form-control'}),
    'content': forms.Textarea(attrs={'class': 'form-control'}),
    }

表单安全性

  • 跨站请求伪造(CSRF)保护
    • 在模板中的 <form> 标签内添加 {% csrf_token %} 模板标签
    • 确保 CsrfViewMiddleware 已启用(默认启用)
  • 示例
    1
    2
    3
    4
    5
    <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">提交</button>
    </form>

高级功能

  1. 表单继承
    • 通过继承基础表单类,复用和扩展表单字段和逻辑
  2. 表单集(Formsets)
    • 管理和处理多个相同类型的表单
  3. 嵌套表单
    • 组合多个表单,实现复杂的数据输入

示例项目概览

目标:创建一个允许用户提交博客文章的表单

  1. 模型定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # myapp/models.py
    from django.db import models

    class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
    return self.title
  2. 表单创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # myapp/forms.py
    from django import forms
    from .models import Post

    class PostForm(forms.ModelForm):
    class Meta:
    model = Post
    fields = ['title', 'content']
    widgets = {
    'title': forms.TextInput(attrs={'class': 'form-control'}),
    'content': forms.Textarea(attrs={'class': 'form-control'}),
    }
  3. 视图创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # myapp/views.py
    from django.shortcuts import render, redirect
    from .forms import PostForm
    from .models import Post

    def create_post(request):
    if request.method == 'POST':
    form = PostForm(request.POST)
    if form.is_valid():
    form.save()
    return redirect('post-list')
    else:
    form = PostForm()
    return render(request, 'create_post.html', {'form': form})

    def post_list(request):
    posts = Post.objects.all().order_by('-created_at')
    return render(request, 'post_list.html', {'posts': posts})
  4. URL 配置

    1
    2
    3
    4
    5
    6
    7
    8
    # myapp/urls.py
    from django.urls import path
    from . import views

    urlpatterns = [
    path('posts/', views.post_list, name='post-list'),
    path('posts/new/', views.create_post, name='create-post'),
    ]
  5. 模板创建

    • 创建文章模板 (templates/create_post.html):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <!DOCTYPE html>
      <html>
      <head>
      <title>创建文章</title>
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
      </head>
      <body>
      <div class="container">
      <h1>创建新文章</h1>
      <form method="post">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit" class="btn btn-primary">保存</button>
      </form>
      </div>
      </body>
      </html>
    • 文章列表模板 (templates/post_list.html):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <!DOCTYPE html>
      <html>
      <head>
      <title>文章列表</title>
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
      </head>
      <body>
      <div class="container">
      <h1>所有文章</h1>
      <a href="{% url 'create-post' %}" class="btn btn-success mb-3">创建新文章</a>
      <ul class="list-group">
      {% for post in posts %}
      <li class="list-group-item">
      <h3>{{ post.title }}</h3>
      <p>{{ post.content }}</p>
      <small>创建于 {{ post.created_at }}</small>
      </li>
      {% empty %}
      <li class="list-group-item">暂无文章。</li>
      {% endfor %}
      </ul>
      </div>
      </body>
      </html>

总结

  • Django 表单:简化用户输入数据的创建、验证和处理过程。
  • 类型
    • 普通表单:自定义字段,灵活使用
    • 模型表单:基于模型,自动生成字段,便于数据保存
  • 关键步骤
    1. 创建表单类(普通或模型表单)
    2. 在视图中处理表单
    3. 在模板中渲染表单
    4. 验证和处理用户输入的数据
  • 安全性
    • 使用 CSRF 保护
    • 验证和清洗用户输入
  • 高级功能
    • 表单继承、表单集、嵌套表单
2019-2025 Sean Yam