django框架 Django介绍 为什么需要学习web框架 web应用请求处理流程
1 2 3 4 5 1.用于搭建Web应用程序(类似之前玩的mini-web,自己处理,而框架则帮我们做好了) * 接收并解析HTTP请求,获取具体的请求信息 * 处理本次HTTP请求,即完成本次请求的业务逻辑处理 * 构造并返回处理结果——HTTP响应 2.免去不同Web应用相同代码部分的重复编写,只需关心Web应用核心的业务逻辑实现
Django是什么 1 2 Django,发音为[`dʒæŋɡəʊ],是用python语言写的开源web开发框架,并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站,而开发出来了这个框架,于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt,他是一个吉普赛人,主要以演奏吉它为主,还演奏过小提琴等。由于Django在近年来的迅速发展,应用越来越广泛,被著名IT开发杂志SDTimes评选为2013SDTimes100,位列"API、库和框架"分类第6位,被认为是该领域的佼佼者。 Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用,多个组件可以很方便的以"插件"形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNotRepeatYourself)原则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # 创建Django项目和应用 * django-admin startproject name * python manager.py startapp name # 重量级框架 对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速。 * 提供项目工程管理的自动化脚本工具 * 数据库ORM支持(对象关系映射,英语:Object Relational Mapping) * 模板 * 表单 * Admin管理站点 * 文件管理 * 认证权限 * session机制 * 缓存
MVC模式 1 2 3 4 5 有一种程序设计模式叫MVC,其核心思想是分工、解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容。 MVC的全拼为Model-View-Controller,最早由TrygveReenskaug在1978年提出,是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件设计模式,是为了将传统的输入(input)、处理(processing)、输出(output)任务运用到图形化用户交互模型中而设计的。随着标准输入输出设备的出现,开发人员只需要将精力集中在业务逻辑的分析与实现上。后来被推荐为Oracle旗下Sun公司Java EE平台的设计模式,并且受到越来越多的使用ColdFusion和PHP的开发者的欢迎。现在虽然不再使用原来的分工方式,但是这种分工的思想被沿用下来,广泛应用于软件工程中,是一种典型并且应用广泛的软件架构模式。后来,MVC的思想被应用在了Web开发方面,被称为Web MVC框架。 * M全拼为Model,主要封装对数据库层的访问,对数据库中的数据进行增、删、改、查操作。 * V全拼为View,用于封装结果,生成页面展示的html内容。 * C全拼为Controller,用于接收请求,处理业务逻辑,与Model和View交互,返回结果。
MVT模式
1 2 3 4 * M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。 * V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。 * T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。 注:差异就在于黑线黑箭头标识出来的部分
Django肯定是遵循MVC思想的,但是在具体实现上,Django是MVT框架模式;而且可以说MVT与Django是紧密不可分的,也正是Django对MVT架构模式的使用,导致了我们的困惑。
附件:
Django环境搭建 为什么需要虚拟环境? 1 2 3 4 5 6 7 8 在开发过程中, 当需要使用python的某些工具包/框架时需要联网安装。比如联网安装Django框架django的1.11.11版本 sudo pip install django==1.11.11 使用如上命令, 会将Django安装到/usr/local/lib/python2.7/dist-packages路径下 如果在一台电脑上, 想开发多个不同的项目, 需要用到同一个包的不同版本, 如果使用上面的命令, 在同一个目录下安装或者更新, 新版本会覆盖以前的版本, 其它的项目就无法运行了. 解决方案:虚拟环境(解决版本迭代,多个框架混乱) * 作用:虚拟环境可以搭建独立的python运行环境, 使得单个项目的运行环境与其它项目互不影响. * 所有的虚拟环境都位于/home/下的隐藏目录.virtualenvs下
就好比一间一间独立的教室,环境下做自己的事情,互不影响。
怎么搭建虚拟环境? 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 a.安装虚拟环境的命令 : sudo pip install virtualenv sudo pip install virtualenvwrapper 找不到pip: sudo apt install python3-pip b.安装完虚拟环境后,如果提示找不到mkvirtualenv命令,须配置环境变量: # 1、创建目录用来存放虚拟环境 mkdir $HOME/.virtualenvs # 2、打开~/.bashrc文件,并添加如下: export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 export WORKON_HOME=$HOME/.virtualenvs source /usr/local/bin/virtualenvwrapper.sh # 3、运行 source ~/.bashrc c.创建虚拟环境的命令 :(如果不指定python版本,默认安装的是python2的虚拟环境) - 在python2中,创建虚拟环境。mkvirtualenv 虚拟环境名称 例 :mkvirtualenv py_django - 在python3中,创建虚拟环境。mkvirtualenv -p python3 虚拟环境名称 例 :mkvirtualenv -p python3 py3_django 提示 : * 创建虚拟环境需要联网 * 创建成功后, 会自动工作在这个虚拟环境上 * 工作在虚拟环境上, 提示符最前面会出现 “虚拟环境名称”
1 2 3 pip install virtualenv pip install virtualenvwrapper 系统环境变量设置 WORKON_HOME d:\.pyenv
1 2 创建虚拟环境,进入虚拟环境目录下的scripts启动和停止。 pip install virtualenvwrapper-win
怎么使用虚拟环境?
1 2 3 * 使用虚拟环境的命令 : workon 虚拟环境名称 例 :使用py3_django的虚拟环境 workon py3_django
1 2 * 退出虚拟环境的命令 : deactivate
1 2 3 4 * 删除虚拟环境的命令 : rmvirtualenv 虚拟环境名称 例 :删除虚拟环境py3_django 先退出:deactivate 再删除:rmvirtualenv py3_django
怎么在虚拟环境中安装工具包?
1 2 3 4 5 6 7 8 9 * 提示 : 工具包安装的位置 : * python2版本下: * ~/.virtualenvs/py_flask/lib/python2.7/site-packages/ * python3版本下: * ~/.virtualenvs/py3_flask/lib/python3.5/site-packages * python3版本下安装django-1.11.11的包 : pip install 包名称 例 : 安装django-1.11.11的包 pip install django==1.11.11
1 2 3 4 和在外面安装没什么区别,只是在虚拟环境里而已。 * 查看虚拟环境中安装的包 : pip list
查询虚拟路径:which python
/d/.pyenv/django_1/Scripts/python
Django 1.11.11 创建项目 创建Django项目 1 2 3 4 5 6 7 8 9 10 在使用Flask框架时,项目工程目录的组织与创建是需要我们自己手动创建完成的。 在django中,项目工程目录可以借助django提供的命令帮助我们创建。 django-admin startproject name bookmanager/ ├── bookmanager │ ├── __init__.py │ ├── settings.py : 项目的整体配置文件。 │ ├── urls.py : 项目的URL配置文件 │ └── wsgi.py : 项目与WSGI兼容的Web服务器入口。 └── manage.py : 项目管理文件,通过它管理项目。
1 2 3 4 5 6 7 在开发阶段,为了能够快速预览到开发的效果,django提供了一个纯python编写的轻量级web服务器,仅在开发阶段使用。 运行服务器命令如下: python manage.py runserver ip:端口 或:(可以不写IP和端口,默认IP是127.0.0.1,默认端口为8000。) python manage.py runserver ps:django默认工作在调式Debug模式下,如果增加、修改、删除文件,服务器会自动重启,按ctrl+c停止服务器。
1 2 3 4 启动项目的时候报错:SyntaxError: Generator expression must be parenthesized (widgets.py, line 152) 由于django 1.11版本和python3.7版本不兼容, 2.0版本以后的Django修复了这个问题 1.要么升级/使用Django高的版本(>=2) 2.要么将home/nbchen/.virtualenvs/py3_django_1.11.11/lib/python3.8/site-packages/django/contrib/admin/路径下的widgets.py 152行的逗号移除 (亲测可以)
创建子应用 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 在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该模块代码整体复制过去,达到复用。 在Flask框架中也有类似子功能应用模块的概念,即蓝图Blueprint。 Django的视图编写是放在子应用中的。 python manager.py startapp name . ├── book │ ├── admin.py : 跟网站的后台管理站点配置相关 │ ├── apps.py : 用于配置当前子应用的相关信息。 │ ├── __init__.py │ ├── migrations : 用于存放数据库迁移历史文件。 │ │ └── __init__.py │ ├── models.py: 用于保存数据库模型类。 │ ├── tests.py :用于开发测试用例,编写单元测试。 │ └── views.py : 用于编写Web应用视图。 ├── bookmanager │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── settings.cpython-38.pyc │ │ ├── urls.cpython-38.pyc │ │ └── wsgi.cpython-38.pyc │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── db.sqlite3 └── manage.py
配置选择之前搭建的虚拟环境py3_djando_1.11.11
注册子应用 1 2 3 4 创建出来的子应用目录文件虽然被放到了工程项目目录中,但是django工程并不能立即直接使用该子应用,需要注册安装后才能使用。 在工程配置文件settings.py中,INSTALLED_APPS项保存了工程中已经注册安装的子应用,初始工程中的INSTALLED_APPS如下: 注册安装一个子应用的方法,即是将子应用的配置信息文件apps.py中的Config类添加到INSTALLED_APPS列表中。 例如,将刚创建的book子应用添加到工程中,可在INSTALLED_APPS列表中添加'book.apps.BookConfig'。 其实就是找到 子应用 apps.py 中的 config配置类/配置类中name的值
一些基础设置 settings.py 1 # 管理者后台 localhost:8000/admin
设置中文和时区
创建管理员 1 2 3 4 5 6 7 python manage.py createsuperuser admin admin@nbchen.com asd741520 # 重置密码 python manager.py changepassword 用户名
Debug模式 1 2 调试模式,创建工程后初始值为True,即默认工作在调试模式下。 注意:部署线上运行的Django不要运行在调式模式下,记得修改DEBUG=False和ALLOW_HOSTS
* 静态文件
1 2 3 4 5 6 7 8 9 10 11 项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。 * STATICFILES_DIRS存放查找静态文件的目录 * STATIC_URL访问静态文件的URL前缀 例子: STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] 此时在static添加的任何静态文件都可以使用网址/static/文件在static中的路径来访问了。 我们向static目录中添加一个index.html文件,在浏览器中就可以使用127.0.0.1:8000/static/index.html来访问。 或者我们在static目录中添加了一个子目录和文件book/detail.html,在浏览器中就可以使用127.0.0.1:8000/static/book/detail.html来访问。
app.py设置 1 2 3 4 5 6 7 8 在创建应用时,Django会向apps.py文件中写入一个该应用的配置类。 我们将此类添加到工程settings.py中的INSTALLED_APPS列表中,表明注册安装具备此配置属性的应用。 from django.apps import AppConfig class UsersConfig(AppConfig): name = 'book' verbose_name = '图书管理' * AppConfig.name属性表示这个配置类是加载到哪个应用的,每个配置类必须包含此属性,默认自动生成。 * AppConfig.verbose_name属性用于设置该应用的直观可读的名字,此名字在Django提供的Admin管理站点中会显示,如
Django中的模型 Django和数据库的关系
1 2 3 4 5 6 * MVT设计模式中的Model, 专门负责和数据库交互.对应(models.py) * 由于Model中内嵌了ORM框架, 所以不需要直接面向数据库编程. * 而是定义模型类, 通过模型类和对象完成数据库表的增删改查. * ORM框架就是把数据库表的行与相应的对象建立关联, 互相转换.使得数据库的操作面向对象. 表 -> 类 字段 -> 属性
模型迁移 1 2 3 4 5 在我理解就是写好实体类,然后通过命令生成表。 - 生成迁移文件。python manage.py makemigrations (根据模型类生成创建表的语句) 在migrations/下生成00001——init.py 里面是相关的实体模型 - 执行迁移。 python manage.py migrate (根据第一步生成的语句在数据库中创建表) ps:默认采用sqlite3数据库来存储数据,在工程项目下,settings.py中的DATABASES下配置
使用MySQL数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1.安装驱动 pip install PyMySQL 2.在Django的工程同名子目录的__init__.py文件中添加如下语句(作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。) import pymysql pymysql.install_as_MySQLdb() 3.修改DATABASES配置信息 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': 'mysql', # 数据库用户密码 'NAME': 'book' # 数据库名字 } } 4.在MySQL中创建数据库 create database book charset=utf8;
注册模型 1 2 3 模型注册后可以在浏览器后台界面可视化操作数据库表 编辑:admin.py admin.site.register(Book)
1 这样注册后,就可以通过python启动的admin后台,在页面添加实体数据。会往默认的数据库sqllite3里面的bookinfo表增加记录。
自定义模型的后台展示信息
定义模型 卸载哪里 1 2 * 模型类被定义在"应用/models.py"文件中。 * 模型类必须继承自Model类,位于包django.db.models中。
命名规范 1 2 不要使用 python,mysql关键字; 不要使用 连续的下划线(__) => orm查询的时候,有连续下划线的用法
例子 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 32 33 34 35 36 37 38 39 40 41 42 from django.db import modelsclass BookInfo (models.Model): name = models.CharField(max_length=20 , verbose_name='名称' ) pub_date = models.DateField(verbose_name='发布日期' ,null=True ) readcount = models.IntegerField(default=0 , verbose_name='阅读量' ) commentcount = models.IntegerField(default=0 , verbose_name='评论量' ) is_delete = models.BooleanField(default=False , verbose_name='逻辑删除' ) class Meta : db_table = 'bookinfo' verbose_name = '图书' def __str__ (self ): """定义每个数据对象的显示信息""" return self .name class PeopleInfo (models.Model): GENDER_CHOICES = ( (0 , 'male' ), (1 , 'female' ) ) name = models.CharField(max_length=20 , verbose_name='名称' ) gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0 , verbose_name='性别' ) description = models.CharField(max_length=200 , null=True , verbose_name='描述信息' ) book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书' ) is_delete = models.BooleanField(default=False , verbose_name='逻辑删除' ) class Meta : db_table = 'peopleinfo' verbose_name = '人物信息' def __str__ (self ): return self .name
字段类型
选项
外键 1 2 3 4 5 6 7 在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量: * CASCADE级联,删除主表数据时连通一起删除外键表中数据 * PROTECT保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据 * SET_NULL设置为NULL,仅在该字段null=True允许为null时可用 * SET_DEFAULT设置为默认值,仅在该字段设置了默认值时可用 * SET()设置为特定值或者调用特定方法 * DO_NOTHING不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
shell工具 1 2 3 4 5 6 7 8 为了更方便快捷的观察Django操作数据库增删改查的结果,可以使用shell命令 python manage.py shell >> from book.models import BookInfo,PeopleInfo book = BookInfo( name='python入门', pub_date='2010-1-1' )
数据库操作 新增
1.通过创建模型类对象,执行对象的save()方法保存到数据库中。
2.通过模型类.objects.create()保存。
修改/更新
1.修改模型类对象的属性,然后执行save()方法
1 2 3 4 5 >>> person = PeopleInfo.objects.get(name='itheima') >>> person.name = 'itcast' >>> person.save() >>> person <PeopleInfo: itcast>
2.使用模型类.objects.filter().update(),会返回受影响的行数
1 2 >>> PeopleInfo.objects.filter(name='itcast').update(name='传智播客') 1
删除
1 2 3 >>> person = PeopleInfo.objects.get(name='传智播客') >>> person.delete() (1, {'book.PeopleInfo': 1})
2.模型类.objects.filter().delete()
1 2 >>> BookInfo.objects.filter(name='python入门').delete() (1, {'book.BookInfo': 1, 'book.PeopleInfo': 0})
查询 1.基础查询
get查询单一结果,如果不存在会抛出模型类.DoesNotExist异常。
all查询多个结果。
count查询结果数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 >>> BookInfo.objects.get(id =1 )<BookInfo: 射雕英雄传> >>> BookInfo.objects.get(pk=2 )<BookInfo: 天龙八部> >>> BookInfo.objects.get(pk=20 )Traceback (most recent call last): File "<console>" , line 1 , in <module> File "/home/python/.virtualenvs/py3_django_1.11/lib/python3.5/site-packages/django/db/models/manager.py" , line 85 , in manager_method return getattr (self .get_queryset(), name)(*args, **kwargs) File "/home/python/.virtualenvs/py3_django_1.11/lib/python3.5/site-packages/django/db/models/query.py" , line 380 , in get self .model._meta.object_name book.models.DoesNotExist: BookInfo matching query does not exist. >>> BookInfo.objects.all ()<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>]> >>> BookInfo.objects.count()4
2.过滤查询
filter 过滤出多个结果(返回列表)
exclude 排除掉符合条件剩下的结果
get 过滤单一结果
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 32 33 34 35 36 37 38 39 过滤条件的表达语法: 属性名称__比较运算符=值 (属性名称和比较运算符间使用两个下划线,所以属性名不能包括多个下划线) 1)相等 exact:表示判等。 查询编号为1的图书 2)模糊查询 contains:是否包含。说明:如果要包含%无需转义,直接写即可。 startswith、endswith:以指定值开头或结尾。 以上运算符都区分大小写, 在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith. 查询书名包含'湖'的图书 查询书名以'部'结尾的图书 3) 空查询 isnull:是否为null。 查询书名为空的图书 4) 范围查询 in:是否包含在范围内。 查询编号为1或3或5的图书 5)比较查询 * gt大于 (greater then) * gte大于等于 (greater then equal) * lt小于 (less then) * lte小于等于 (less then equal) 不等于的运算符,使用exclude()过滤器 查询编号大于3的图书 6)日期查询 year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。 查询1980年发表的图书 查询1990年1月1日后发表的图书
F对象 1 2 3 4 5 6 7 8 9 10 11 之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。 F(属性名) 查询阅读量大于等于评论量的图书。 >>> from django.db.models import F >>> BookInfo.objects.filter(readcount__gt=F('commentcount')) <QuerySet [<BookInfo: 雪山飞狐>]> 可以在F对象上使用算数运算。 例:查询阅读量大于2倍评论量的图书。 >>> BookInfo.objects.filter(readcount__gt=F('commentcount')*2) <QuerySet [<BookInfo: 雪山飞狐>]>
Q对象 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 多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。 例:查询阅读量大于20,并且编号小于3的图书。 >>> BookInfo.objects.filter(readcount__gt=20,id__lt=3) <QuerySet [<BookInfo: 天龙八部>]> 或者 >>> BookInfo.objects.filter(readcount__gt=20).filter(id__lt=3) <QuerySet [<BookInfo: 天龙八部>]> 如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符,Q对象被义在django.db.models中。 语法如下:Q(属性名__运算符=值) 例:查询阅读量大于20的图书,改写为Q对象如下。 BookInfo.objects.filter(Q(readcount__gt=20)) Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或。 例:查询阅读量大于20,或编号小于3的图书,只能使用Q对象实现 >>> BookInfo.objects.filter(Q(readcount__gt=20)|Q(id__lt=3)) <QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]> Q对象前可以使用~操作符,表示非not。 例:查询编号不等于3的图书。 >>> BookInfo.objects.filter(~Q(id=3)) <QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]>
聚合函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg平均,Count数量,Max最大,Min最小,Sum求和,被定义在django.db.models中。 例:查询图书的总阅读量。 >>> from django.db.models import Sum >>> BookInfo.objects.aggregate(Sum('readcount')) {'readcount__sum': 126} 注意aggregate的返回值是一个字典类型,格式如下: {'属性名__聚合类小写':值} 如:{'readcount__sum': 126} 使用count时一般不使用aggregate()过滤器。 例:查询图书总数。 BookInfo.objects.count() 注意count函数的返回值是一个数字。
排序函数 1 2 3 4 5 6 7 8 # 使用order_by对结果进行排序 # 默认升序 >>> BookInfo.objects.all().order_by('readcount') <QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 笑傲江湖>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]> # 降序 >>> BookInfo.objects.all().order_by('-readcount') <QuerySet [<BookInfo: 雪山飞狐>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 射雕英雄传>]>
关联查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 查询书籍为1的所有人物信息 查询人物为1的书籍信息 由一到多的访问语法:一对应的模型类对象.多对应的模型类名小写_set 例: >>> book = BookInfo.objects.get(id=1) >>> book.peopleinfo_set.all() <QuerySet [<PeopleInfo: 郭靖>, <PeopleInfo: 黄蓉>, <PeopleInfo: 黄药师>, <PeopleInfo: 欧阳锋>, <PeopleInfo: 梅超风>]> 由多到一的访问语法:多对应的模型类对象.多对应的模型类中的关系类属性名 例: person = PeopleInfo.objects.get(id=1) person.book <BookInfo: 射雕英雄传> 访问一对应的模型类关联对象的id语法: 多对应的模型类对象.关联类属性_id 例: >>> person = PeopleInfo.objects.get(id=1) >>> person.book_id 1
关联过滤查询 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 由多模型类条件查询一模型类数据: 语法如下:关联模型类名小写__属性名__条件运算符=值 注意:如果没有"__运算符"部分,表示等于。 例: 查询图书,要求图书人物为"郭靖" >>> book = BookInfo.objects.filter(peopleinfo__name='郭靖') >>> book <QuerySet [<BookInfo: 射雕英雄传>]> 查询图书,要求图书中人物的描述包含"八" >>> book = BookInfo.objects.filter(peopleinfo__description__contains='八') >>> book <QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>]> 由一模型类条件查询多模型类数据: 语法如下:一模型类关联属性名__一模型类属性名__条件运算符=值 注意:如果没有"__运算符"部分,表示等于。 例: 查询书名为“天龙八部”的所有人物。 >>> people = PeopleInfo.objects.filter(book__name='天龙八部') >>> people <QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>]> 查询图书阅读量大于30的所有人物 >>> people = PeopleInfo.objects.filter(book__readcount__gt=30) >>> people <QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>, <PeopleInfo: 胡斐>, <PeopleInfo: 苗若兰>, <PeopleInfo: 程灵素>, <PeopleInfo: 袁紫衣>]>
查询集QuerySet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Django的ORM中存在查询集的概念。 查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。 当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表): * all():返回所有数据。 * filter():返回满足条件的数据。 * exclude():返回满足条件之外的数据。 * order_by():对结果进行排序。 对查询集可以再次调用过滤器进行过滤,如 >>> books = BookInfo.objects.filter(readcount__gt=30).order_by('pub_date') >>> books <QuerySet [<BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]> 也就意味着查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果。 从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句。 判断某一个查询集中是否有数据:exists():判断查询集中是否有数据,如果有则返回True,没有则返回False
1 2 3 4 5 6 7 创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用 例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集books books = BookInfo.objects.all() 继续执行遍历迭代操作后,才真正的进行了数据库的查询 for book in books: print(book.name)
1 2 3 4 使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。 情况一:如下是两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载。 from book.models import BookInfo
1 情况二:经过存储后,可以重用查询集,第二次使用缓存中的数据。
1 2 3 4 5 6 7 8 可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句。 注意:不支持负数索引。 对查询集进行切片后返回一个新的查询集,不会立即执行查询。 如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()如果没有数据引发DoesNotExist异常。 示例:获取第1、2项,运行查看。 >>> books = BookInfo.objects.all()[0:2] >>> books <QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>]>
1 2 3 4 5 6 7 8 9 10 #查询数据 books = BookInfo.objects.all() #导入分页类 from django.core.paginator import Paginator #创建分页实例 paginator=Paginator(books,2) #获取指定页码的数据 page_skus = paginator.page(1) #获取分页数据 total_page=paginator.num_pages
1 2 3 删除缓存,生成的初始化文件,数据库表。 重新运行命名,不行的话指定应用名生成 python manage.py makemigrations goods
Django中的视图 Django中的视图
1 2 3 4 5 6 7 * 对于Django的设计框架MVT. * 用户在URL中请求的是视图. * 视图接收请求后进行处理. * 并将处理的结果返回给请求者. * 使用视图时需要进行两步操作 * 1.定义视图 * 2.配置URLconf
怎么创建视图 1 2 就是在views.py中定义一个第一个参数是request的函数,该函数返回HttpResponse对象,包含响应信息。 from django.http import HttpResponse
配置URLconf Django查找视图的过程:
1 拿到url,到项目/urls.py中的urlpatterns匹配。成功则到对应模块,失败则返回404.
1 2 3 1.在项目中定义URLconf http://ip:port/path/?key=value (urpatterns中,正则表达式匹配的是path) 2.在应用中定义URLconf
1 2 可以在项目url中配置,但是为了规范.项目中配置各子应用的。子应用中配置各功能的。 注意:配置的时候,是项目的匹配规则+子应用的匹配规则。所以项目中配置/book/子应用就不用再配置/book了。
正则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1. 正则部分推荐使用 r,表示字符串不转义,这样在正则 表达式中使用 \ 只写一个就可以 2. 不能在开始加反斜杠,推荐在结束加反斜杠 正确:path/ 正确:path 错误:/path 错误:/path/ 3. 请求的url被看做是一个普通的python字符串,进行匹配时不包括域名、get或post参数 3.1 如请求地址如下: http://127.0.0.1:8000/18/?a=10 3.2 去掉域名和参数部分后,只剩下如下部分与正则匹配 18/ ps: 虽然路由结尾带/能带来上述好处,但是却违背了HTTP中URL表示资源位置路径的设计理念。 是否结尾带/以所属公司定义风格为准。
配置local host和本地ip都可以访问
路由命名 1 2 3 4 5 6 7 8 9 10 方便查找特定视图的具体路径信息。 url(r'^',include('book.urls',namespace='book')) # 凡是book.urls中定义的路由,均属于namespace指明的book名下。避免不同应用中的路由使用了相同的名字发生冲突,使用命名空间区别开。 urlpatterns = [ url(r'^$',index), # 匹配书籍列表信息的URL,调用对应的bookList视图 url(r'^booklist/$',bookList,name='index'), url(r'^testproject/$',views.testproject,name='test'), ]
1 通过reverse,查找路由命名,查到路由。这样就可以达到路由动态效果。路由修改起来也很方便。
1 2 3 4 5 6 7 8 9 # 下面这个可以防止不同子应用下的路由命名相同。 url(r'^',include('book.urls',namespace='book')) # 定义视图:提供书籍列表信息 def bookList(request): url = reverse('book:test') print(url) return HttpResponse('index') * 对于未指明namespace的,reverse(路由name) * 对于指明namespace的,reverse(命名空间namespace:路由name)
获取 HttpRequest 请求 的参数 1 2 3 4 5 6 # 用HTTP协议向服务器传参有几种途径? * 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取; * 查询字符串(query string),形如key1=value1&key2=value2; * 请求体(body)中发送的数据,比如表单数据、json、xml; * 在http报文的头(header)中。 这些地方的信息,和我们后面爬虫的时候,息息相关
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 如果想从URL中获取值,需要在正则表达式中使用分组,获取值分为两种方式: * 位置参数 * 参数的位置不能错 * 关键字参数 * 参数的位置可以变,跟关键字保持一致即可 ps: 两种参数的方式不要混合使用,在一个正则表达式中只能使用一种参数方式 # 尝试获取url参数信息: http://127.0.0.1:8000/18/188/ 1. 通过位置参数方式获取 应用中urls.py: url(r'^(\d+)/(\d+)/$', views.index), 视图中函数: 参数的位置不能错 def index(request, value1, value2): # 构造上下文 context = {'v1':value1, 'v2':value2} return render(request, 'Book/index.html', context) 2. 通过关键字参数方式获取 应用中urls.py:url(r'^(?P<value1>\d+)/(?P<value2>\d+)/$', views.index), * 其中?P<value1>部分表示为这个参数定义的名称为value1 * 可以是其它名称,起名要做到见名知意 视图中函数: 参数的位置可以变,跟关键字保持一致即可 def index(request, value2, value1): # 构造上下文 context = {'v1':value1, 'v2':value2} return render(request, 'Book/index.html', context)
1 2 3 4 5 6 7 8 9 10 11 12 13 形如?k1=v1&k2=v2,可以通过request.GET属性获取,返回QueryDict对象。 # /get/?a=1&b=2&a=3 def get(request): a = request.GET.get('a') b = request.GET.get('b') alist = request.GET.getlist('a') print(a) # 3 print(b) # 2 print(alist) # ['1', '3'] return HttpResponse('OK') 重要:查询字符串不区分请求方式,即假使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串数据。
1 2 3 请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。 可以发送请求体数据的请求方式有POST、PUT、PATCH、DELETE。 Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如:
1 2 3 4 5 6 7 8 9 前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。 def post(request): a = request.POST.get('a') b = request.POST.get('b') alist = request.POST.getlist('a') print(a) print(b) print(alist) return HttpResponse('OK')
非表单类型 Non-Form Data 请求体获取
1 2 3 4 5 6 7 8 9 10 11 非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。request.body返回bytes类型。 例如要获取请求体中的如下JSON数据 {"a": 1, "b": 2}, 可以进行如下方法操作: ps: JSON是双引号 import json def post_json(request): json_str = request.body json_str = json_str.decode() # python3.6 无需执行此步 req_data = json.loads(json_str) print(req_data['a']) print(req_data['b']) return HttpResponse('OK')
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 可以通过request.META属性获取请求头headers中的数据,request.META为字典类型。 常见的请求头如: * CONTENT_LENGTH – The length of the request body (as a string). * CONTENT_TYPE – The MIME type of the request body. * HTTP_ACCEPT – Acceptable content types for the response. * HTTP_ACCEPT_ENCODING – Acceptable encodings for the response. * HTTP_ACCEPT_LANGUAGE – Acceptable languages for the response. * HTTP_HOST – The HTTP Host header sent by the client. * HTTP_REFERER – The referring page, if any. * HTTP_USER_AGENT – The client’s user-agent string. * QUERY_STRING – The query string, as a single (unparsed) string. * REMOTE_ADDR – The IP address of the client. * REMOTE_HOST – The hostname of the client. * REMOTE_USER – The user authenticated by the Web server, if any. * REQUEST_METHOD – A string such as"GET"or"POST". * SERVER_NAME – The hostname of the server. * SERVER_PORT – The port of the server (as a string). 具体使用如: def get_headers(request): print(request.META['CONTENT_TYPE']) return HttpResponse('OK')
也可以自己写header请求头里加信息(系统会自动加HTTP_前缀)
1 2 3 4 5 6 7 * method:一个字符串,表示请求使用的HTTP方法,常用值包括:'GET'、'POST'。 * user:请求的用户对象。 * path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。 * encoding:一个字符串,表示提交的数据的编码方式。 * 如果为None则表示使用浏览器的默认设置,一般为utf-8。 * 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。 * FILES:一个类似于字典的对象,包含所有的上传文件。
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 32 33 34 35 36 37 38 39 40 41 42 43 视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。 1.可以使用django.http.HttpResponse来构造响应对象。 HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码) 2.也可通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码: * content:表示返回的内容。 * status_code:返回的HTTP响应状态码。 响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置: response = HttpResponse() response['itcast'] = 'Python' # 自定义响应头Itcast, 值为Python 示例: from django.http import HttpResponse def response(request): return HttpResponse('itcast python', status=400) 或者 response = HttpResponse('itcast python') response.status_code = 400 response['itcast'] = 'Python' return response # 状态码 Django提供了一系列HttpResponse的子类,可以快速设置状态码 * HttpResponseRedirect 301 * HttpResponsePermanentRedirect 302 * HttpResponseNotModified 304 * HttpResponseBadRequest 400 * HttpResponseNotFound 404 * HttpResponseForbidden 403 * HttpResponseNotAllowed 405 * HttpResponseGone 410 * HttpResponseServerError 500 # 若要返回json数据,可以使用JsonResponse来构造响应对象,作用: * 帮助我们将数据转换为json字符串 * 设置响应头Content-Type为application/json from django.http import JsonResponse def response(request): return JsonResponse({'city': 'beijing', 'subject': 'python'}) # redirect重定向 from django.shortcuts import redirect def response(request): return redirect('/get_header')
类视图 1 思考:一个视图,是否可以处理两种逻辑?比如get和post请求逻辑。
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 32 33 34 35 36 37 # 注册视图处理get和post请求 以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。 def register(request): """处理注册""" # 获取请求方法,判断是GET/POST请求 if request.method == 'GET': # 处理GET请求,返回注册页面 return render(request, 'register.html') else: # 处理POST请求,实现注册逻辑 return HttpResponse('这里实现注册逻辑') ===========> 优化 在Django中也可以使用类来定义一个视图,称为类视图。 使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示 from django.views.generic import View class RegisterView(View): """类视图:处理注册""" def get(self, request): """处理GET请求,返回注册页面""" return render(request, 'register.html') def post(self, request): """处理POST请求,实现注册逻辑""" return HttpResponse('这里实现注册逻辑') 类视图的好处: * 代码可读性好 * 类视图相对于函数视图有更高的复用性 , 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可 定义类视图需要继承自Django提供的父类View,可使用from django.views.generic import View或者from django.views.generic.base import View导入,定义方式如上所示 # 配置路由时,使用类视图的as_view()方法来添加 urlpatterns = [ # 视图函数:注册 # url(r'^register/$', views.register, name='register'), # 类视图:注册 url(r'^register/$', views.RegisterView.as_view(), name='register'), ]
1 2 3 4 5 6 7 8 9 10 11 12 13 class CenterView(View): def get(self,request): return HttpResponse("OK") def post(self,request): return HttpResponse("OK") 使用面向对象多继承的特性。 class CenterView(LoginRequireMixin,View): def get(self,request): return HttpResponse("OK") def post(self,request): return HttpResponse("OK")
中间件:https://docs.djangoproject.com/en/1.11/topics/http/middleware/
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 32 33 34 35 36 37 38 39 40 41 Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。 我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。 1 中间件的定义方法 定义一个中间件工厂函数,然后返回一个可以被调用的中间件。 中间件工厂函数需要接收一个可以调用的get_response对象。 返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。 def simple_middleware(get_response): # 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。 def middleware(request): # 此处编写的代码会在每个请求处理视图前被调用。 response = get_response(request) # 此处编写的代码会在每个请求处理视图之后被调用。 return response return middleware 例如,在book应用中新建一个middleware.py文件, def my_middleware(get_response): print('init 被调用') def middleware(request): print('before request 被调用') response = get_response(request) print('after response 被调用') return response return middleware # 定义好中间件后,需要在settings.py 文件中添加注册中间件 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', 'book.middleware.my_middleware', # 添加中间件 ] # 定义一个视图进行测试 def middleware(request): print('view 视图被调用') return HttpResponse('OK')
1 2 3 4 5 注意:Django运行在调试模式下,中间件init部分有可能被调用两次。 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 32 33 34 35 36 37 38 39 40 41 示例: 定义两个中间件 def my_middleware(get_response): print('init 被调用') def middleware(request): print('before request 被调用') response = get_response(request) print('after response 被调用') return response return middleware def my_middleware2(get_response): print('init2 被调用') def middleware(request): print('before request 2 被调用') response = get_response(request) print('after response 2 被调用') return response return middleware 注册添加两个中间件 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', 'users.middleware.my_middleware', # 添加 'users.middleware.my_middleware2', # 添加 ] 执行结果 init2 被调用 init 被调用 before request 被调用 before request 2 被调用 view 视图被调用 after response 2 被调用 after response 被调用
Django中的模板 Django中的模板
怎么创建模板? 1 2 3 1.在应用同级目录下创建模板文件夹templates 2.在templates文件夹下, 创建应用同名文件夹. 例, Book 3.在应用同名文件夹下创建网页模板文件. 例 :index.html
设置模板查找路径
模板使用 1 将渲染的内容以字典传入页面,页面中以 {{}} 接收
总体的流程:
Django自带模板 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 1 配置 在工程中创建模板目录templates。 在settings.py配置文件中修改TEMPLATES配置项的DIRS值: TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], # 此处修改 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] 2 定义模板 在templates目录中新建一个模板文件,如index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ city }} </body> </html> 3 模板渲染 调用模板分为两步骤: 1. 找到模板 loader.get_template(模板文件在模板目录中的相对路径) -> 返回模板对象 2. 渲染模板 模板对象.render(context=None, request=None) -> 返回渲染后的html文本字符串 context 为模板变量字典,默认值为None request 为请求对象,默认值为None 例如,定义一个视图 from django.http import HttpResponse from django.template import loader def index(request): # 1.获取模板 template=loader.get_template('index.html') context={'city': '北京'} # 2.渲染模板 return HttpResponse(template.render(context)) Django提供了一个函数render可以简写上述代码。 render(request对象, 模板文件路径, 模板数据字典) from django.shortcuts import render def index(request): context={'city': '北京'} return render(request,'index.html',context) 4 模板语法 4.1 模板变量 变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。 语法如下: 模板变量可以使python的内建类型,也可以是对象。 def index(request): context = { 'city': '北京', 'adict': { 'name': '西游记', 'author': '吴承恩' }, 'alist': [1, 2, 3, 4, 5] } return render(request, 'index.html', context)
1 2 3 4 5 6 7 8 9 10 11 12 比较运算符如下: == != < > <= >= 布尔运算符如下: and or not 注意:运算符左右两侧不能紧挨变量或常量,必须有空格。
过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 语法如下: * 使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。 * 如果过滤器需要参数,则使用冒号:传递参数。 * 变量|过滤器:参数 列举几个如下: * safe,禁用转义,告诉模板这个变量是安全的,可以解释执行 * length,长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数。 * default,默认值,如果变量不存在时则返回默认值。 * data|default:'默认值' * date,日期,用于对日期类型的值进行字符串格式化,常用的格式化字符如下: * Y表示年,格式为4位,y表示两位的年。 * m表示月,格式为01,02,12等。 * d表示日, 格式为01,02等。 * j表示日,格式为1,2等。 * H表示时,24进制,h表示12进制的时。 * i表示分,为0-59。 * s表示秒,为0-59。 * value|date:"Y年m月j日 H时i分s秒"
模板继承 1 2 3 4 5 6 模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量。 父模板 如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。 标签block:用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同。 为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。 子模板 标签extends:继承,写在子模板文件的第一行。
1 子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。
Django使用jinja2模板
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 32 33 jinja2介绍 Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,尤其是Flask框架内置的模板语言 由于django默认模板引擎功能不齐全,速度慢,所以我们也可以在Django中使用jinja2, jinja2宣称比django默认模板引擎快10-20倍。 Django主流的第三方APP基本上也都同时支持Django默认模板及jinja2,所以要用jinja2也不会有多少障碍。 安装jinja2模块 pip install jinja2 Django配置jinja2 1. 在项目文件中创建 jinja2_env.py 文件 from jinja2 import Environment def environment(**options): env = Environment(**options) return env 2.在settings.py文件 TEMPLATES = [ { 'BACKEND': 'django.template.backends.jinja2.Jinja2',#修改1 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS':True, 'OPTIONS':{ 'environment': 'jinja2_env.environment',# 修改2 'context_processors':[ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] jinja2模板的使用绝大多数和Django自带模板一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 jinja2自定义过滤器:https://docs.djangoproject.com/en/1.11/topics/templates/#module-django.template.backends.django 在jinja2_env.py文件中自定义过滤器 from jinja2 import Environment def environment(**options): env = Environment(**options) # 2.将自定义的过滤器添加到 环境中 env.filters['do_listreverse'] = do_listreverse return env # 1.自定义过滤器 def do_listreverse(li): if li == "B": return "哈哈"
CSRF 1 2 3 4 5 6 7 * CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。 * CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。 * 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账...... * 造成的问题:个人隐私泄露以及财产安全。 CSRF攻击示意图 * 客户端访问服务器时没有同服务器做安全验证
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 防止 CSRF 攻击 步骤 1. 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值 2. 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token 3. 在用户点击提交的时候,会带上这两个值向后台发起请求 4. 后端接受到请求,以会以下几件事件: * 从 cookie中取出 csrf_token * 从 表单数据中取出来隐藏的 csrf_token 的值 * 进行对比 5. 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作 代码演示 未进行 csrf 校验的 WebA * 后端代码实现 #定义路由 from django.conf.urls import url from pay import views urlpatterns = [ url(r'^$',views.LoginView.as_view(),name='index'), #登录路由 url(r'^transfer/$',views.TransferView.as_view(),name='transfer'), #转账路由 ] #定义视图 class LoginView(View): def post(self,request): # 取到表单中提交上来的参数 username = request.POST.get("username") password = request.POST.get("password") if not all([username, password]): print('参数错误') else: print(username, password) if username == 'laowang' and password == '1234': # 状态保持,设置用户名到cookie中表示登录成功 response = redirect(reverse('transfer')) response.set_cookie('username', username) return response else: print('密码错误') return render(request,'login.html') def get(self,request): return render(request,'login.html') class TransferView(View): def post(self,request): # 从cookie中取到用户名 username = request.COOKIES.get('username', None) # 如果没有取到,代表没有登录 if not username: return redirect(reverse('index')) to_account = request.POST.get("to_account") money = request.POST.get("money") print('假装执行转操作,将当前登录用户的钱转账到指定账户') return HttpResponse('转账 %s 元到 %s 成功' % (money, to_account)) def get(self, request): # 渲染转换页面 response = render(request, 'transfer.html') return response * 前端登录页面代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <h1>我是网站A,登录页面</h1> <form method="post"> <label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/> <label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/> <input type="submit" value="登录"> </form> </body> </html> * 前端转账页面代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>转账</title> </head> <body> <h1>我是网站A,转账页面</h1> <form method="post"> <label>账户:</label><input type="text" name="to_account" placeholder="请输入要转账的账户"><br/> <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/> <input type="submit" value="转账"> </form> </body> </html> 运行测试,如果在未登录的情况下,不能直接进入转账页面,测试转账是成功的 攻击网站B的代码 * 后端代码实现 #定义路由 from django.conf.urls import url from ads import views urlpatterns = [ url(r'^$',views.AdsView.as_view()), ] #定义视图 class AdsView(View): def get(self,request): return render(request,'index.html') * 前端代码实现 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>我是网站B</h1> <form method="post" action="http://127.0.0.1:9000/transfer/"> <input type="hidden" name="to_account" value="黑客"> <input type="hidden" name="money" value="190000" hidden> <input type="submit" value="点击领取优惠券"> </form> </body> </html> 运行测试,在用户登录网站A的情况下,点击网站B的按钮,可以实现伪造访问 在网站A中实现 csrf_token 校验的流程 * 导入生成 csrf_token 的函数 from django.middleware.csrf import get_token csrf_token = get_token(request) * 在渲染转账页面的,做以下几件事情: 1. 生成 csrf_token 的值 2. 在返回转账页面的响应里面设置 csrf_token 到 cookie 中 3. 将 csrf_token 保存到表单的隐藏字段中 * def get(self, request): # 生成csrf_token from django.middleware.csrf import get_token csrf_token = get_token(request) # 渲染转换页面,传入 csrf_token 到模板中 response = render(request, 'transfer.html',context={'csrf_token':csrf_token}) # 设置csrf_token到cookie中,用于提交校验 response.set_cookie('csrf_token', csrf_token) return response * 在转账模板表单中添加 csrf_token 隐藏字段 <head> <meta charset="UTF-8"> <title>转账</title> </head> <body> <h1>我是网站A,转账页面</h1> <form method="post"> <input type="hidden" name="csrftoken" value="{{ csrf_token }}"> <label>账户:</label><input type="text" name="to_account" placeholder="请输入对方账户"><br/> <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/> <input type="submit" value="转账"> </form> </body> </html> 运行测试,进入到转账页面之后,查看 cookie 和 html 源代码
1 2 3 4 5 6 7 8 9 10 11 * 在执行转账逻辑之前进行 csrf_token 的校验 # 取出表单中的 csrf_token form_csrf_token = request.POST.get("csrftoken") # 取出 cookie 中的 csrf_token cookie_csrf_token = request.COOKIES.get('csrf_token') # 进行对比 if cookie_csrf_token != form_csrf_token: return HttpResponse('token校验失败,可能是非法操作') 运行测试,用户直接在网站 A 操作没有问题,再去网站B进行操作,发现转账不成功,因为网站 B 获取不到表单中的 csrf_token 的隐藏字段,而且浏览器有同源策略,网站B是获取不到网站A的 cookie 的,所以就解决了跨站请求伪造的问题 在 Django项目中解决 CSRF 攻击 Django默认是开启CSRF的
1 2 3 4 模板中设置 CSRF 令牌 {% csrf_token %} 或者 <input type="hidden" value="{{ csrf_token }}">
cookie 和 session 07_cookie 和 session
状态保持 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 浏览器请求服务器是无状态的。 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。 无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等 实现状态保持主要有两种方式: 在客户端存储信息使用Cookie 在服务器端存储信息使用Session --------------- Cookie Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。 Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。 Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。 Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型记住用户名。 Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用。 特点: Cookie以键值对的格式进行信息的存储。 Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。
使用Cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1 设置Cookie 可以通过HttpResponse对象中的set_cookie方法来设置cookie。 HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期) - max_age单位为秒,默认为None 。如果是临时cookie,可将max_age设置为None。 def cookie(request): response = HttpResponse('ok') response.set_cookie('itcast1', 'python1') # 临时cookie(浏览器关闭就过期) response.set_cookie('itcast2', 'python2', max_age=3600) # cookie有效期一小时,浏览器关闭再打开还可以看到 return response 2 读取Cookie 可以通过HttpResponse对象的COOKIES属性来读取本次请求携带的cookie值。request.COOKIES为字典类型。 def cookie(request): cookie1 = request.COOKIES.get('itcast1') print(cookie1) return HttpResponse('OK') 3 删除Cookie 可以通过HttpResponse对象中的delete_cookie方法来删除。 response.delete_cookie('itcast2') # 底层本质就是设置过期时间max_age=0
session 1 2 3 session依赖于cookie,如果浏览器禁用了cookie,session就不能用了(一些后台逻辑会用到session,这样就有问题了,京东淘宝是返回错误加载图片) 大白话:session信息保存再服务端,可以存储一些用户密码之类的信息.相对cookie安全。(服务器自动)返回sessionId,存在客户端的cookie.每次携带sessionId,(服务器自动)根据sessionId判断,用于识别身份。
1 Django项目默认启用Session,可以在settings.py文件中查看,如图所示
1 如需禁用session,将上图中的session中间件注释掉即可。
1 2 3 4 5 在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。 2.1 数据库 存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。 SESSION_ENGINE='django.contrib.sessions.backends.db' 如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。
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 操作Session包括三个数据:键,值,过期时间。 2.2 本地缓存 存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。 SESSION_ENGINE='django.contrib.sessions.backends.cache' 2.3 混合存储 优先从本机内存中存取,如果没有则从数据库中存取。 SESSION_ENGINE='django.contrib.sessions.backends.cached_db' 2.4 Redis 在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。 网络连接:https://django-redis-chs.readthedocs.io/zh_CN/latest/ 安装 :pip install django-redis 在settings.py文件中做如下设置 CACHES = { 'default': { 'BACKEND': 'django_redis.cache.RedisCache', 'LOCATION': 'redis://127.0.0.1:6379/1', 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', } } } SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_CACHE_ALIAS = 'default' ps:如果redis的ip地址不是本地回环127.0.0.1,而是其他地址,访问Django时,可能出现Redis连接错误,如下:
1 2 3 4 5 解决方法: 修改redis的配置文件,添加特定ip地址。 打开redis的配置文件 sudo vim /etc/redis/redis.conf 在如下配置项进行修改(如要添加10.211.55.5地址)
1 2 重新启动redis服务 sudo service redis-server restart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 通过HttpRequest对象的session属性进行会话的读写操作。 1) 以键值对的格式写session。 request.session['键']=值 2)根据键读取值。 request.session.get('键',默认值) # 这个好些,如果不存在不会报错 3)清除所有session,在存储中删除值部分。 request.session.clear() 4)清除session数据,在存储中删除session的整条数据。 request.session.flush() 5)删除session中的指定键及值,在存储中只删除某个键及对应的值。 del request.session['键'] 6)设置session的有效期 request.session.set_expiry(value) * 如果value是一个整数,session将在value秒没有活动后过期。 * 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。 * 如果value为None,那么session有效期将采用系统默认值, 默认为两周,可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。
美哆商城
DRF框架
美哆商城后台
项目部署
flask框架
黑马头条
人工智能基础
推荐系统基础
黑马头条推荐系统
python测试
NLP自然语言处理
深度学习与机器视觉
爬虫
数据分析
Python操作 Python压缩解压zip文件 Python压缩解压zip文件
导入相关模块 1 2 3 4 import os import shutil import zipfile from os.path import join, getsize
python压缩指定文件夹 1 2 3 4 5 6 7 8 9 10 def zip_file(src_dir): zip_name = src_dir +'.zip' z = zipfile.ZipFile(zip_name,'w',zipfile.ZIP_DEFLATED) for dirpath, dirnames, filenames in os.walk(src_dir): fpath = dirpath.replace(src_dir,'') fpath = fpath and fpath + os.sep or '' for filename in filenames: z.write(os.path.join(dirpath, filename),fpath+filename) print ('==压缩成功==') z.close()
其中
1 2 src_dir:你要压缩的文件夹的路径 zip_name:压缩后zip文件的路径及名称
python解压zip 1 2 3 4 5 6 7 8 def unzip_file(zip_src, dst_dir): r = zipfile.is_zipfile(zip_src) if r: fz = zipfile.ZipFile(zip_src, 'r') for file in fz.namelist(): fz.extract(file, dst_dir) else: print('This is not zip')
其中
1 2 zip_src:是zip文件的全路径 dst_dir:是要解压到的目的文件夹
Python其它操作文件方法
1 shutil.move(filename, dst_dir)
1 for filename in os.listdir(src_dir):
1 shutil.copyfile(src_file,dst_file)
1 2 3 4 5 def get_dir_size(dir_path): size = 0L for root, dirs, files in os.walk(dir_path): size += sum([getsize(join(root, name)) for name in files]) return size
可以根据文件大小做不同的判断,如:
1 2 3 4 file_size = get_dir_size(DATA_PATH) max_size = file_size / 1024 / 1024 ##获得的是以Mb为单位的值 if max_size < 100: pass
python xlrd python xlrd
1 2 3 要爬虫导出excel并解析 - xlrd模块实现对excel文件内容读取 - xlwt模块实现对excel文件的写入。
xlrd模块使用:
(1) 打开excel文件并获取所有sheet
1 2 3 4 5 import xlrd # 打开Excel文件读取数据 data = xlrd.open_workbook('联系人.xls') sheet_name = data.sheet_names() # 获取所有sheet名称 print(sheet_name) # ['银行2', '银行3']
补充rb知识
1 2 3 4 "r" 以读方式打开,只能读文件 , 如果文件不存在,会发生异常 "w" 以写方式打开,只能写文件, 如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件 "rb" 以二进制读方式打开,只能读文件 , 如果文件不存在,会发生异常 "wb" 以二进制写方式打开,只能写文件, 如果文件不存在,创建该文件;如果文件已存在,先清空,再打开文件
(2) 根据下标获取sheet名称,下表从0开始
1 2 3 # 根据下标获取sheet名称 sheet2_name = data.sheet_names()[1] print(sheet2_name) # '银行3'
(3) 根据sheet索引或者名称获取sheet内容,同时获取sheet名称、行数、列数
1 2 3 4 5 6 7 8 9 10 11 12 # 根据sheet索引或者名称获取sheet内容,同时获取sheet名称、列数、行数 sheet2 = data.sheet_by_index(1) print('sheet2名称:{}\nsheet2列数: {}\nsheet2行数: {}'.format(sheet2.name, sheet2.ncols, sheet2.nrows)) # sheet2名称:银行3 # sheet2列数: 7 # sheet2行数: 5 sheet1 = data.sheet_by_name('银行2') print('sheet1名称:{}\nsheet1列数: {}\nsheet1行数: {}'.format(sheet1.name, sheet1.ncols, sheet1.nrows)) # sheet1名称:银行2 # sheet1列数: 8 # sheet1行数: 6
(4) 根据sheet名称获取整行和整列的值
1 2 3 4 5 6 # 根据sheet名称获取整行和整列的值 sheet1 = data.sheet_by_name('银行2') print(sheet1.row_values(3)) # ['', '张2', '开发', 'IT编码', 999.0, 133111.0, 41463.0, 'zhang2@164.com'] 日期2013/7/7,实际却显示为浮点数41463.0 print(sheet1.col_values(3)) # ['', '工作职责', '', 'IT编码', '网络维修', '']
(5)获取指定单元格的内容
1 2 3 4 # 获取指定单元格的内容 print(sheet1.cell(1,0).value) # 第2 行1列内容:机构名称 print(sheet1.cell_value(1,0)) # 第2 行1列内容:机构名称 print(sheet1.row(1)[0].value) # 第2 行1列内容:机构名称
(6)获取单元格内容的数据类型
1 2 3 4 5 # 获取单元格内容的数据类型 print(sheet1.cell(1,0).ctype) # 第2 行1列内容 :机构名称为string类型 print(sheet1.cell(3,4).ctype) # 第4行5列内容:999 为number类型 print(sheet1.cell(3,6).ctype) # 第4 行7列内容:2013/7/8 为date类型 # 说明:ctype : 0 empty,1 string, 2 number, 3 date, 4 boolean, 5 error
(7)获取单元内容为日期类型的方式
使用xlrd的xldate_as_tuple处理为date格式
1 2 3 4 5 6 7 8 from datetime import datetime,date if sheet1.cell(3,6).ctype == 3 : print(sheet1.cell(3, 6).value) # 41463.0 date_value = xlrd.xldate_as_tuple(sheet1.cell(3, 6).value, data.datemode) print(date_value) # (2013, 7, 8, 0, 0, 0) print(date(*date_value[:3])) # 2013-07-08 print(date(*date_value[:3]).strftime('%Y/%m/%d')) # 2013/07/08
(8)获取单元内容为number的方式(转为整型)
1 2 3 4 if sheet1.cell(3, 5).ctype == 2: print(sheet1.cell(3, 5).value) # 133111.0 num_value = int(sheet1.cell(3, 5).value) print(num_value) # 133111
(9) 获取合并单元格的内容
需要merged_cells属性
1 2 3 4 5 6 7 8 9 10 11 # 这里,需要在读取文件的时候添加个参数,将formatting_info参数设置为True,默认是False,否则可能调用merged_cells属性获取到的是空值。 data = xlrd.open_workbook('联系人.xls',formatting_info=True) sheet1 = data.sheet_by_name('银行2') print(sheet1.merged_cells) # [(0, 1, 0, 8), (2, 6, 0, 1)] # merged_cells返回的这四个参数的含义是:(row,row_range,col,col_range),其中[row,row_range)包括row,不包括row_range,col也是一样,下标从0开始。 #(0, 1, 0, 8) 表示1列-8列合并 (2, 6, 0, 1)表示3行-6行合并 #分别获取合并2个单元格的内容: print(sheet1.cell(0,0).value) # 银行2 print(sheet1.cell_value(2, 0)) # 银行2
规律 : 获取merge_cells返回的row和col低位的索引即可!
使用以下方法更加方便
1 2 3 4 5 6 7 8 merge_value = [] for (row,row_range,col,col_range) in sheet1.merged_cells: merge_value.append((row,col)) print(merge_value) # [(0, 0), (2, 0)] for v in merge_value: print(sheet1.cell(v[0], v[1]).value) # 银行2 # 银行2
python 使用 redis 安装Redis的有3种方式https://github.com/andymccurdy/redis-py
调⽤模块
准备
在桌面上创建redis目录
使用pycharm打开 redis目录
创建redis_string.py文件
1 2 3 4 5 6 7 8 from redis import *if __name__=="__main__" : try : sr=StrictRedis() except Exception as e: print (e)
string-增加
⽅法set,添加键、值,如果添加成功则返回True,如果添加失败则返回False
编写代码如下
1 2 3 4 5 6 7 8 9 10 11 from redis import *if __name__=="__main__" : try : sr=StrictRedis() result=sr.set ('name' ,'itheima' ) print (result) except Exception as e: print (e)
string-获取
⽅法get,添加键对应的值,如果键存在则返回对应的值,如果键不存在则返回None
编写代码如下
1 2 3 4 5 6 7 8 9 10 11 from redis import *if __name__=="__main__" : try : sr=StrictRedis() result = sr.get('name' ) print (result) except Exception as e: print (e)
string-修改
⽅法set,如果键已经存在则进⾏修改,如果键不存在则进⾏添加
编写代码如下
1 2 3 4 5 6 7 8 9 10 11 from redis import *if __name__=="__main__" : try : sr=StrictRedis() result = sr.set ('name' ,'itcast' ) print (result) except Exception as e: print (e)
string-删除
⽅法delete,删除键及对应的值,如果删除成功则返回受影响的键数,否则则返 回0
编写代码如下
1 2 3 4 5 6 7 8 9 10 11 from redis import *if __name__=="__main__" : try : sr=StrictRedis() result = sr.delete('name' ) print (result) except Exception as e: print (e)
获取键
1 2 3 4 5 6 7 8 9 10 11 from redis import *if __name__=="__main__" : try : sr=StrictRedis() result=sr.keys() print (result) except Exception as e: print (e)
操作redis集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from rediscluster import *if __name__ == '__main__' : try : startup_nodes = [ {'host' : '192.168.26.128' , 'port' : '7000' }, {'host' : '192.168.26.130' , 'port' : '7003' }, {'host' : '192.168.26.128' , 'port' : '7001' }, ] src=StrictRedisCluster(startup_nodes=startup_nodes,decode_responses=True ) result=src.set ('name' ,'itheima' ) print (result) name = src.get('name' ) print (name) except Exception as e: print (e)