模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
Django 安装
Windows下安装 Django
如果你还未安装Python环境需要先下载Python安装包。
1、Python 下载地址:https://www.python.org/downloads/
2、Django 下载地址:https://www.djangoproject.com/download/
注意:目前Django 1.6.x以上版本已经完全兼容Python 3.x。
Django 安装
下载 Django 压缩包,解压并和Python安装目录放在同一个根目录,进入 Django 目录,执行python setup.py install,然后开始安装,Django将要被安装到Python的Lib下site-packages。
[图片上传失败...(image-697485-1530838002396)]
然后是配置环境变量,将这几个目录添加到系统环境变量中: C:\Python33\Lib\site-packages\django;C:\Python33\Scripts。 添加完成后就可以使用Django的django-admin.py命令新建工程了
[图片上传失败...(image-a01c57-1530838002396)]
Linux 上安装 Django
yum 安装方法
以下安装位于 Centos Linux 环境下安装,如果是你的 Linux 系统是 ubuntu 请使用 apt-get 命令。
默认情况下 Linux 环境已经支持了Python。你可以在终端输入Python命令来查看是否已经安装。
Python 2.7.3 (default, Aug 1 2012, 05:14:39)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
安装 setuptools
命令:
yum install python-setuptools
完成之后,就可以使用 easy_install 命令安装 django
easy_install django
之后我们在python解释器输入以下代码:
[root@solar django]# python
Python 2.7.3 (default, May 15 2014, 14:49:08)
[GCC 4.8.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 6, 5, 'final', 0)
>>>
我们可以看到输出了Django的版本号,说明安装成功。
Windows下安装django
pip 命令安装方法
pip install Django
如果 pip < 1.4,安装方法如下:
pip install https://www.djangoproject.com/download/1.11a1/tarball/
Django与Python版本对应的关系
[图片上传失败...(image-c8fe70-1530838002396)]
安装Django 1.11.4
pip install Django==1.11.4
检查是否安装成功
输入以下命令进行检查:
>>> import django
>>> django.get_version() #获取当前的版本号
[图片上传失败...(image-9beef2-1530838002396)]
如果输出了Django的版本号说明安装正确。
创建Django项目
创建第一个项目
使用 django-admin 来创建 HelloWorld 项目:
在合适位置创建一个目录
打开黑屏终端进入到上一步创建的目录下
django-admin startproject HelloWorld(项目名)
创建完成后我们可以查看下项目的目录结构:
$ cd HelloWorld/
$ tree
|-- HelloWorld
| |-- __init__.py
| |-- settings.py
| |-- urls.
| `-- wsgi.py
`-- manage.py
目录说明:
- HelloWorld: 项目的容器。
- manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
- HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
- HelloWorld/settings.py: 该 Django 项目的设置/配置。
- HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
- HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
接下来我们进入 HelloWorld 目录输入以下命令,启动服务器:
python manage.py runserver 0.0.0.0:8000
启动django后,不能访问,报400错误。
原因:没有开启允许访问
处理:编辑HelloWorld目录下setting.py ,把其中的
ALLOWED_HOSTS=[]改成ALLOWED_HOSTS=['*'] ##* 表示任意地址。
Django 模型
Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。
Django 为这些数据库提供了统一的调用API。 我们可以根据自己业务需求选择不同的数据库。
MySQL 是 Web 应用中最常用的数据库。
数据库配置
修改项目的__init__ 的文件 添加
import pymysql
pymysql.install_as_MySQLdb()
我们在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:
HelloWorld/HelloWorld/settings.py: 文件代码:82行
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 或者使用 mysql.connector.django
'NAME': '数据库名称',
'USER': '用户名',
'PASSWORD': '密码',
'HOST':'主机名',
'PORT':'端口号',
}
}
这里添加了中文注释,所以你需要在 HelloWorld/settings.py 文件头部添加 # -*- coding: UTF-8 --*。
上面包含数据库名称和用户的信息,它们与 MySQL 中对应数据库和用户的设置相同。Django 根据这一设置,与 MySQL 中相应的数据库和用户连接起来。
定义模型
创建 APP
Django规定,如果要使用模型,必须要创建一个app。我们使用以下命令创建一个 TestModel 的 app:
django-admin startapp TestModel
目录结构如下:
HelloWorld
|-- TestModel
| |-- __init__.py
| |-- admin.py 配置后台
| |-- models.py 模型
| |-- tests.py 测试
| `-- views.py 视图
设计表
班级表结构 grades
班级名称 gname
成立时间 gdate
女生总数 ggirlnum
男生总数 gboynum
是否删除 isDelelte
学生表结构 students
学生姓名 sname
学生性别 ssex
学生年龄 sage
学生简介 scontend
是否删除 isDelete
我们修改 TestModel/models.py 文件,代码如下:
HelloWorld/TestModel/models.py: 文件代码:
# models.py
from django.db import models
class Grades(models.Model):
gname = models.CharField(max_length=20)
gdate = models.DateTimeField()
ggirlnum = models.IntegerField()
gboynum = models.IntegerField()
isdelete = models.BooleanField(default=False)
class Students(models.Model):
sname = models.CharField(max_length=20)
ssex = models.BooleanField(default=True)
sage = models.IntegerField()
scontented = models.CharField(max_length=20)
isdelete = models.BooleanField(default=False)
sgrade = models.ForeignKey("Grades")
以上的类名代表了数据库表名,且继承了models.Model,类里面的字段代表数据表中的字段(name),数据类型则由CharField(相当于varchar)、DateField(相当于datetime), max_length 参数限定长度。
在settings.py文件中,将TestModel应用加入到INSTALLED_APPS选项中
接下来在settings.py中找到INSTALLED_APPS这一项,如下:
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'TestModel', # 添加此项 你的项目名称
)
在命令行中运行:
$ python manage.py makemigrations TestModel # 让 Django 知道我们在我们的模型有一些变更 #在migrations目录下生成一个迁移文件,此时数据库中还没有生成数据表
$ python manage.py migrate #m 创建表结构
#$ python manage.py migrate TestModel # 创建表结构
看到几行 "Creating table…" 的字样,你的数据表就创建好了。
Creating tables ...
……
Creating table TestModel_test #我们自定义的表
……
表名组成结构为:应用名_类名(如:TestModel_test)。
注意:尽管我们没有在models给表设置主键,但是Django会自动添加一个id作为主键。
测试数据库
(一) 进入到python shell
执行 python manage.py shell
(二) 引入包
from myApp.models import Grades,Students #导入models的类
from django.utils import timezone #导入时区(如果存在写入时间的字段)
#from datetime import * #导入时间模块
(三) 查询所有数据
类名.objects.all()
如 :Grades.objects.all()
(四) 添加数据
对象 = 类名()
如: g = Grades()
g.字段名 = 值
保存
g.save()
注意:如果给gdate添加时间应该设置为 timezone.now() 否则会报错
解决办法:
g.gdate = timezone.now()
[图片上传失败...(image-493dab-1530838002396)]
(五) 查看某个对象
类名.objects.get(pk=2) #pk 相当于自增id
如果返回的是对象 那么在models里面的类 添加
def __str__(self):
return self.sname
(六) 修改数据
模型对象.属性 = 新值
如 : g = Grades.objects.get(pk=2)
g.字段名= 值
g.save()
(七) 删除数据
模型对象.delete()
如 : g = Grades.objects.get(pk=2)
g.delete()
注意:物理删除,数据库中的表里的数据被删除了
(八) 给关联的表添加数据
>>> stu = Students()
>>> stu.sname = "薛艳梅"
>>> stu.sgender = False
>>> stu.sage = 20
>>> stu.scontend = "我叫薛艳梅"
>>> stu.sgrade = grade1 #为表 grade1对象 grade1 = Grades.objects.get(pk=1)或者 直接等于grade的自增id
>>> stu.save()
(九) 关联对象
获得关联对象的集合
需求:获取python04班级的所有学生
如:
g = Grades.objects.get(pk=1)
g.students_set.all() #获取所有g表关联Students表里面所有的数据 类名首字母小写
格式:对象.类名_set.all()
Admin站点管理
一 配置Admin应用
在settinngs.py文件中的INSTALLED_APPS中添加'django.contrib.admin' 默认是已经添加好的
二 创建管理员用户
执行 python manage.py createsuperuser
依次输入用户名、邮箱、密码
三 配置中国的时区
修改settings.py文件
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
四 管理数据表
(1) 修改admin.py文件 配置后台显示页面
from .models import Grades,Students
# 注册
admin.site.register(Grades)
admin.site.register(Students)
(2) 配置后台成绩的页面显示
from .models import Grades,Students
# 注册
class GradesAdmin(admin.ModelAdmin):
#列表页属性
#显示字段
list_display = ['pk', 'gname', 'gdate', 'ggirlnum', 'gboynum', 'isdelete']
#过滤字段
list_filter = ['gname']
#搜索字段
search_fields = ['gname']
#分页
list_per_page = 5
# 添加、修改数据的时候 属性的先后顺序
# fields = ['ggirlnum','gboynum','gname','gdate','isdelete']
#添加、修改数据的时候 给属性分组
fieldsets = [
("num",{"fields":['ggirlnum','gboynum']}),
("base",{"fields":['gname','gdate','isdelete']}),
]
admin.site.register(Grades, GradesAdmin)
注意:fields与fieldsets不能同时使用
(3) 关联对象
需求:在创建一个班级时可以直接添加几个学生
#TabularInline 横着显示添加学生布局
#StackedInline #竖着显示添加学生的布局
class StudentsInfo(admin.TabularInline):
model = Students
extra = 2 #2带表添加的学生的个数
class GradesAdmin(admin.ModelAdmin):
inlines = [StudentsInfo]
(4) 布尔值显示问题(显示成男女)
class StudentsAdmin(admin.ModelAdmin):
def gender(self):
if self.ssex:
return "男"
else:
return "女"
#设置页面列的名称
gender.short_description = "性别"
list_display = ['pk','sname','sage',gender,'scontented','sgrade','isdelete']
list_per_page = 10
# 执行动作的位置 搜索框的上下位置
actions_on_top = False
actions_on_bottom = True
admin.site.register(Students,StudentsAdmin)
(5) 使用装饰器完成注册
@admin.register(tudents)
class StudentAdmin(admin.ModelAdmin):
pass
@admin.register(Students)
class StudentsAdmin(admin.ModelAdmin):
pass
#admin.site.register(Students,StudentsAdmin)
# admin.site.register(Grades, GradesAdmin)
五 配置控制器
概述:在django中,视图对web请求进行回应
视图就是一个python函数,在views.py文件中定义
(1) 定义控制器
在myApp下的views.py
HelloWorld
|-- myApp
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- models.py
| |-- views.py
| |-- ....py
|-- projct
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| |-- urls.pyc
| |-- view.py
| |-- view.pyc
| |-- wsgi.py
| `-- wsgi.pyc
|-- manage.py
`-- templates
`-- hello.html
(2) 配置 urls.py 路由
修改project目录下的urls.py文件
from django.conf.urls import url,include
from django.contrib import admin #导入后台登录模块
from myApp import views
urlpatterns = [
url(r'^admin/', admin.site.urls), #访问后台的路由
#url(r'^', include('myApp.urls')), #访问在myApp中路由的文件(自定义的)
url(r'^hello$', views.hello),
url(r'^setdb$', views.setdb),
url(r'^getDate$', views.getDate),
]
在myApp下单独创建一个urls.py文件
from django.conf.urls import url
from myApp import views
urlpatterns = [
url(r'^$',views.index,name='index'),
]
(3) HttpResponse 不通过模板 而是直接通过控制器进行返回 (一般作为调试使用)
from django.http import HttpResponse
from django.http import HttpResponse
def index(request):
return HttpResponse("我是首页") #不通过模板 而是直接通过控制器进行返回
(4) 将数据返回给视图进行渲染
数据是字典形式
from django.shortcuts import render #导入发送到模板的方法
from datetime import datetime #导入时间模块
from django.http import HttpResponse
from myApp.models import Grades #导入班级的类
def hello(request):
context = {}
context['hello'] = 'Hello World!'
context['bool'] = True
context['myFor'] = 'abcdefg'
return render(request, 'hello.html', context)
# 数据库操作
def setdb(request):
g = Grades()
g.gname = "python1701"
g.gdate = datetime.now()
g.ggirlnum = 10
g.gboylnum = 40
g.save()
return HttpResponse("<p>数据添加成功!</p>") #直接进行返回
def getDate(request):
dataList = {}
dataList['con'] = Grades.objects.all()
return render(request,'show.html',dataList)
六 配置 templates 模板
概述:模板是HTML页面,可以根据视图中传递过来的数据进行填充
(1)配置模板路径
修改settings.py文件下的TEMPLATES 59行
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 'DIRS': [],
'DIRS': [BASE_DIR+"/templates",]或[os.path.join(BASE_DIR,'templates')], #配制模板路径
(2) 模板语法
<h1>{{ hello }}</h1> 双{} 代表存储的是变量
Django 模板标签
if/else 标签
基本语法格式如下:
{% if condition %}
... display
{% endif %}
或者:
{% if condition1 %}
... display 1
{% elif condition2 %}
... display 2
{% else %}
... display 3
{% endif %}
根据条件判断是否输出。if/else 支持嵌套。
{% if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),例如:
{% if athlete_list and coach_list %}
athletes 和 coaches 变量都是可用的。
{% endif %}
for 标签
{% for %} 允许我们在一个序列上迭代。
与Python的 for 语句的情形类似,循环语法是 for X in Y ,Y是要迭代的序列而X是在每一个特定的循环中使用的变量名称。
每一次循环中,模板系统会渲染在 {% for %} 和 {% endfor %} 之间的所有内容。
例如,给定一个运动员列表 athlete_list 变量,我们可以使用下面的代码来显示这个列表:
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
给标签增加一个 reversed 使得该列表被反向迭代:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
可以嵌套使用 {% for %} 标签:
{% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
{% for sport in athlete.sports_played %}
<li>{{ sport }}</li>
{% endfor %}
</ul>
{% endfor %}
ifequal/ifnotequal 标签
{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。
下面的例子比较两个模板变量 user 和 currentuser :
{% ifequal user currentuser %}
<h1>Welcome!</h1>
{% endifequal %}
和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签:
{% ifequal section 'sitenews' %}
<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}
注释标签
Django 注释使用 {# #}。
{# 这是一个注释 #}
过滤器
模板过滤器可以在变量被显示前修改它,过滤器使用管道字符,如下所示:
{{ name|lower }}
{{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。
过滤管道可以被* 套接* ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入:
{{ my_list|first|upper }}
以上实例将第一个元素并将其转化为大写。
有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:
{{ bio|truncatewords:"30" }}
这个将显示变量 bio 的前30个词。
其他过滤器:
addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。
-
date : 按指定的格式字符串参数格式化 date 或者 datetime 对象,实例:
{{ pub_date|date:"F j, Y" }}
length : 返回变量的长度。
include 标签
{% include %} 标签允许在模板中包含其它的模板的内容。
下面这个例子都包含了 nav.html 模板:
{% include "nav.html" %}
模板继承
模板可以用继承的方式来实现复用。
接下来我们先创建之前项目的 templates 目录中添加 base.html 文件,代码如下:
HelloWorld/templates/base.html 文件代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<h1>Hello World!</h1>
<p>菜鸟教程 Django 测试。</p>
{% block mainbody %}
<p>original</p>
{% endblock %}
</body>
</html>
以上代码中,名为 mainbody 的 block 标签是可以被继承者们替换掉的部分。
所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。
hello.html 中继承 base.html,并替换特定 block,hello.html 修改后的代码如下:
{% extends "base.html" %}
{% block mainbody %}<p>继承了 base.html 文件</p>
{% endblock %}
模型 model
Django对各种数据库提供了很好的支持,Django为这些数据库提供了统一的调用API,可以根据不同的业务需求选择不同的数据库
一 配置数据库
修改工程目录下的_init_.py文件
import pymysql
pymysql.install_as_MySQLdb()
修改settings.py文件
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': "kaishen",
'USER':'root',
'PASSWORD':'sunck',
'HOST':'localhost',
'PORT':'3306',
}
}
Zh
二 开发流程
配置数据库
-
定义模型类
一个模型类都在数据库中对应一张数据表
生成迁移文件
-
执行迁移生成数据表
python manage.py makemigrations TestModel python manage.py migrate # 创建表结构
使用模型类进行增删改查(crud)操作
三 ORM
概述
对象-关系-映射
任务
- 根据对象的类型生成表结构
- 将对象、列表的操作转换为sql语句
- 将sql语句查询到的结果转换为对象、列表
优点
极大的减轻了开发人员的工作量,不需要面对因数据库的变更而修改代码
图解
[图片上传失败...(image-31c2b-1530838045053)]
四 定义模型
(1) 模型、属性、表、字段间的关系
一个模型类在数据库中对应一张表,在模型类中定义的属性,对应该模型对照表中的一个字段
(2) 定义属性
详情请见定义属性.md
(3) 创建模型类
(4) 元选项
在模型类中定义Meta类,用于设置元信息
-
db_table
定义数据表名,推荐使用小写字母,数据表名默认为项目名小写_类名小写
-
ordering
对象的默认排序字段,获取对象的列表时使用
- ordering['id'] 升序
- ordering['-id'] 降序
注意:排序会增加数据库的开销
实例
class Students(models.Model):
sname = models.CharField(max_length=20)
sgender = models.BooleanField(default=True)
sage = models.IntegerField(db_column="age")
scontend = models.CharField(max_length=20)
isDelete = models.BooleanField(default=False)
# 关联外键
sgrade = models.ForeignKey("Grades")
def __str__(self):
return self.snamea
lastTime = models.DateTimeField(auto_now=True)
createTime = models.DateTimeField(auto_now_add=True)
class Meta:
db_table="students" #手动起表名
ordering=['id'] #按照id升序
五 模型成员
类属性
(1) objects
是Manager类型的一个对象,作用是与数据库进行交互
当定义模型类是没有指定管理器,则Django为模型创建一个名为objects的管理器
(2) 自定义管理器
class Students(models.Model):
# 自定义模型管理器
# 当自定义模型管理器,objects就不存在了
stuObj = models.Manager()
当为模型指定模型管理器,Django就不在为模型类生成objects模型管理器
(3) 自定义管理器Manager类
模型管理器是Django的模型进行与数据库进行交互的接口,一个模型可以有多个模型管理器
作用
向管理器类中添加额外的方法
修改管理器返回的原始查询集
重写get_queryset()方法
代码示例
class StudentsManager(models.Manager):
def get_queryset(self):
return super(StudentsManager,self).get_queryset().filter(isDelete=False)#将isDelete为 False的数据进行返回
class Students(models.Model):
# 自定义模型管理器
# 当自定义模型管理器,objects就不存在了
stuObj = models.Manager()
stuObj2 = StudentsManager()
创建对象
(1) 目的
向数据库中添加数据
当创建对象时,django不会对数据库进行读写操作,当调用save()方式时才与数据库交互,将对象保存到数据库表中
注意:__init__方法已经在父类models.Model中使用,在自定义的模型中无法使用
(2) 方法
1. 在模型类中增加一个类方法
实例
class Students(models.Model):
#定义一个类方法创建对象
@classmethod
def addStudents(cls, name, age, gender, contend, grade, isD=False):
stu = cls(sname = name, sage = age, sgender = gender, scontend = contend, sgrade = grade, isDelete=isD)
return stu
在视图中使用
def addStudents(request):
g = Grades.objects.get(pk=1)
# return HttpResponse(g)
stu = Students.addStudents('李四',10,'0','我是类方法添加的',g)
stu.save()
return HttpResponse('添加完成')
2. 在定义管理器中添加一个方法
实例
class StudentsManager(models.Manager):
def get_queryset(self):
return super(StudentsManager,self).get_queryset().filter(isDelete=False)
def addStudents(self, name, age, ssex, contend, grade, lastT, createT, isD=False):
stu = self.model()
# print(type(grade))
stu.sname = name
stu.sage = age
stu.ssex = ssex
stu.scontend = contend
stu.sgrade = grade
return stu
class Students(models.Model):
stuObj2 = StudentsManage() #添加了自己过滤方法的类
在视图中使用
def addStudents2(request):
g = Grades.objects.get(pk=1)
stu = Students.stuObj2.addStudents('w1W',30,'0','我是类方法添加的',g)
stu.save()
return HttpResponse('***')
六 模型查询
概述
- 查询集表示从数据库获取的对象集合
- 查询集可以有多个过滤器
- 过滤器就是一个函数,基于所给的参数限制查询集结果
- 从sql角度来说,查询集合select语句等价,过滤器就像where条件
(1) 查询集
在管理器上调用过滤器方法返回查询集
查询集经过过滤器筛选后返回新的查询集,所以可以写成链式调用
Entry.objects.filter (
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.now()
... ).filter (
... pub_date__gte=datetime(2005, 1, 1)
... )
最后返回的QuerySet是headline like 'What%' and put_date<now() and pub_date>2005-01-01
惰性执行:创建查询集不会带来任何数据的访问,直到调用数据时,才会访问数据库
QuerySet是延迟加载
只在使用的时候才会去访问数据库,如下:
>>> q = Entry.objects.filter (headline__startswith="What")
>>> q = q.filter (pub_date__lte=datetime.now())
>>> q = q.exclude(body_text__icontains="food")
>>> print q
在print q时才会访问数据库。
直接访问数据的情况
- 迭代
- 序列化
- 与if合用
(2) 返回查询集的方法称为过滤器
1. all() 返回查询集中的所有数据
Students.stuObj2.all() #获取Students类里面所有数据
Students.stuObj2.all()[0:5] #获取Students类里面前5条数据数据
Students.stuObj2.all()[(int(page)-1)*5:int(page)*5] 分页
url(r'^stu/(\d+)/$', views.stupage),
def stupage(request,page):
dataList = {}
dataList['stu'] = Students.stuObj2.all()[(int(page)-1)*5:int(page)*5]
2. filter() 返回符合条件的数据
得到一个集合对象
返回一个与参数匹配的QuerySet,相当于等于(=).
一个条件的情况
- filter(键=值)
多个条件 并且的关系
-
filter(键=值, 键=值)
Students.stuObj.filter(sname__contains='孙',ssex=True)
filter(键=值).filter(键=值)
实例
表.objects.filter(id=2) ---[obj1,] ,得到一个集合对象,集合里只有一个,跟上first()或者【0】取到一个具体对象
3. exclude() 过滤掉符合条件的数据
返回一个与参数不匹配的QuerySet,相当于不等于(!=)
Students.stuObj.exclude(sname__contains='孙') #过滤掉sname字段值包含孙的数据
4. order_by(字段名) 排序
实例
按照id升序
Students.stuObj2.order_by('pk')
按照id降序
Students.stuObj2.order_by('-pk')
扩展
如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参数就可以了,如下:
Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]
我们还可以指定逆向排序,在前面加一个减号 - 前缀:
Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]
尽管很灵活,但是每次都要用 order_by() 显得有点啰嗦。 大多数时间你通常只会对某些 字段进行排序。 在这种情况下,Django让你可以指定模型的缺省排序方式:
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
class Meta:
ordering = ['name'] #在查询数据的时候 就按照name升序
现在,让我们来接触一个新的概念。 class Meta,内嵌于 Publisher 这个类的定义中(如果 class Publisher 是顶格的,那么 class Meta 在它之下要缩进4个空格--按 Python 的传统 )。你可以在任意一个 模型 类中使用 Meta 类,来设置一些与特定模型相关的选项。 在 附录B 中有 Meta 中所有可选项的完整参考,现在,我们关注 ordering 这个选项就够了。 如果你设置了这个选项,那么除非你检索时特意额外地使用了 order_by(),否则,当你使用 Django 的数据库 API 去检索时,Publisher对象的相关返回值默认地都会按 name 字段排序。
5. reverse() 对查询结果反向排序
注意: 需要先排序order_by(*field),才能反转
Students.stuObj.all().order_by('pk').reverse()
6. distinct() 从返回结果中剔除重复纪录
不支持
Students.stuObj.all().order_by('pk').distinct('sage')
#DISTINCT ON fields is not supported by this database backend 数据库不支持
7. values() 一条数据就是一个对象(字典),返回一个列表
实例
Students.stuObj.values() #得到QuerySet集合是所有的记录,,里面是字典,key是字段,value是值,
参数:为要返回的字段名和值 (默认返回全部)
类名.objects.values('title','price')#可以加多个字段
连贯操作
Book.objects.filter(id__gt=4).values('title')#查询id大于4,的title,得到一个对象集合,显示书名
#<QuerySet [{'title': '富爸爸'}]>
8. value_list ( ) 得到一个元组格式的数据,只有字段的值,
# ----valuelist 方法查询 ,得到一个元组格式数据,只有字段的值,
b4 = Book.objects.filter(id__gt=3).values_list('title')
print(b4)
#<QuerySet [('追风筝的人',), ('富爸爸',)]>
(3) 返回单个数据
-
get() 返回一个满足条件的对象
注意:
- 如果没有找到符合条件的对象,会引发"模型类.DoesNotExist"异常
- 如果找到多个对象,会引发"模型类.MultipleObjectsReturned"异常
Students.stuObj.get(pk=1) #查询id为1的数据 >>> Entry.objects.order_by('headline')[0] #这是取按headline字段排序后的第一个对象。 >>> Entry.objects.order_by('headline')[0:1].get () #这和上面的等同的。
表.objects.get(id=2)---obj,得到一个单独的对象,确定能找到,可以用,如果找到多个或者没有的,都报错。
-
count() 返回当前查询集中的对象个数
Students.stuObj.filter(ssex=True).count() #性别为1的人数
-
first() 返回查询集中的第一个对象
Students.stuObj.filter(ssex=True).first() #返回 性别为1 的第一条数据 QuerySet.first(),与get()方法一样,都得到一个对象
-
last() 返回查询集中的最后一个对象
Students.stuObj.filter(ssex=True).last() #返回 性别为1 的最后一条数据 QuerySet.last(),与get()方法一样,都得到一个对象
-
exists() 如果QuerySet包含数据,就返回True,否则返回False ---只判断是否有记录
Students.stuObj.exists() Students.stuObj.filter(isDelete=False).exists()
(4) 限制查询集
查询集返回列表,可以使用下标的方法进行限制,等同于sql中的limit语句
studentsList = Students.stuObj2.all()[0:5]
>>> Entry.objects.order_by('headline')[0]
#这是取按headline字段排序后的第一个对象。
>>> Entry.objects.order_by('headline')[0:1].get ()
#这和上面的等同的。
注意:下标不能是负数
(5) 查询集的缓存
每个查询集都包含一个缓存,来最小化的对数据库访问
在新建的查询集中,缓存首次为空,第一次对查询集求值,会发生数据缓存,django会将查询出来的数据做一个缓存,并返回查询结构,以后的查询直接使用查询集的缓存
以下语句要查询两次数据库
print([c.code for c in Catalog.objects.all()])
print([c.name for c in Catalog.objects.all()])
以下语句可以用上django的缓存机制,只用访问一次数据
cl = Catalog.objects.all();
print([c.code for c in cl])
print([c.name for c in cl])
最好是把所有数据,先遍历一边,在进行访问:
cl = Catalog.objects.all();
[c.code for c in cl]
cl[1]; # 从缓存读数据
cl[1]; # 从缓存读数据
以下四种方法将对象集放进缓存
[c for c in queryset]
bool(queryset)
c in queryset
list(queryset)
# 在对象查询集中使用数据索引要格外小心,因为每次索引都会访问1次数据库,即使是访问同一个元素。注意如下语句:
cl = Catalog.objects.all();
cl[1]; # 访问1次数据库
cl[1]; # 在访问1次数据库
(6) 字段查询
概述
实现了sql中的where语句,作为方法filter()、exclude()、get()的参数
语法
属性名称__比较运算符=值
外键
属性名_id
Students.stuObj2.filter(sgrade_id=1)
(7) 字段值修改
update 和save方法区别
Book.objects.filter(id=5).update(price=1000) #直接更新update 是QuerySet集合对象的方法,推荐
save方法
book = Book.objects.get(id=5)
book.price=400
book.save()
比较运算符
-
完全匹配运算符
__exact 判断,大小写敏感
__iexact 精确等于 忽略大小写 ilike 'aaa'
精确等于 like 'aaa'
filter(sname_exact=False)
Students.stuObj.filter(sname = 'aw1w') #不区分大小写 Students.stuObj.filter(sname__exact = 'aw1w') #不区分大小写 #等同于SELECT ... WHERE headline = 'aw1w'; Students.stuObj.filter(sname__iexact = 'aw1w') #不区分大小写 Blog.objects.get (id__exact=14) # Explicit form >>> Blog.objects.get (id=14) # __exact is implied 这两种方式是等同的,都是查找id=14的对象。
注意,如果在exact运算符右侧指定None,那么在翻译成SQL语句时就会按照SQL中的NULL进行比较。
Students.stuObj.filter(sname__exact=None) 等价于SQL语法: select * from students where sname is null
-
__contains 是否包含,大小敏感
包含 like '%aaa%'
__icontains 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains
studentsList = Students.stuObj2.filter(sname__contains="孙") #查询sname属性中包含孙的所有数据 Students.stuObj.filter(sname__icontains='w') #不区分大小写 等同于SELECT ... WHERE sname LIKE '%w%';
-
__startswith、__endswith 以value开头或结尾,大小写敏感
__istartswith 以...开头 忽略大小写
__iendswith 以...结尾,忽略大小写
studentsList = Students.stuObj2.filter(sname__startswith="孙") #sname字段以孙子开头的所有数据 studentsList = Students.stuObj2.filter(sname__endswith="孙") #sname字段以孙子结尾的所有数据 #不区分大小写 Students.stuObj.filter(sname__istartswith='a') Students.stuObj.filter(sname__iendswith='a') startswith 等同于sql语句中的 name like 'Lennon%', endswith等同于sql语句中的 name like '%Lennon'.
以上四个在前面加上i,就表示不区分大小写iexact、icontains、istartswith、iendswith
-
__isnull 是否为空
__isnull=True/False
filter(sname__isnull=False)
-
__in 是否包含在范围内
studentsList = Students.stuObj2.filter(pk__in=[2,4,6,8,10]) #取反 not-in studentsList = Students.stuObj2.exclude(pk__in=[2,4,6,8,10])
-
__range() 范围bettwen and
__range(start_date, end_date) models.Tb1.objects.filter(id__range=[1, 2])
-
__gt、__gte、__lt、__lte 属性名__gt/gte/lt/lte
Students.stuObj2.filter(sage__gt=30) #年龄大于30的数据
-
__year、__month、__day、__week_day、__hour、__minute、__second
Students.stuObj2.filter(lastTime__year=2017) #查询lastname字段的年份为2017的所有数据 Entry.objects.filter (pub_date__year=2006) 不使用Entry.objects.all().filter (pub_date__year=2006),虽然也能运行,all()最好再获取所有的对象时使用。 slect * from entry where pub_date_year='2006' 简单日期操作 from datetime import datetime; from datetime import date; print(Catalog.objects.filter(create_date__year=2013)); # 时间的年份为2013年 print(Catalog.objects.filter(create_date__gte=date.today())); # 时间大于今天 print(Catalog.objects.filter(create_date__gte=datetime(2013, 5, 23))); # 时间大于2013年5月23日 print(Catalog.objects.filter(create_date__lte='2013-05-23')) # 时间小于2013年5月23日
聚合函数
使用aggregate()函数返回聚合函数的值
- Avg 平均数
- Count 统计
- Max 最大
- Min 最小
- Sum 求和
实例
from django.db.models import Max
from django.db.models import aggregates
maxAge = Students.stuObj2.aggregate(Max('sage'))
F对象
可以使用模型的A属性与B属性进行比较,
实例
from django.db.models import F,Q
def grades(request):
g = Grades.objects.filter(ggirlnum__gt=F('gboynum'))
print(g)
return HttpResponse("打印属性值ggirlnum大于gboynfum的字段值")
支持F对象的算术运算
Grades.objects.filter(ggirlnum__gt=F('gboynum')+20)
Q对象
概述: 过滤器的方法中的关键字参数,条件为And模式
需求: 进行or查询
Students.stuObj2.filter(Q(pk__lte=3)) #匹配id小于等于 3的数据
Students.stuObj.filter(Q(pk=1)| Q(pk=2)) #查询id为1 或者 为2 的所有数据
用Q对象实现复杂的查询
Q(question__startswith='Who') | Q(question__startswith='What')
等同于WHERE question LIKE 'Who%' OR question LIKE 'What%'
import datetime
Poll.objects.get (
Q(question__startswith='Who'),
Q(pub_date=datetime.date(2018,2,26)) | Q(pub_date=datetime.date(2018,2,26))
)
等同于SELECT * from polls WHERE question LIKE 'Who%' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
取反
Students.stuObj.filter(~Q(pk__in=[1])) #匹配id为1以外的所有数据
注意
-
id/pk/exect
>>> Blog.objects.get (id__exact=14) # Explicit form >>> Blog.objects.get (id=14) # __exact is implied >>> Blog.objects.get (pk=14) # pk implies id__exact 等同于select * from where id=14
注意:pk代表当前的主键 而不是id
-
转义 %
>>> Entry.objects.filter (headline__contains='%') 等同于SELECT ... WHERE headline LIKE '%\%%';
-
Caching and QuerySets
>>> print [e.headline for e in Entry.objects.all()] >>> print [e.pub_date for e in Entry.objects.all()] 应改写为: >> queryset = Poll.objects.all() >>> print [p.headline for p in queryset] # Evaluate the query set. >>> print [p.pub_date for p in queryset] # Re-use the cache from the evaluation.、 这样利用缓存,减少访问数据库的次数。
-
删除
Entry.objects.filter (pub_date__year=2005).delete()
删除所有
Entry.objects.all().delete()
-
一次更新多个值
# Update all the headlines with pub_date in 2007. Entry.objects.filter (pub_date__year=2007).update(headline='Everything is the same') >>> b = Blog.objects.get (pk=1) # Change every Entry so that it belongs to this Blog. >>> Entry.objects.all().update(blog=b) #更改id在2,4,6的sname和ssex的值 Students.stuObj.filter(pk__in=[2,4,6]).update(sname='张三',ssex=1) #更改所有的数据的字段sname和ssex的值 Students.stuObj.all().update(sname='张三',ssex=1) #删除id在2,4,6的sname和ssex的值 Students.stuObj.filter(pk__in=[2,4,6,8,10]).delete() 如果用save()方法,必须一个一个进行保存,需要对其就行遍历,如下: for item in my_queryset: item.save()
注意:save更适合单条数据的更新 update更适合批量次的更新
模型之间的关系
- 1 :1
- 1 :N
- M:N
一、1:1
- 使用models.OneToOneField实现
- 这个字段在哪一个模型中都是可以使用的
- 使用了OneToOne的模型,默认它会随着绑定的数据而进行变化,绑定的数据被删除,它也会被删除
- 添加顺序
- OneToOne绑定的表是主表
- 使用OneToOne的是从表
- on_delete
- models.CASCADE 默认值
默认CASECADE,默认从表数据跟随主表删除(比较暴力,容易出问题,开发中基本不用) - models.PROTECT 保护模式
PROTECT,保护默认,开发中常用(主表数据有从表数据的时候,主表是不能实现删除的) - models.SET_NULL 置空模式
# 在删除的时候模式有5个,默认是伴随删除,setXXX设置为某一值
id_person = models.OneToOneField("Person", on_delete=models.SET_NULL, null=True)#注意 要设置为null为True,因为字段默认不为nully - SETXXX 当删除,设置对应的字段值为YYY
- models.CASCADE 默认值
- PROTECT保护的是主表数据
- SETXXX 设置的是从表
二、1:N
- 使用ForeignKey实现
- 在使用的时候也有on_delete
- on_delete和一对一完全一样
三、M:N
- 使用ManyToMany实现
- 最好使用在从表中,非用户表
- 模型在生成数据表的时候,会生成专门的一张表来维护多对多的关系,关系表
- 关系表中存储的是两张表的id
- 关系表存储多对多的关系
- 主从关系
- 使用ManyToMany的可以认为是从表
- 从表可以管理自己的主表对应数据
- 主表也可以而管理从表中自己对应的数据
- 数据都是一个集合
- add
- remove
- clear
创建多对多关系实例:
-
方式一
from django.db import models
class Class5(models.Model):
name = models.CharField(max_length=32)class Class3(models.Model):
name = models.CharField(max_length=32)class Computer(models.Model):
c5 = models.ForeignKey("Class5")
c3 = models.ForeignKey("Class3")1234567891011 -
方式二
from django.db import models
class Class5(models.Model):
name = models.CharField(max_length=32)class Class3(models.Model):
name = models.CharField(max_length=32)
c5 = models.ManyToManyField("Class5")12345678
多对多关系增加数据
以方式二为例
from django.db import models
# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
# 添加关系,与Class5中id为1的关联
obj.c5.add(1)
# obj.c5.add(1,2,3) # 添加多个关系
# obj.c5.add(*[1,2,3]) # 添加多个关系123456789
多对多关系查询数据
from django.db import models
# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
# 获取所有与当前数据关联的Class5数据
obj.c5.all()123456
多对多关系更新
from django.db import models
# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
# 更新数据
obj.c5.set([1,2,3])1234567
多对多关系删除
from django.db import models
# 先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
# 删除单条数据
obj.c5.remove(1)
# 删除多条数据
obj.c5.remove(1,2,3) # 或者:obj.c5.remove(*[1,2,3])
# 删除所有与当前数据关联的数据
obj.c5.clear()
模型继承
- 抽象
- 公共特性,人和狗
- name,legs
- 不同的职能
- 人享受,人做饭,狗看门,
- 抽象的模型,不会生成表
- 继承自抽象模型的模型,它会在自己的表中包含抽象模型中的字段
- 抽象的模型不能实例化(只能子类实例化)
- 非抽象模型,会生成自己的表
- 非抽象模型也可以继承
- 所有子表的公共数据会在父表中存储
- 子表特有的属性会在自己额外的表中存储,子表和父表通过外键级联在一起
模型中的属性
- 显性(自己直接创建的)
- 隐形的(系统为我们创建的)
- Manager(默认系统自动创建,对应的字段叫objects)
- 自定义管理器
- 继承自models.Manager
- 定义自己的方法
- 添加统一的删除过滤
- 添加创建对象的方法
ORM字段属性的使用
定义属性
概述
·django根据属性的类型确定以下信息
·当前选择的数据库支持字段的类型
·渲染管理表单时使用的默认html控件
·在管理站点最低限度的验证
·django会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则django不会再生成默认的主键列
·属性命名限制
·遵循标识符规则
·由于django的查询方式,不允许使用连续的下划线
库
·定义属性时,需要字段类型,字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中
·使用方式
·导入from django.db import models
·通过models.Field创建字段类型的对象,赋值给属性
逻辑删除
·对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False
字段类型
·AutoField
·一个根据实际ID自动增长的IntegerField,通常不指定如果不指定,一个主键字段将自动添加到模型中
·CharField(max_length=字符长度)
·字符串,默认的表单样式是 TextInput
·TextField
·大文本字段,一般超过4000使用,默认的表单控件是Textarea
·IntegerField
·整数
·DecimalField(max_digits=None, decimal_places=None)
·使用python的Decimal实例表示的十进制浮点数
·参数说明
·DecimalField.max_digits
·位数总数
·DecimalField.decimal_places
·小数点后的数字位数
·FloatField
·用Python的float实例来表示的浮点数
·BooleanField
·true/false 字段,此字段的默认表单控制是CheckboxInput
·NullBooleanField
·支持null、true、false三种值
·DateField([auto_now=False, auto_now_add=False])
·使用Python的datetime.date实例表示的日期
·参数说明
·DateField.auto_now
·每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false
·DateField.auto_now_add
·当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
·说明
·该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
·注意
·auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果
·TimeField
·使用Python的datetime.time实例表示的时间,参数同DateField
·DateTimeField
·使用Python的datetime.datetime实例表示的日期和时间,参数同DateField
·FileField
·一个上传文件的字段
·ImageField
·继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image
字段选项
字段选项
·概述
·通过字段选项,可以实现对字段的约束
·在字段对象时通过关键字参数指定
·null
·如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False
·blanke
·如果为True,则该字段允许为空白,默认值是 False
·注意
·null是数据库范畴的概念,blank是表单验证证范畴的
·db_column
·字段的名称,如果未指定,则使用属性的名称
·db_index
·若值为 True, 则在表中会为此字段创建索引
·default
·默认值
·primary_key
·若为 True, 则该字段会成为模型的主键字段
·unique
·如果为 True, 这个字段在表中必须有唯一值
关系
关系
·分类
·ForeignKey:一对多,将字段定义在多的端中
·ManyToManyField:多对多,将字段定义在两端中
·OneToOneField:一对一,将字段定义在任意一端中
·用一访问多
·格式
·对象.模型类小写_set
·示例
grade.students_set
·用一访问一
·格式
·对象.模型类小写
·示例
·grade.students
·访问id
·格式
·对象.属性_id
·示例
·student.sgrade_id
视图 view
一 视图的概念
(1) 视图的作用
视图接受web请求,并响应web请求
(2) 视图的本质
视图就是一个python中的函数
(3) 响应
-
网页
- 重定向
- 错误视图
- 404
- 500
- 400
JSON数据
二 url配置
路由:
处理URL与函数之间关系的程序称为路由
(1) 配置流程
制定根级url配置文件
settings.py文件中的ROOT_URLCONF(默认实现了)
ROOT_URLCONF = 'project.urls'
ROOT_URLCONF = '当前Django项目名.urls'
(2) urlpatterns
一个url实例的列表
url参数
- Z正则表达式
- 视图名称
- 名称 当前反向解析的name的名称
url匹配正则的注意事项:
- 如果想要从url中获取一个值,需要对正则加小括号
- 匹配正则前方不需要加反斜杠
- 正则前需要加r表示字符串不转义
实例
urlpatterns = [
url(r'^stu/(\d+)/$', views.stupage,name='stupage'),
]
(3) 引入其他url配置
在应用中创建urls.py文件,定义本应用的url配置,在工程urls.py文件中使用include()方法
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('myApp.urls', namespace="myApp"))
]
myApp文件夹下的URL配置s
from django.conf.urls import url
from . import views/fsrom myApp import views 那种引入views都可以
urlpatterns = [
url(r'^$', views.index, name="index"),
]
(4) URL的反向解析
概述: 如果在视图、模板中使用了硬编码链接,在url配置发生改变时,动态生成链接的地址
解决: 在使用链接时,通过url配置的名称,动态生成url地址
作用于: 使用url模板
使用方法:
- 定义url时,需要为include定义namespace属性,为url定义name属性
- 使用时,在模板中使用url标签,在视图中使用reverse函数,根据正则表达式动态生成地址,减轻后期维护成本。
模板中超链接步骤:
1)在项目urls.py中为include定义namespace属性。
url(r’^’,include(‘booktest.urls’,namespace=’booktest’)),
2)在应用的urls.py中为url定义name属性,并修改为fan2。
url(r’^fan2/$’, views.fan2,name=’fan2’),
3)在模板中使用url标签做超链接,此处为templates/booktest/fan1.html文件。
<html>
<head>
<title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan2/">普通fan2</a>
<hr>
反向解析:<a href="{%url 'booktest:fan2'%}">反向解析fan2</a>
</body>
</html>
4)回到浏览器中,后退,刷新,查看源文件,两个链接地址一样
[图片上传失败...(image-80ea03-1530838081153)]
5)在应用的urls.py中,将fan2修改为fan_show。
url(r’^fan_show/$’, views.fan2,name=’fan2’),
6)回到浏览器中,刷新,查看源文件,两个链接地址不一样。
[图片上传失败...(image-ea84ae-1530838081153)]
三 视图函数
(1) 定义视图
本质:一个函数
视图参数:
- 一个HttpRequest的实例
- 通过正则表达式获取的参数
位置: 一般在views.py文件下定义
(2) 错误视图
404视图
找不到网页(url匹配不成功)时返回
在templates目录下定义404.html
request_path 导致错误的网址
实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404页面</title>
</head>
<body>
<h1>页面丢失</h1>
<h2>{{request_path}}</h2>
</body>
</html>
配置settings.py
DEBUG 如果为True永远不会调用404.html页面
settings 第26行
DEBUG = False
500视图
在视图代码中出现错误(服务器代码)
400视图
错误出现在客户的操作
四 HttpRequest对象
(1) 概述
服务器接收http请求后,会根据报文创建HttpRequest对象
视图的第一个参数就是HttpRequest对象
django创建的,之后调用试图时传递给视图
也就是在浏览器请求的时候给视图的数据
(2) 属性
- path 请求的完整路径(不包括域名和端口)
- method 表示请求的方式,常用的有GET、POST
- encoding 表示浏览器提交的数据的编码方式 一般为utf-8
- GET 类似字典的对象,包含了get请求的所有参数
- POST 类似字典的对象,包含了post请求的所有参数
- FILES 类似字典的对象,包含了所有上传的文件
- COOKIES 字典,包含所有的cookie
- session 类似字典的对象,表示当前会话
实例
print(request.path)
print(request.method)
print(request.encoding) #在视图中获取不到
print(request.GET)
print(request.POST)
print(request.FILES)
print(request.COOKIES)
print(request.session)
(3) 方法
is_ajax() 如果是通过XMLHttpRequest发起的,返回True
QueryDict对象
(4) QueryDict对象
request对象中的GET、POST都属于QueryDict对象
方法
get()
- 作用:根据键获取值d
- 只能获取一个值
- 如
www.sunck.wang/abc?a=1&b=2&c=3
getlist()
- 将键的值以列表的形式返回
- 可以获取多个值dx
www.sunck.wang/abc?a=1&a=2&c=3
(5) GET属性
获取浏览器传递过来给服务器的数据
实例
url为 http://127.0.0.1:8000/sunck/get1?a=1&b=2&c=3
def get1(request):
a = request.GET.get('a')
b = request.GET['b']
c = request.GET.get('c')
return HttpResponse(a + " " + b + " " + c)
http://127.0.0.1:8000/sunck/get2?a=1&a=2&c=3
def get2(request):
a = request.GET.getlist('a')
a1 = a[0]
a2 = a[1]
c = request.GET.get('c')
return HttpResponse(a1 + " " + a2 + " " + c)
(6) POST属性
使用表单提交实现post请求
关闭csrf
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',
]
实例
def showregist(request):
return render(request, 'myApp/regist.html')
def regist(request):
name = request.POST.get("name")
gender = request.POST.get("gender")
age = request.POST.get("age")
hobby = request.POST.getlist("hobby")
print(name)
print(gender)
print(age)
print(hobby)
return HttpResponse("post")
五 HttpResponse对象
概述:给浏览器返回数据
HttpRequest对象是由django创建的,HttpResponse对象由程序员创建
(1) 返回用法
不调用模板,直接返回数据
HttpResponse("直接返回给浏览器 不会调用模板")
from django.http import HttpResponse
def index(request):
return HttpResponse("直接返回给浏览器 不会调用模板")
调用模板
使用render方法
原型:render(request, templateName[, context])
作用: 结合数据和模板,返回完整的HTML页面
参数:
- request 请求体对象
- templateName 模板路径
- context 传递给需要渲染在模板上的数据
实例:
def index(request):
return render(request,'myApp/index.html',{'传递给模板的键名':"传递过去的数据"})
(2) 属性
content 表示返回的内容
charset 编码格式
status_code 响应状态码 【ˈstætəs'】
-
_content_type_for_repr 指定输出的MIME类型
MIME类型-在把输出结果传送到浏览器上的时候,浏览器必须启动是党的应用程序来处理这个输出文档。这可以通过多种类型MIME(多功能网际邮件扩充协议)来完成。在HTTP中,MIME类型被定义在Content-Type header中
实例
def showHttResponse(request):
res = HttpResponse()
res.content = b'good' #设置内容
print(res.content)
print(res.charset)
print(res.status_code)
print(res._content_type_for_repr)
(3) 方法
HttpResponse() 使用页面内容实例化HttpResponse对象
write(content) 以文件的形式写入 向浏览器输出内容
flush() 以文件的形式输出缓冲区
set_cookie(key, value='', max_age=None,expires=None) 设置cookie
delete_cookie(key) 删除cookie
-
Request.COOKIES.get(key) 获取cookie 的值
注意: 如果删除一个不存在的key,就当什么都没发生
Cookie:会话控制 cookie和session 为了维护客户端的状态 因为HTTP协议是无状态的协议
Cookie常用参数
- key:键
- value:值
- max_age:多久后过期,时间为秒
- expires:过期时间,为具体时间
- path:生效路径
- domain:生效的域名
- secure:HTTPS传输时应设置为true
- httponly:值应用于http传输,JavaScript无法获取
def myCookie(request):
res = HttpResponse()
print(request.COOKIES)s
print(request.COOKIES.get('a','b')) #获取cookie键为a的值 如果cookie不存在则返回b
# res.set_cookie('mycookie','value',expires=2) #设置2秒的过期时间
res.delete_cookie(key)#删除cookie
return res
Cookie加解密
# salt:加密盐,内容可随便定义
HttpResponse().set_signed_cookie("key","value",salt="jkkll")
# 解密,加密盐的内容应与加密时的内容保持一致
request.get_signed_cookie("key",salt="jkkll")
注意:如果没有设置Cookie超时时间,表示关闭浏览器之后自动删除Cookie,Cookie尽量避免存储敏感信息。
六 子类HttpResponseRedirect
功能: 重定向,服务器端跳转
简写: redirect(to) to推荐使用反向解析
实例
# 重定向
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
def redirect1(request):
# return HttpResponseRedirect('/sunck/redirect2')
return redirect('/sunck/redirect2')
def redirect2(request):
return HttpResponse("我是重定向后的视图")
重定向参数问题
from django.core.urlresolvers import reverse
1)在booktest/urls.py中,修改fan2如下:
url(r’^fan(\d+)_(\d+)/$’, views.fan2,name=’fan2’),
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
def redirect1(request):
return redirect(reverse('myApp:fun2',args = (1,2)))
return HttpResponseRedirect(reverse('app:car',args=[1]))
def fun2(request,arg1,arg2):
print(arg1,arg2)
return HttpResponse("我是重定向后的视图")
注意:路径必须使用反向解析 参数必须为元组或者列表的序列类型
关键字参数
1)在booktest/urls.py中,修改fan2如下:
url(r'^fan(?P<id>\d+)_(?P<age>\d+)/$', views.fan2,name='fan2'),
给匹配的值起下标名的例子
import re
x = re.compile("(?P<id>\d+)_(?P<age>\d+)")
a = x.search('1_2')
print(a.group('id'))
print(a.group('age'))
2)修改templates/booktest/fan1.html文件如下:
<html>
<head>
<title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan100_18/">fan2</a>
<hr>
<!--关键字参数-->
反向解析:<a href="{%url 'booktest:fan2' id=100 age=18%}">fan2</a>
<!--普通参数-->
反向解析:<a href="{%url 'booktest:fan2' 100 18%}">fan2</a>
</body>
</html>
3)回到浏览器中,刷新,查看源文件如下图:
[图片上传失败...(image-758b3c-1530838081153)]
- 使用重定向传递关键字参数格式如下:
return redirect(reverse(‘booktest:fan2’, kwargs={‘id’:110,’age’:26}))
七 子类JsonResponse
返回json数据,一般用于异步请求
json = JsonResponse(data)
注意:Content-type类型为application/jsonc
from django.http import HttpResponseRedirect,JsonResponse
def json(request):
j = JsonResponse({'name':'zs','age':18})
print(type(j))
return HttpResponse(j)
八 session
概述: http是无状态的协议,每次请求都是一次新的请求
客户端与服务器端的一次通信就是一次会话
实现状态保持,在客户端或者服务端存储有关会话的数据
存储方式:
- cookie 所有的数据存储在客户端,不要存敏感的数据
- session 所有的数存储在服务端,在客户端用cookie存储session_id
状态保持的目的:在一段时间内跟踪请求者的状态,可以实现跨页面访问当前的请求者的数据
注意: 不同的请求者之间不会共享这个数据,与请求者一一对应的
(1) 启用session
settings文件中
INSTALLED_APPS 'django.contrib.sessions', 34行
MIDDLEWARE 'django.contrib.sessions.middleware.SessionMiddleware', 44行
(2) 使用session
启用session后,每个HttpRequest对象都有一个session属性,就是一个类似字典的对象
1.设置session
request.session['键'] = '值'
2. 根据键获取session值
get(key, default=None)
request.session.get["k1"],如果不存在则会报错,为了防止出错可以request.session.get('k1',none)
3. 清空所有的会话(但是不会删除Django的session表中的session数据和cookie)
clear()
request.session.clear()
4. 删除当前的会话并删除会话的cookie
flush()
request.session.flush()
del request.session['k1'] 删除
5.导入 logout 清除session
from django.contrib.auth import logout
logout(request)
注意:cookie依然存在 只是清除了cookie的值和session表中的数据
(3) 设置过期时间
如果不设置,14天后过期
-
整数:
request.session.set_expiry(10)
但是不会删除Django的session表中的session数据
时间对象
0 关闭浏览器时失效
None 依赖全局session失效策略
实例 登录退出
def index(request):
myDict =
myDict['mame'] = '首页'
print(request.session.get('username','游客'))
myDict['username'] = request.session.get('username','游客')
return render(request,'myApp/index.html',myDict)
#登录
def login(request):
print('走到这里了')
return render(request,'myApp/login.html')
#登录处理
def doLogin(request):
print(request.POST.get('uname'))
print(request.POST.get('password'))
request.session['username'] = request.POST.get('uname')
request.session.set_expiry(10) #设置过期时间
return redirect('/abc/')
#退出登录
from django.contrib.auth import logout as l
def logout(request):
# request.session.flush()
# request.session.clear()
l(request)
return HttpResponse('<meta http-equiv="refresh" content="3;/abc/">退出成功')
#模板
<form action="{% url 'myApp:dologin' %}" method="post">
<p>用户名: <input type="text" name="uname"></p>
<p>密码: <input type="password" name="password"></p>
<p><input type="submit" value="submit"></p>
</form>
#URL
url(r'^login/$',views.login,name='login'),
url(r'^dologin/$',views.doLogin,name='dologin'),
url(r'^logout/$',views.logout,name='logout'),
配置 settings.py
(4) 存储session的位置
-
数据库 默认存储在数据库中
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
-
缓存 (只存储在本地内存中,如果丢失不能找回,比数据库快)
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
-
数据库和缓存 (优先从本地缓存中读取,读取不到再去数据库中获取)
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
-
设置session的公共部分
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
(5) 使用redis缓存session
安装 django-redis-sessions
pip install django-redis-sessions
settings.py进行配置
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 0
SESSION_REDIS_PASSWORD = '123456'
SESSION_REDIS_PREFIX = 'session'
总报redis连接 密码输入错误
(6) session常用的操作
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']
# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
# 用户session的随机字符串
request.session.session_key
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key")
# 删除当前用户的所有Session数据
request.session.delete("session_key")
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
模板 templates
一 概述
模板由两部分组成
- HTML代码
- 逻辑控制代码
作用
快速生成HTML页面
优点
- 模板的设计实现了业务逻辑与现实内容的分离
- 视图可以使用任何模板l
模板处理
- 加载
- 渲染
二 定义模板
(1) 变量
视图传递给模板的数据
要遵守标识符规则
-
语法
{{ var }}
-
注意
如果使用的变量不存在,则插入的是空字符串
-
在模板中使用语法
- 字典查询
- 属性或者方法
- 数字索引
-
在模板中调用对象的方法
注意:不能传递参数
(2) 标签
语法: {% tag %}
作用:
- 在输出中创建文本
- 控制逻辑和循环
1、if/else 标签
==, !=, >=, <=, >, < 这些比较都可以在模板中使用
and, or, not, in, not in 也可以在模板中使用
基本语法格式如下:
{% if condition %}
... display
{% endif %}
或者:
{% if condition1 %}
... display 1
{% elif condition2 %}
... display 2
{% else %}
... display 3
{% endif %}
{% if a == 10 %}
<h1>10</h1>
{% elif a == 20 %}
<h2>20</h2>
{% endif %}
根据条件判断是否输出。if/else 支持嵌套。
{% if %} 标签接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not ),例如:
{% if athlete_list and coach_list %}
athletes 和 coaches 变量都是可用的。
{% endif %}d
2、for 标签
{% for %} 允许我们在一个序列上迭代。
与Python的 for 语句的情形类似,循环语法是 for X in Y ,Y是要迭代的序列而X是在每一个特定的循环中使用的变量名称。
每一次循环中,模板系统会渲染在 {% for %} 和 {% endfor %} 之间的所有内容。
例如,给定一个运动员列表 athlete_list 变量,我们可以使用下面的代码来显示这个列表:
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
给标签增加一个 reversed 使得该列表被反向迭代:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
可以嵌套使用 {% for %} 标签:
{% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
{% for sport in athlete.sports_played %}
<li>{{ sport }}</li>
{% endfor %}
</ul>
{% endfor %}
{% empty %}
{% for 变量 in 列表 %}
语句1
{% empty %}
语句2
{% endfor %}
注意: 列表为空或者列表不存在时执行语句2
{{ forloop.counter }} 表示当前是第几次循环
遍历字典
views.py
def home(request):
info_dict = {'site': u'自强学堂', 'content': u'各种IT技术教程'}
return render(request, 'home.html', {'info_dict': info_dict})
home.html
站点:{{ info_dict.site }} 内容:{{ info_dict.content }}
注意:在模板中取字典的键是用点info_dict.site,而不是Python中的 info_dict['site']
还可以这样遍历字典:
{% for key, value in info_dict.items %}
{{ key }}: {{ value }}
{% endfor %}
其实就是遍历这样一个 List: [('content', u'自强学堂'), ('site', u'各种IT技术教程')]
实例,在模板进行 条件判断和 for 循环的详细操作:
views.py
def home(request):
List = map(str, range(100))# 一个长度为100的 List
return render(request, 'home.html', {'List': List})
假如我们想用逗号将这些元素连接起来:
home.html
{% for item in List %}
{{ item }},
{% endfor %}
效果如下:
[图片上传失败...(image-21fe0b-1530838118122)]
我们会发现最后一个元素后面也有一个逗号,这样肯定不爽,如果判断是不是遍历到了最后一个元素了呢?
用变量 forloop.last 这个变量,如果是最后一项其为真,否则为假,更改如下:
{% for item in List %}
{{ item }}{% if not forloop.last%},{% endif %}
{% endfor %}
在for循环中还有很多有用的东西,如下:
变量 | 描述 |
---|---|
forloop.counter | 索引从 1 开始算 |
forloop.counter0 | 索引从 0 开始算 |
forloop.revcounter | 索引从最大长度到 1 |
forloop.revcounter0 | 索引从最大长度到 0 |
forloop.first | 当遍历的元素为第一项时为真 |
forloop.last | 当遍历的元素为最后一项时为真 |
forloop.parentloop | 用在嵌套的 for 循环中,获取上一层 for 循环的 forloop |
3、ifequal/ifnotequal 标签
{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。
下面的例子比较两个模板变量 user 和 currentuser :
{% ifequal user currentuser %}
<h1>Welcome!</h1>
{% endifequal %}
和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签:
{% ifequal section 'sitenews' %}
<h1>Site News</h1>
{% else %}
<h1>No News Here</h1>
{% endifequal %}
4、注释标签b
comment
作用:注释多行
{% comment %}
<h1>sunck is a cool man</h1>
<h1>sunck is a handsome man</h1>
{{stu.sname}}
{% endcomment %}
Django 注释使用 {# #}。
{# 这是一个注释 #}
注意:注释的代码都不会再浏览器的HTML页面中显示出来
5、include 标签
{% include %} 标签允许在模板中包含其它的模板的内容。
下面这个例子都包含了 nav.html 模板:
{% include "nav.html" %}
注意:如果是myApp下的模板需要写成
{% include "myApp/head.html" %}
6、url 反向解析
作用:反向解析
格式: {% url 'namespace:name' [ 参数1 参数2 ] %}
<a href="{% url 'myApp:logout' %}">退出</a>
反向解析中URL的参数
位置参数
1)在booktest/urls.py中,修改fan2如下:
url(r’^fan(\d+)_(\d+)/$’, views.fan2,name=’fan2’),
2)修改templates/booktest/fan1.html文件如下:
<html>
<head>
<title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan2_3/">fan2</a>
<hr>
反向解析:<a href="{%url 'booktest:fan2' 2 3%}">fan2</a>
</body>
</html>12345678910
关键字参数
1)在booktest/urls.py中,修改fan2如下:
url(r'^fan(?P<id>\d+)_(?P<age>\d+)/$', views.fan2,name='fan2'),
2)修改templates/booktest/fan1.html文件如下:
<html>
<head>
<title>反向解析</title>
</head>
<body>
普通链接:<a href="/fan100_18/">fan2</a>
<hr>
反向解析:<a href="{%url 'booktest:fan2' id=100 age=18%}">fan2</a>
</body>
</html>12345678910
- 使用重定向传递关键字参数格式如下:
return redirect(reverse(‘booktest:fan2’, kwargs={‘id’:110,’age’:26}))
7、跨站请求伪造 csrf
某些恶意网站包含链接、表单、按钮、js,利用登陆用户在浏览器中认证,从而攻击服务
作用:用于跨站请求伪造保护
防止CSRF
在settings.py文件中的MIDDLEWARE增加(默认就有)
'django.middleware.csrf.CsrfViewMiddleware',
格式:{% csrf_token %}
实例
<form action="{% url 'myApp:dologin' %}" method="post">
{% csrf_token %}
</form>
8、模板继承
block、extends
作用:用于模板的继承 可以减少页面的内容的重复定义,实现页面的重用
block标签
在父模板中预留区域,子模板去填充
语法:
{% block 标签名 %}
{%endblock 标签名%}
extends标签
语法:
{% extends '父模板路径' %}
接下来我们先创建之前项目的 templates 目录中添加 base.html 文件,代码如下:
HelloWorld/templates/base.html 文件代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}菜鸟教程(runoob.com){% endblock %}</title>
</head>
<body>
{% include 'nav.html' %}
{% block content %}
<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% endblock %}
{% include 'bottom.html' %}
</body>
</html>
以上代码中,名为 mainbody 的 block 标签是可以被继承者们替换掉的部分。
所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。
hello.html 中继承 base.html,并替换特定 block,hello.html 修改后的代码如下:
{% extends "base.html" %}
{% block title %}欢迎光临首页{% endblock %}
{% block content %}
{% include 'ad.html' %}
这里是首页,欢迎光临
{% endblock %}
9、HTML转义
return render(request,'myApp/index.html',{"code":"<h1>xlg is a very good man</h1>"})
{{code}}
将接收到的code当成普通字符串渲染
将接收到的字符串当成HTML代码渲染
-
safe
{{code|safe}}
-
autoescape
{% autoescape off %} {{code}} {% endautoescape %}
(3) 过滤器
语法:{{ var|过滤器 }}
作用:在变量被显示前修改它
模板过滤器可以在变量被显示前修改它,过滤器使用管道字符,如下所示:
{{ name|lower }}
{{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。
过滤管道可以被* 套接* ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入:
{{ my_list|first|upper }} #第一个显示并转化为大写
{{ my_list|last|upper }} #最后一个显示并转化为大写
以上实例将第一个元素并将其转化为大写。
有些过滤器有参数。 过滤器的参数跟随冒号之后并且总是以双引号包含。 例如:
{{ bio|truncatewords:"30" }}
{{ letter|truncatewords:'20'|upper }} #取出钱20个值并转化为大写
这个将显示变量 bio 的前30个词。
注意: 需要将变量bio的单词或者汉子 空格来划分 否则不起作用 如:bio = ab cd ef 而不是 abcdef
其他过滤器:
-
addslashes : 添加反斜杠到任何反斜杠、单引号或者双引号前面。
letter:'abc\def' {{ letter|addslashes }}
-
date : 按指定的格式字符串参数格式化 date 或者 datetime 对象,实例:
{{ pub_date|date:"F j, Y" }}
length : 返回变量的长度。
-
join
格式:值|join:‘#’
<h1>{{list|join:'#'}}</h1>
如果一个变量没有被提供,或者值为false、空,可以使用默认值
default
格式:{{ var|default:'good'}}
<h1>{{test|default:'没有'}}</h1>
django 模板中 加减乘除 求余
加法
{{value|add:10}}
note:value=5,则结果返回15
减法
{{value|add:-10}}
note:value=5,则结果返回-5,加一个负数就是减法了
乘法
{% widthratio 5 1 100%}
note:等同于:(5 / 1) * 100 ,结果返回500,withratio需要三个参数,它会使用参数1/参数2*参数3的方式进行运算,进行乘法运算,使「参数2」=1
除法
{% widthratio 5 100 1%}
note:等同于:(5 / 100) * 1,则结果返回0.05,和乘法一样,使「参数3」= 1就是除法了。
求余 求2的余数
{% if forloop.counter0|divisibleby:2 %}
三 验证码
作用:在用户注册、登陆页面的时候使用,为了防止暴力请求,减轻服务器的压力 防止csrf一种方式
验证码代码
安装pillow模块
pip install pillow
def verifycode(request):
#引入绘图模块
from PIL import Image, ImageDraw, ImageFont
#引入随机函数模块
import random
#定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(
20, 100), random.randrange(20, 100))
width = 100
height = 50
#创建画面对象
im = Image.new('RGB', (width, height), bgcolor)
#创建画笔对象
draw = ImageDraw.Draw(im)
#调用画笔的point()函数绘制噪点
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill=fill)
#定义验证码的备选值
str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
#随机选取4个值作为验证码
rand_str = ''
for i in range(0, 4):
rand_str += str[random.randrange(0, len(str))]
#构造字体对象
font = ImageFont.truetype(r'C:\Windows\Fonts\AdobeArabic-Bold.otf', 40)
#构造字体颜色
fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
#绘制4个字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
#释放画笔
del draw
#存入session,用于做进一步验证
request.session['verify'] = rand_str
#内存文件操作
import io
buf = io.BytesIO()
#将图片保存在内存中,文件类型为png
im.save(buf, 'png')
#将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')
模板中的代码
<form method="post" action="/verifycodecheck/">
{%csrf_token%}
<input type="text" name="verifycode"/>
<img src="/verifycode/" alt="" onclick="this.src='/verifycode/?id='+Math.random()"></p>
<input type="submit" value="登陆"/>
<span>{{flag}}</span>
</form>
在视图中去验证验证码
from django.shortcuts import render,redirect
def verifycodefile(request):
f = request.session.get("flag", True)
str = ""
if f == False:
str = "请重新输入"
request.session.clear()
return render(request,'myApp/verifycodefile.html',{"flag":str})
def verifycodecheck(request):
code1 = request.POST.get("verifycode").upper()
code2 = request.session["verify"].upper()
if code1 == code2:
return render(request,'myApp/success.html')
else:
request.session["flag"] = False
return redirect('/verifycodefile/')
URL配置
url(r'^verifycode/$', views.verifycode),
url(r'^verifycodefile/$', views.verifycodefile),
url(r'^verifycodecheck/$', views.verifycodecheck),
Django高级
一、静态文件
管理静态文件(例如图像,JavaScript,CSS,字体,图片)
网站通常需要提供其他文件,如图片,JavaScript或CSS。在Django中,我们将这些文件称为“静态文件”。Django提供 django.contrib.staticfiles
来帮助你管理它们。
本页介绍如何提供这些静态文件。
配置静态文件
确保
django.contrib.staticfiles
包含在你的INSTALLED_APPS
。-
在您的设置文件中,定义
STATIC_URL
,例如:STATIC_URL = '/static/'
-
在您的模板中,或者使用硬编码url的方式
/static/my_app/example.jpg
,最好使用static
模板标签通过使用配置的STATICFILES_STORAGE
存储来构建给定相对路径的URL (当您想要切换到内容交付网络(CDN)时,用于提供静态文件)。{% load static %} <img src="{% static "my_app/example.jpg" %}" alt="My image"/>
-
将您的静态文件存储
static
在应用程序中调用的文件夹中。例如my_app/static/my_app/example.jpg
。
您的项目可能还会有不与特定应用绑定的静态资产。除了static/
在应用程序中使用目录之外,您还可以[STATICFILES_DIRS
]在设置文件中定义一个目录列表(),其中Django也将查找静态文件。例如:
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
'/var/www/static/',
]
配置settings.py
STATIC_URL = '/static/'#比如图片
#普通文件会查找下面的路径 比如导入js,css
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]
实例
{% load static from staticfiles %}
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" type="text/css" href="{% static 'myApp/css/style.css' %}"/>
<script type="text/javascript" src="/static/myApp/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="/static/myApp/js/sunck.js"></script>
</head>
<body>
<h1>xlg is a good man</h1>
<img src="/static/myApp/img/2.png"/>
<img src="{% static 'myApp/img/2.png' %}"/>
</body>
</html>
二、中间件
概述: 一个轻量级、底层的插件,可以介入Django的请求和响应
本质:一个Python类
方法
__init__不需要传参数,服务器响应第一个请求的时候自动调用,用于确定是否启用该中间件
process_request(self,request) 在执行视图之前被调用(分配url匹配视图之前),每个请求上都会调用,返回None或者HttpResponse对象
process_view(self,request,view_func,view_args,view_kwargs) 调用视图之前执行,每个请求都会调用,返回None或者HttpResponse对象see
-
process_template_response(self,request,response) 在视图刚好执行完后调用,每个请求都会调用,返回None或者HttpResponse对象
使用render
process_response(self,request,response) 所有响应返回浏览器之前调用,每个请求都会调用,返回HttpResponse对象
process_exception(self,request,exception) 当视图抛出异常时调用,返回HttpResponse对象
自定义中间件
UM和myApp同级->创建工程目录middleware-->目录下创建myApp目录
-
|-myApp |-middleware |--myApp |----myMiddle.py |-project
在middleware下的myApp里 创建一个python文件 myMiddle.py
from django.utils.deprecation import MiddlewareMixin
class MyMiddle(MiddlewareMixin):
def process_request(self, request):
print("get参数为:", request.GET.get("a"))
-
使用自定义中间件
配置settings.py文件
MIDDLEWARE中添加
'middleware.myApp.myMiddle.MyMiddle'
三、上传图片
概述:文件上传时,文件数据存储在request.FILES属性中
注意:
- form表单要上传文件需要加enctype="multipart/form-data"
- 上传文件必须是post请求
存储路径
在static目录下创建upfile目录用于存储接收上传的文件
-
配置settings.py文件
MDEIA_ROOT=os.path.join(BASE_DIR,r'static/upfile')
代码示例
模板
<body>
<form method="post" action="/savefile/" enctype="multipart/form-data">
{%csrf_token%}
<input type="file" name="file"/>
<input type="submit" value="上传"/>
</form>
视图
def upfile(request):
return render(request, 'myApp/upfile.html')
import os
from django.conf import settings
def savefile(request):
if request.method == "POST":
f = request.FILES["file"]
# 文件在服务器端的路径
#os.path.splitext(path)获取文件扩展名
filePath = os.path.join(settings.MDEIA_ROOT, f.name)
with open(filePath, 'wb') as fp:
for info in f.chunks():# 分块写入文件
fp.write(info)
return HttpResponse("上传成功")
else:
return HttpResponse("上传失败")
在进行进一步的代码解释之前,需要先讲几个关于上传文件的方法和属性:
myFile.read():从文件中读取整个上传的数据,这个方法只适合小文件;
myFile.chunks():按块返回文件,通过在for循环中进行迭代,可以将大文件按块写入到服务器中;
myFile.multiple_chunks():这个方法根据myFile的大小,返回True或者False,当myFile文件大于2.5M(默认为2.5M,可以调整)时,该方法返回True,否则返回False,因此可以根据该方法来选择选用read方法读取还是采用chunks方法:
if myFile.multiple_chunks() == False:
# 使用myFile.read()
else:
# 使用myFile.chunks()
myFile.name:这是一个属性,不是方法,该属性得到上传的文件名,包括后缀,如123.exe;
myFile.size:这也是一个属性,该属性得到上传文件的大小。
四、分页
(1) Paginator对象
-
创建对象
格式:Paginator(列表, 整数)
返回值:返回的分页对象
-
属性
count 对象总数
num_pages 页面总数
page_range [1,2,3,4,5] 页码从1开始
-
方法
page(num) 获得一个Page对象,如果提供的页码不存在会抛出"InvalidPage"异常
-
异常
- InvalidPage 当向page()传递的是一个无效的页码时抛出
- PageNotAnInteger 当向page()传递的不是一个整数时抛出
- EmptyPage 当向page()传递一个有效值,但是该页面时没有数据时抛出
(2) Page对象c
-
创建对象
Paginator对象的page()方法返回得到Page对象
不需要手动创建
-
属性
- object_list 当前页上所有的数据(对象)列表
- number 当前页的页码值
- paginator 当前page对象关联的paginator对象
-
方法
- has_next() 判断是否有下一页,如果有返回True
- has_previous() 判断是否有上一页,如果有返回True
- has_other_pages() 判断是否有上一页或下一页,如果有返回True
- next_page_number() 返回下一页的页码,如果下一页不存在抛出InvalidPage异常
- previous_page_number() 返回上一页的页码,如果上一页不存在抛出InvalidPage异常
- len() 返回当前页的数据(对象)个数
代码示例
路由配置
url(r'^stupage/(\d+)/$',views.stuPage,name='page'),
视图
allStu = Students.stuObj.all()
pag = Paginator(allStu,6)
# if int(nowPage)>=int(pag.num_pages):
# nowPage = pag.num_pages
try:
page = pag.page(nowPage)
except:
page = pag.page(pag.num_pages)
# print(page)
return render(request,'myApp/studentPage.html',{'students':page})
模板
<body>
<ul>
{% for stu in students %}
<li>
{{stu.sname}}-{{stu.sgrade}}
</li>
{% endfor %}
</ul>
<ul>
{% for index in students.paginator.page_range %}
{% if index == students.number %}
<li>
{{index}}
</li>
{% else %}
<li>
<a href="{% url 'myApp:page' index %}">{{index}}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</body>
五、Ajax
需要动态生成,请求JSON数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="/static/myApp/js/jquery-3.1.1.min.js"></script>
</head>
<body>
<h1>学生信息列表</h1>
<button id="btn">显示学生信息</button>
<script type="text/javascript" src="/static/myApp/js/sunck.js"></script>
</body>
</html>
Jquery代码
<script src="{% static 'js/jquery-1.8.3.min.js' %}"></script>
<script>
$(function () {
$('button').click(function () {
{% comment %}
$.ajax({
type:'get',
url:'{% url "myApp:stuinfo" %}',
dataType:'json',
success:function(status,data){
console.log(status)
}
})
{% endcomment %}
$.get('{% url "myApp:stuinfo" %}',function(data,status){
str = ''
if(status == 'success'){
for(var i in data.data){
{# console.log(data.data[i][0])#}
str += '<li>'+data.data[i][0]+' '+data.data[i][1]+'</li>';
}
$('ul').append(str)
}
})
})
})
</script>
</head>
<body>
<ul>
</ul>
<button>点击</button>
</body>
视图
def ajaxstudents(request):
return render(request, 'myApp/ajaxstudents.html')
from django.http import JsonResponse
def studentsinfo(request):
s = request.is_ajax() #判断是否是ajax请求
if not s:
return HttpResponse('不是ajax请求')
stus = Students.objects.all()
list = []
for stu in stus:
list.append([stu.sname, stu.sage])
return JsonResponse({"data":list})
#使用json.dumps写法 传到前台的json需要解析
import json
return HttpResponse(json.dumps({'data':l}))
#ajax获取的数据改成
data = JSON.parse(data).data#如果使用jsondump需要去解析
for(var i in a){
console.log(a[i][0])
}
六、富文本
pip install django-tinymce
在站点中使用
(1) 配置settings.py文件
INSTALLED_APPS添加 'tinymce',
Settings.py添加
TINYMCE_DEFAULT_CONFIG = {
'theme':'advanced',
'width':600,
'height':400,
}
(2) 创建一个模型类
from tinymce.models import HTMLField
class Text(models.Model):
str = HTMLField()
进行文件迁移
python manage.py makemigrations
python manage.py migrateas
(3) 配置站点
admin.py文件
from .models import Text
admin.site.register(Text)
python manage.py createsuperuser #创建站点用户
在自定义视图中使用
{% load static from staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>富文本</title>
<script type="text/javascript" src="/static/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
'mode':'textareas',
'theme':'advanced',
'width':800,
'height':600,
})
</script>
</head>
<body>
<form action="/saveedit/" method="post">
<textarea name="str">sunck is a good man</textarea>
<input type="submit" value="提交"/>
</form>
</body>
</html>
七、celerys
http://docs.jinkan.org/docs/celery/
问题:
用户发起request,并且要等待response返回。但是在视图中有一些耗时的操作,导致用户可能会等待很长时间才能接受response,这样用户体验很差
网站每隔一段时间要同步一次数据,但是http请求是需要触发的
(1) celery
-
任务task
本质是一个python函数,将耗时操作封装成一个函数
-
队列queue
将要执行的任务放队列里
-
工人worker
负责执行队列中的任务
-
代理broker
负责调度,在部署环境中使用redis
(2) 解决
celery来解决
- 将耗时的操作放到celery中执行
- 使用celery定时执行
(3) 安装
- pip install celery
- pip install celery-with-redis
- pip install django-celery
(4) 配置settings.py
INSTALLED_APPS 添加
djcelery
在settings下方添加如下代码
import djcelery
djcelery.setup_loader()#初始化
BROKER_URL='redis://:密码@127.0.0.1:6379/0'#0带表16个库中使用第0个库
CELERY_IMPORTS=('myApp.task') #myApp是项目名
(5) 在应用目录下创建task.py文件 也就是myApp下
from celery import task
import time
@task
def tt():
print("xlg is a good man")
time.sleep(5)
print("xlg is a nice man")
(6) 迁移,生成celery需要的数据库表
python manage.py migrate
(7) 在工程目录下的project目录下创建celery.py的文件
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'whthas_home.settings')
app = Celery('portal')
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
(8) 在工程目录下的project目录下的init.py文件中添加
from .celery import app as celery_app
(9) 视图
from .task import tt
def celery(request):
tt.delay()#添加到celery中执行,不会阻塞
return render(request, 'myApp/celery.html')
(10) 启动redis
C:\redis64-2.8.2101>redis-server.exe redis.windows.conf
C:\redis64-2.8.2101>Redis-cli.exe
c127.0.0.1:6379>auth '123456'
(11) 启动服务
python manange.py runserver 0.0.0:8000
(12) 启动worker
python manage.py celery worker --loglevel=info