Python开发-Django常用的 Web 应用程序工具

常用的 Web 应用程序工具

Django 提供了多种开发 Web 应用程序所需的常用工具:

一、认证:

下在从认证的角度,我们本文主要讲的内容有以下:

概述使用认证系统密码管理自定义认证API 参考

概述

首先我们来看看Django中的用户认证

Django 是自带有一个用户验证系统。它主要的功能是负责处理用户账号、组、权限和基于cookie的用户会话,也就是用户角色权限分配管理。本文在这部分将解释默认下的实现如何开箱即用,以及如何扩展和自定义以满足你的项目需求。

什么是验证认证系统:

Django 验证系统处理验证和授权。简单来说,验证检验用户是否是他们的用户,授权决定已验证用户能做什么或有什么功能可操作。这里的术语验证用于指代这两个功能任务。

下面我们来说说认证系统都有那些部分组成:

  • 用户
  • 权限:二进制(是/否)标识指定用户是否可以执行特定任务。
  • 组:将标签和权限应用于多个用户的一般方法。
  • 可配置的密码哈希化系统
  • 为登录用户或限制内容提供表单和视图工具
  • 可插拔的后端系统

Django 里的验证系统实现的比较通用化,不提供一些常见的 web 验证系统的特性。但是其中一些常见问题的解决方案已在第三方包中实现。

  • 密码强度检查
  • 限制登录尝试
  • 针对第三方的身份验证(例如OAuth)
  • 对象级权限

验证系统的安装

验证系统被捆绑为 django.contrib.auth 的 Django contrib 模块。默认情况下,所需的配置以及包含在 django-admin startproject 生成的 settings.py 中,在 INSTALLED_APPS 配置列出了以下两个条目:

  • ‘django.contrib.auth’ 包含了验证框架的内核和它的默认模型。

  • ‘django.contrib.contenttypes’ 是 Django content type system ,允许你创建的模型和权限相关联。
    这些条目在你的 MIDDLEWARE 设置中:

  • SessionMiddleware 通过请求管理 sessions 。

  • AuthenticationMiddleware 使用会话将用户和请求关联。

有了这些设置,运行命令 manage.py migrate 为auth相关模型创建必要的数据表,并为已安装应用中定义的任何模型创建许可。

使用认证系统

上面我们已经安装,那么如何使用 Django 的验证系统

下面我们来介绍Django 验证系统在默认配置下的使用方法。默认配置满足最常见的项目需求,可以处理相当多的任务,还有一个安全的密码和权限实现。对于验证需求与默认配置不同的项目,Django 支持对身份验证进行扩展和定制。

Django 验证同时提供身份验证和授权,通常称为身份验证系统,因为这些功能在某种程度上是耦合的。

User 对象

用户对象是认证系统的核心。它通常代表了与你的站点交互的人员,并用于允许诸如限制访问、注册用户配置文件、将内容与创建者关联等功能。Django 的认证框架中用户只有一个类,例如 “超级管理员”或“普通管理员”只是具有特殊属性集的用户对象,而不是用户对象的不同类。

默认用户的主要属性是:

  • username
  • password
  • email
  • first_name
  • last_name

下面我们使用包含 create_user() 的函数来创建用户:

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')

# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.

>>> user.last_name = 'Lennon'
>>> user.save()

如果你已经安装了 Django admin 管理后台,你也可以在 admin 管理后台交互式地创建用户:ref:create users interactively

同样我们可以通过命令行 createsuperuser 创建超级管理员:

$ python manage.py createsuperuser --username=lili --email=lili@example.com

创建过程中会提示输入密码,完成之后,超级管理员就被创建成功了。如果你没有填写参数 --username <createsuperuser --username> or :option:--email <createsuperuser --email> ,也将会被提示输入这些值。

如何我们需要修改密码怎么办呢:

Django 不会在用户模型里保存原始(明文)密码,而只会存储哈希值(如何管理密码 documentation of how passwords are managed ) 。因此,请不要试图直接操作用户的密码,这就是创建用户需要辅助函数的原因。

其实更改一个用户的密码,你有几个选择:

manage.py changepassword username 提供了在命令行修改用户密码的方法。它会提示你输入两次新密码,如果操作成功,新密码就立刻生效。如果你没有提供参数 username ,那么将会尝试修改当前系统用户的密码。

你也可以在代码里修改密码,使用 set_password():

>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username='john')
>>> u.set_password('new password')
>>> u.save()

如果你已经安装了 Django admin 管理后台,你也可以在管理后台页面修改密码(:ref:authentication system's admin pages )。

Django 还提供了允许用户自行修改密码的 :ref:views 和 :ref:forms

修改密码将会注销用户的所有会话。密码更改时会话失效 。

那又是如何验证用户的呢

authenticate(request=None, **credentials)

使用 authenticate() 来验证用户。它使用 username 和 password 作为参数来验证,对每个身份验证后端( authentication backend )进行检查。如果后端验证有效,则返回一个 :class:~django.contrib.auth.models.User 对象。如果后端引发 PermissionDenied 错误,将返回 None。

举例:

from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
    # A backend authenticated the credentials
else:
    # No backend authenticated the credentials

request 是可选的 HttpRequest ,它在身份验证后端上的 authenticate() 方法来传递。

权限和认证

Django 内置了一个权限系统。它提供了为指定的用户和用户组分配权限的方法。

它在 Django 管理后台界面里使用,但你也可以在自己的代码中使用它。

Django 的 admin 页面使用了如下权限:

  • 访问查看的对象仅限于具有该类型对象的“查看”或“更改”权限的用户。
  • 访问“添加”表单和添加对象仅限于具有该类型对象的“添加”权限的用户。
  • 访问修改列表、查看“修改”表单和修改对象仅限于对该类型对象的“修改”权限的用户。
  • 访问删除对象仅限于对该类型对象的“删除”权限的用户。

不仅可以为每个对象类型设置权限,还可以为每个指定对象实例设置权限。通过使用 ModelAdmin 类提供的 has_view_permission(), has_add_permission(), has_change_permission() 和 has_delete_permission() 方法,可以为同一类型的不同实例定制权限。

User 对象有两个多对多字段:groups 和 user_permissions。 User 对象可以像访问其他 :doc:Django model: 一样访问他们的相关对象。

myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()

下面来说说如何以编程方式创建权限:

from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(
    codename='can_publish',
    name='Can Publish Posts',
    content_type=content_type,
)

那么如何权限缓存:

from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404

from myapp.models import BlogPost

def user_gains_perms(request, user_id):
    user = get_object_or_404(User, pk=user_id)
    # any permission check will cache the current set of permissions
    user.has_perm('myapp.change_blogpost')

    content_type = ContentType.objects.get_for_model(BlogPost)
    permission = Permission.objects.get(
        codename='change_blogpost',
        content_type=content_type,
    )
    user.user_permissions.add(permission)

    # Checking the cached permission set
    user.has_perm('myapp.change_blogpost')  # False

    # Request new instance of User
    # Be aware that user.refresh_from_db() won't clear the cache.
    user = get_object_or_404(User, pk=user_id)

    # Permission cache is repopulated from the database
    user.has_perm('myapp.change_blogpost')  # True

    ...

下面我们来说说代理模型:

class Person(models.Model):
    class Meta:
        permissions = [('can_eat_pizzas', 'Can eat pizzas')]

class Student(Person):
    class Meta:
        proxy = True
        permissions = [('can_deliver_pizzas', 'Can deliver pizzas')]

>>> # Fetch the content type for the proxy model.
>>> content_type = ContentType.objects.get_for_model(Student, for_concrete_model=False)
>>> student_permissions = Permission.objects.filter(content_type=content_type)
>>> [p.codename for p in student_permissions]
['add_student', 'change_student', 'delete_student', 'view_student',
'can_deliver_pizzas']
>>> for permission in student_permissions:
...     user.user_permissions.add(permission)
>>> user.has_perm('app.add_person')
False
>>> user.has_perm('app.can_eat_pizzas')
False
>>> user.has_perms(('app.add_student', 'app.can_deliver_pizzas'))
True

用户如何登陆

如果有一个已验证的用户想附加到当前会话(session)中,将通过 login() 函数完成。

login(request, user, backend=None)

要在视图中让用户登录,使用 login() 。它需要 HttpRequest 对象和 User 对象。通过 Django 的 session 框架, login() 会在 session 中保存用户的ID。

注意,在匿名会话期间设置的任何数据都会在用户登录后保留在会话中。

这个例子展示了如何使用 authenticate() 和 login(): :

from django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(request, username=username, password=password)
    if user is not None:
        login(request, user)
        # Redirect to a success page.
        ...
    else:
        # Return an 'invalid login' error message.
        ...

选择验证后端

当用户登录时,用户 ID 和用于身份验证的后端会被保存在用户会话中。允许相同的 authentication backend 在未来的请求中获取用户详情。选择要在会话中保存的验证后端如下:

  • 使用提供了的可选 backend 参数值。
  • 使用 user.backend 的值。允许配对 authenticate() 和 login() :当返回用户对象时authenticate() 设置 user.backend 属性。
  • 使用 AUTHENTICATION_BACKENDS 存在的 backend 。
  • 否则,抛出一个异常。

在1和2中,backend 参数和 user.backend 属性应该是完整的导入路径(像 AUTHENTICATION_BACKENDS 里的路径一样),而不是真实的后端类。

用户如何登出

logout(request)

如果已经通过 django.contrib.auth.login() 登录的用户想退出登录,可以在视图中使用 django.contrib.auth.logout() 。需要传入 HttpRequest 对象,并且该函数不会返回值。例如:

from django.contrib.auth import logout

def logout_view(request):
    logout(request)
    # Redirect to a success page.

注意,如果用户未登录,logout() 不会报错。

调用 logout() 后,当前请求的会话数据会被全部清除。这是为了防止其他使用同一个浏览器的用户访问前一名用户的会话数据。如果想在登出后立即向用户提供的会话中放入任何内容,请在调用 django.contrib.auth.logout() 之后执行此操作。

密码管理

Django中的密码管理

密码管理通常不应该被重新再设计,Django 努力提供了一个安全且灵活的管理用户密码的工具。这篇文档描述了 Django 如何存储密码,如何配置存储哈希,和一些使用哈希密码的工具。

Django 如何存储密码

Django 提供灵活的密码存储系统,默认使用 PBKDF2。

User 对象的 password 属性是如下这种格式:

<algorithm>$<iterations>$<salt>$<hash>

PASSWORD_HASHERS 的默认值是:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

这意味着 Django 除了使用 PBKDF2 来存储所有密码,也支持使用 PBKDF2SHA1 、argon2 和 bcrypt 来检测已存储的密码。

自定义认证

Django 中的自定义验证

Django 自带的身份验证已经足够满足大多数常见的情况,但你可能有一些需求没有被开箱即用的默认值所满足。在你的项目中定制身份验证需要了解所提供系统的哪些点是可扩展或可替换的。本文档详细介绍了如何定制认证系统。

当用户模型中存储的用户名和密码需要对一个不同于 Django 默认服务验证时,认证后端 提供了一个可扩展的系统。

你可以给你的模型 自定义权限 并且可以通过 Django 的授权系统检查。

你可以 扩展 默认的 User 模型,或者完全自定义一个模型进行 替换

其它认证资源

有时候你需要连接到其他认证源——一个包含用户名及密码的源或者认证方法。

例如,你的公司可能已经有了一个 LDAP 配置,为每个员工存储了一个用户名和密码。如果用户在 LDAP 和基于 Django 的应用程序中分别有不同的账户,那么对于网络管理员和用户本身来说都是一件很麻烦的事情。

所以,为了处理这样的情况,Django 认证系统让你可以插入其他认证源。你可以覆盖 Django 默认的基于数据库的方案,也可以将默认系统与其他系统串联使用。

API 参考

django.contrib.auth

该文档提供了 Django 认证系统组件的 API 。有关更多这些组件的用例,或需要自定义认证与鉴权

User 模型

class models.User

字段

class models.User

User 对象有如下字段:

username

必要的150 个字符或以下。用户名可包含字母数字、_、@、+、. 和 - 字符。

max_length 对许多使用情况来说应该是足够的。如果你需要更长的长度,请使用 自定义用户模型。如果你使用的 MySQL 是 utf8mb4 编码(推荐用于适当的 Unicode 支持),最多指定 max_length=191,因为在这种情况下,MySQL 默认只能创建 191 个字符的唯一索引。

first_name

可选的(blank=True)。150 个字符或更少。

Changed in Django 3.1:

max_length 从 30 个字符增加到 150 个字符。

last_name

可选的(blank=True)。150 个字符或更少。

email

可选的(blank=True)。电子邮件地址。

password

需要。一个密码的哈希值和元数据。(Django 不存储原始密码。)原始密码可以任意长,可以包含任何字符。

groups

多对多关系到 Group

user_permissions

多对多关系到 Permission

is_staff

布尔型。指定该用户是否可以访问管理站点。

is_active

布尔值。指定该用户账户是否应该被视为活跃账户。我们建议你把这个标志设置为 False,而不是删除账户;这样,如果你的应用程序对用户有任何外键,外键就不会被破坏。

这不一定能控制用户是否能登录。认证后端不一定需要检查 is_active 标志,但默认的后端(ModelBackend)和 RemoteUserBackend 会检查。如果你想允许不活跃的用户登录,你可以使用 AllowAllUsersModelBackend 或者 AllowAllUsersRemoteUserBackend。在这种情况下,你还需要自定义 AuthenticationForm 所使用的 LoginView,因为它拒绝非活动用户。需要注意的是, has_perm() 等权限检查方法,以及 Django 管理中的认证方法,都会对非活跃用户返回 False。

is_superuser

布尔值。指定该用户拥有所有权限,而不用一个个开启权限。

last_login

用户最后一次登录的日期时间。

date_joined

指定账户创建时间的日期时间。帐户创建时,默认设置为当前日期/时间。

属性

class models.User

is_authenticated

只读属性,始终返回 True (匿名用户 AnonymousUser.is_authenticated 始终返回 False )。这是一种判断用户是否已通过身份认证的方法。这并不意味着任何权限,也不会检查用户是否处于活动状态或是否具有有效会话。即使通常你会根据 request.user 检查这个属性,以确定它是否被 AuthenticationMiddleware 填充(表示当前登录的用户),但是你应该知道该属性对于任何 User 实例都返回 True。

is_anonymous

只读属性,总是 False。这是区分 User 和 AnonymousUser 对象的一种方式。一般来说,你应该优先使用 is_authenticated 来代替这个属性。

方法

class models.User

get_username()

返回用户的用户名。由于 User 模型可以被替换,你应该使用这个方法而不是直接引用用户名属性。

get_full_name()

返回 first_name 加上 last_name,中间有一个空格。

get_short_name()

返回 first_name。

set_password(raw_password)

将用户的密码设置为给定的原始字符串,并进行密码哈希处理。不保存 User 对象。

当 raw_password 为 None 时,密码将被设置为不可用的密码,就像 set_unusable_password() 一样。

check_password(raw_password)

如果给定的原始字符串是用户的正确密码,返回 True。(密码哈希值用于比较)

set_unusable_password()

标记该用户没有设置密码。 check_password() 对这个用户的检查永远不会返回 True。不会保存 User 对象。

如果针对现有外部源(例如 LDAP 目录)进行应用程序的身份认证,则可能需要这个功能。

has_usable_password()

如果 set_unusable_password() 被调用,返回 False。

get_user_permissions(obj=None)

返回用户直接拥有的一组权限字符串。

如果传入了 obj,则只返回这个特定对象的用户权限。

get_group_permissions(obj=None)

返回用户通过他们的组拥有的一组权限字符串。

如果传入了 obj,则只返回这个特定对象的组权限。

get_all_permissions(obj=None)

返回用户拥有的一组权限字符串,包括通过组和用户的权限。

如果传入了 obj,则只返回这个特定对象的权限。

has_perm(perm, obj=None)

如果用户拥有指定的权限,返回 True,其中 perm 的格式是 “.”。如果用户是不活跃的,这个方法将总是返回 False。对于活跃的超级用户,本方法将始终返回 True。

如果传入了 obj,这个方法不会检查模型的权限,而是检查这个特定对象的权限。

has_perms(perm_list, obj=None)

如果用户拥有指定的每个权限,返回 True,其中每个 perm 的格式为 “.”。如果用户不活跃,本方法将总是返回 False。对于活跃的超级用户,本方法将始终返回 True。

如果传入了 obj,这个方法不会检查模型的权限,而是检查这个特定对象的权限。

has_module_perms(package_name)

如果用户在给定的包(Django 应用标签)中有任何权限,则返回 True。如果用户不活跃,本方法将总是返回 False。如果是活跃的超级用户,本方法将始终返回 True。

email_user(subject, message, from_email=None, **kwargs)

向用户发送邮件。如果 from_email 是 None,Django 使用 DEFAULT_FROM_EMAIL。任何 **kwargs 都会传递给底层的 send_mail() 调用。

二、缓存

Django 缓存框架

动态网站的一个基本取舍是,它们是动态的。每当用户请求一个页面时,网络服务器都会进行各种计算 —— 从数据库查询到模板渲染再到业务逻辑 —— 以创建访客看到的页面。从处理花费的角度来看,这比标准的从文件系统中读取文件的服务器安排要昂贵得多。

对于大多数网络应用来说,这种开销并不是什么大问题。大多数网络应用不是 washingtonpost.com 或 slashdot.org;它们是流量不大的中小型网站。但对于中高流量的网站来说,必须尽可能地减少开销。

这就是缓存的用武之地。

缓存就是将昂贵的计算结果保存下来,这样下次就不用再进行计算了。以下是一些伪代码,解释了如何在动态生成的网页中使用这种方法:

given a URL, try finding that page in the cache
if the page is in the cache:
    return the cached page
else:
    generate the page
    save the generated page in the cache (for next time)
    return the generated page

Django 自带强大的缓存系统,可以让你保存动态页面,这样就不必为每次请求计算。为了方便,Django 提供了不同级别的缓存粒度。你可以缓存特定视图的输出,你可以只缓存难以生成的部分,或者你可以缓存整个网站。

二、日志

日志管理快速入门

Django 使用 Python 内置的 logging 模块处理系统日志。关于该模块的使用,Python 文档里有更详细的讨论。不过,如果你从未用过 Python 的 logging 框架(或者即便你用过),这里是一篇快速入门。

日志框架的组成元素

一份 Python logging 配置有下面四个部分组成:

  • Loggers
  • Handlers
  • 过滤器
  • Formatters

九、发送邮件

发送邮件

虽然 Python 通过 smtplib 模块提供了邮件发送的接口,但是 Django 在其基础上提供了更简化的支持。这些封装意在加快邮件发送,在开发时测试发送邮件,在不支持 SMTP 的平台上支持发送邮件。

这些代码位于 django.core.mail 模块。

快速上手

仅需两行代码:

from django.core.mail import send_mail
send_mail(
    'Subject here',
    'Here is the message.',
    'from@example.com',
    ['to@example.com'],
    fail_silently=False,
)

邮件是通过 SMTP 主机和端口发送的,由配置项 EMAIL_HOST 和 EMAIL_PORT 指定。如果配置了 EMAIL_HOST_USER 和 EMAIL_HOST_PASSWORD ,那么它们将被用来验证 SMTP 服务器。配置项 EMAIL_USE_TLS 和 EMAIL_USE_SSL 控制是否使用安全连接。

四、资讯聚合 (RSS/Atom)

Feed 聚合框架

Django 提供了一个高级的 feed 聚合生成框架来创建 RSS 和 Atom feed。

要创建任何聚合 feed,你只需要写一个简短的 Python 类。你可以创建任何你想要的 feed。

Django 还提供了一个低级的 feed 生成 API。如果你想在 Web 上下文之外生成 feed,或者用其他低级的方式,可以使用这个 API。

高级框架
概况

高级 feed 生成框架由 Feed 类提供。要创建一个 feed,需要写一个 Feed 类,并在你的 URLconf 中指向它的一个实例。

Feed 类

  • Feed 类是一个 Python 类,它表示一个聚合 feed。Feed 可以是简单的(例如,一个“站点新闻”feed,或者一个显示博客最新条目的基本 feed),也可以是更复杂的(例如,一个显示特定类别中所有博客条目的 feed,其中类别是可变的)。

  • Feed 类子类 django.contrib.syndication.views.Feed。它们可以存在于你代码库的任何地方。

  • Feed 类的实例是可以在你的 URLconf 中使用的视图。

一个简单的例子

这个简单的例子,取自于一个假设的警拍新闻网站,描述了一个最新的五个新闻项目的 feed:

from django.contrib.syndication.views import Feed
from django.urls import reverse
from policebeat.models import NewsItem

class LatestEntriesFeed(Feed):
    title = "Police beat site news"
    link = "/sitenews/"
    description = "Updates on changes and additions to police beat central."

    def items(self):
        return NewsItem.objects.order_by('-pub_date')[:5]

    def item_title(self, item):
        return item.title

    def item_description(self, item):
        return item.description

    # item_link is only needed if NewsItem has no get_absolute_url method.
    def item_link(self, item):
        return reverse('news-item', args=[item.pk])
要连接到这个 feed 的 URL,在你的 URLconf 中放入一个 Feed 对象的实例。例如:

from django.urls import path
from myproject.feeds import LatestEntriesFeed

urlpatterns = [
    # ...
    path('latest/feed/', LatestEntriesFeed()),
    # ...
]

五、分页

Django 提供了高级和低级的方法来帮助你管理分页数据——也就是说,数据被分割在几个页面上,并带有“上一页/下一页”链接。

Paginator 类

在幕后,所有分页方法都使用:Paginator 类。它完成了将 QuerySet 拆分为 Page 对象的所有繁重工作。

例如

给 Paginator 一个对象列表,以及你希望在每个页面上拥有的项目数,它提供了访问每页项目的方法:

>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)

>>> p.count
4
>>> p.num_pages
2
>>> type(p.page_range)
<class 'range_iterator'>
>>> p.page_range
range(1, 3)

>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']

>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4

>>> p.page(0)
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last):
...
EmptyPage: That page contains no results

六、消息框架

消息框架

在网页应用中,相当常见的是,你需要在处理完一个表单或一些其他类型的用户输入后,向用户显示一个一次性的通知消息(也称为“即时消息”)。

为此,Django 为匿名用户和认证用户提供了对基于 cookie 和会话的消息传递的全面支持。消息框架允许你在一个请求中临时存储消息,并在随后的请求(通常是下一个请求)中检索显示。每条消息都有一个特定的 level 标签,以确定其优先级(例如,info、warning 或 error)。

启用消息

消息是通过一个 中间件 类和相应的 上下文处理器 来实现的。

由 django-admin startproject 创建的默认 settings.py 已经包含了启用消息功能所需的所有配置:

  • ‘django.contrib.messages’ 在 INSTALLED_APPS 中。

  • MIDDLEWARE 包含 ‘django.contrib.session.middleware.SessionMiddleware’ 和 ‘django.contrib.message.middleware.MessageMiddleware’。

默认的 存储后端 依赖于 会话。这就是为什么 SessionMiddleware 必须被启用,并且出现在 MIDDLEWARE 中 MessageMiddleware 之前。

  • 在你的 TEMPLATES 配置中定义的 DjangoTemplates 后端的 '‘context_processors’ 选项中包含 ‘django.contrib.messages.context_processors.messages’。

如果你不想使用消息,你可以从你的 INSTALLED_APPS 中删除 ‘django.contrib.messages’,从 MIDDLEWARE 中删除 MessageMiddleware 行,从 TEMPLATES 中删除 messages 上下文处理器。

七、序列化

序列化 Django 对象

Django 的序列化框架提供了一种将 Django 模型“翻译”为其他格式的机制。通常,这些其他格式将基于文本,并用于在网络上发送 Django 数据,但是序列化程序可以处理任何格式(无论是否基于文本)。

序列化数据
在最高层面,你可以像这样序列化数据:

from django.core import serializers
data = serializers.serialize("xml", SomeModel.objects.all())

serialize 函数的参数是数据序列化的目标格式 (查看 序列化格式)和用来序列化的 QuerySet。(实际上,第二个参数可以是任何生成 Django 模型实例的迭代器,但它几乎总是一个QuerySet)。

django.core.serializers.get_serializer(format)

你也可以直接使用序列化器对象:

XMLSerializer = serializers.get_serializer("xml")
xml_serializer = XMLSerializer()
xml_serializer.serialize(queryset)
data = xml_serializer.getvalue()

如果要将数据直接序列化到类似文件的对象(包括 HttpResponse)这将很有用:

with open("file.xml", "w") as out:
    xml_serializer.serialize(SomeModel.objects.all(), stream=out)

八、会话

如何使用会话

Django 是支持匿名会话的。会话框架允许您基于每个站点访问者存储和检索任意数据。它在服务器端存储数据并提供cookie的发送和接收。Cookie包含会话ID - 而不是数据本身(除非您使用基于cookie的后端)。

打开会话

会话通过配置一个中间件实现的

为了打开会话,需要做下面的操作

编辑设置中的 MIDDLEWARE,并确保他包含了 ‘django.contrib.sessions.middleware.SessionMiddleware’。通过 django-admin startproject 创建的默认 settings.py 文件是已经打开了 SessionMiddleware 这项设置的。
如果你不想使用会话功能,你可以从配置的 MIDDLEWARE 中删除 `SessionMiddleware,并且从 INSTALLED_APPS 中删除 ‘django.contrib.sessions’。它将会为您节省一点开销。

九、站点地图

站点地图框架

Django 自带了一个高级站点地图生成框架来创建 sitemap XML 文件。

概况

站点地图是你网站上的一个 XML 文件,它告诉搜索引擎索引器你页面的变化频率,以及某些页面相对于你网站上其他页面的“重要性”。这些信息有助于搜索引擎对你的网站进行索引。

Django 站点地图框架通过让你用 Python 代码表达这些信息以自动创建这个 XML 文件。

它的工作原理很像 Django 的 联合框架。要创建一个站点地图,写一个 Sitemap 类,并将其指向你的 URLconf。

安装

要安装站点地图应用,请按照以下步骤进行:

  • 将 ‘django.contrib.sitemaps’ 添加到你的 INSTALLED_APPS 配置中。
  • 确保你的 TEMPLATES 配置中包含一个 DjangoTemplates 后端,其 APP_DIRS 选项设置为 True。默认有一个后端,所以你只需要改变这个配置就可以了。
  • 确保你已经安装了 sites framework。

(注意:站点地图应用不会安装任何数据库表。它需要进入 INSTALLED_APPS 的唯一原因是为了让 Loader() 模板加载器能够找到默认模板。)

初始化

views.sitemap(request, sitemaps, section=None, template_name='sitemap.xml', content_type='application/xml')

要在你的 Django 网站上激活站点地图的生成,在你的 URLconf 中添加这一行:

from django.contrib.sitemaps.views import sitemap

path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
     name='django.contrib.sitemaps.views.sitemap')

十、静态文件管理

staticfiles 应用

django.contrib.staticfiles 从你的每一个应用程序(以及你指定的任何其他地方)收集静态文件到一个单一的位置,可以很容易地在生产中服务。

关于静态文件应用的介绍和一些使用示例,请参见 管理静态文件(比如图片、JavaScript、CSS)。关于部署静态文件的指南,请参见 部署静态文件。

配置
关于以下配置,请参见 静态文件配置:

STATIC_ROOT
STATIC_URL
STATICFILES_DIRS
STATICFILES_STORAGE
STATICFILES_FINDERS

管理命令

django.contrib.staticfiles 公开了三个管理命令。

collectstatic
django-admin collectstatic

将静态文件收集到 STATIC_ROOT 中。

十一、数据验证

编写验证器

验证器是一个可调用对象,它接收一个值,如果它不符合某些标准,就会引发一个 ValidationError。验证器对于在不同类型的字段之间重用验证逻辑非常有用。

例如,这里有一个只允许偶数的验证器:

from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError(
            _('%(value)s is not an even number'),
            params={'value': value},
        )

你可以通过字段的 validators 参数将其添加到模型字段:

from django.db import models

class MyModel(models.Model):
    even_field = models.IntegerField(validators=[validate_even])

因为在验证器运行之前,值已经被转换为 Python,你甚至可以在表单中使用相同的验证器:

from django import forms

class MyForm(forms.Form):
    even_field = forms.IntegerField(validators=[validate_even])

你也可以使用一个带有 call() 方法的类,用于更复杂或可配置的验证器。 RegexValidator,例如,使用了这种技术。如果在 validators 模型字段选项中使用了一个基于类的验证器,你应该通过添加 deconstruct() 和 eq() 方法来确保它是 可由迁移框架序列化。

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

huidaoli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值