# Django_base **Repository Path**: huanyushi/django_base ## Basic Information - **Project Name**: Django_base - **Description**: Django学习项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-01-06 - **Last Updated**: 2022-01-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、Django初学 ## 一、Django Python开源框架,遵循MVC ### 1) 重量级框架 对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速。 - 提供项目工程管理的自动化脚本工具 - 数据库ORM支持(对象关系映射,英语:Object Relational Mapping) - 模板 - 表单 - Admin管理站点 - 文件管理 - 认证权限 - session机制 - 缓存 ### 2)MVT模式 有一种程序设计模式叫**MVC**,其核心思想是**分工、解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容**。 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641542952738-be822f75-5bb7-47d0-88e6-30658ae8d6b0.png) - M全拼为Model,主要封装对数据库层的访问,对数据库中的数据进行增、删、改、查操作。 - V全拼为View,用于封装结果,生成页面展示的html内容。 - C全拼为Controller,用于接收请求,处理业务逻辑,与Model和View交互,返回结果。 #### Django的MVT ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641542973126-4b3a83e5-e86b-4a79-a010-2a375655bdb8.png) - M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。 - V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。 - T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。 ### 3)初始化项目 #### 1.创建Django项目 ```python django-admin startproject name ``` #### 2.创建子应用 ```python python manager.py startapp name ``` #### 3.注册子应用 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543034663-15e257b9-be9b-4574-9577-7544c50ecd86.png) #### 4.项目运行 ```python python manage.py runserver ``` ### 4)模型 #### 1.数据库开发的提示 - MVT设计模式中的Model, 专门负责和数据库交互.对应(models.py) - 由于Model中内嵌了ORM框架, 所以不需要直接面向数据库编程. - 而是定义模型类, 通过模型类和对象完成数据库表的增删改查. - ORM框架就是把数据库表的行与相应的对象建立关联, 互相转换.使得数据库的操作面向对象. #### 2.数据库开发的步骤 ##### 定义模型类 ##### 模型迁移 - 迁移由两步完成 : - - 生成迁移文件:根据模型类生成创建表的语句 ```python python manage.py makemigrations ``` - - 执行迁移:根据第一步生成的语句在数据库中创建表 **操作数据库** ### 5)站点管理 - **站点**: 分为内容发布和公共访问两部分 - **内容发布**的部分由网站的管理员负责查看、添加、修改、删除数据 - Django能够根据定义的模型类自动地生成管理模块 - 使用Django的管理模块, 需要按照如下步骤操作 : - - 1.管理界面本地化 - 2.创建管理员 - - 3.注册模型类 - 4.发布内容到数据库 #### 管理界面本地化 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543082775-8421cdc1-ee5a-4dcf-92a6-5b9f50cba2d1.png) ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543096403-2ecbffee-c664-4050-9688-681c0f6abe3f.png) #### 创建管理员 - 创建管理员的命令 : ```python python manage.py createsuperuser ``` 按提示输入用户名、邮箱、密码 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543110551-ae2b1ba0-c135-46a9-b39a-5e23db080805.png) - 重置密码 ```plain python manager.py changepassword 用户名 ``` - 登陆站点 :http://127.0.0.1:8000/admin #### 注册模型类 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543129858-f8a87ab3-2a80-4a74-9347-3c6c3aa95654.png) 注册模型成功后, 就可以在站点管理界面方便快速的管理数据 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543144686-46f2262c-ff6d-4c8d-94fb-ae3088db3fa6.png) #### 发布内容到数据库 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543157476-ccf0660e-1021-4166-befc-91190cb92582.png) 优化展示效果 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543180813-5d48c340-0da9-4ec7-ae3a-43d9ade8ec8b.png) ### 6)视图和URL - 站点管理页面做好了, 接下来就要做`公共访问`的页面了. - 对于`Django`的设计框架`MVT` - - 用户在URL中请求的是视图. - 视图接收请求后进行处理. - - 并将处理的结果返回给请求者. - 使用视图时需要进行两步操作 - - 1.定义视图 - 2.配置URLconf #### 定义视图 ```plain from django.http import HttpResponse # Create your views here. """ 1.视图函数的第一个参数接受请求:HttpRequest的类对象 2.必须返回一个相应 """ def index(request): return HttpResponse("ok") ``` #### 配置URLconf ```python from django.contrib import admin from django.urls import path, include # from book.views import index urlpatterns = [ path('admin/', admin.site.urls), # path('路由',视图函数名) # path('index/', index) path('book/', include('book.urls')) ] ``` ### 7)模板(了解) 思考 : 网站如何向客户端返回一个漂亮的页面呢? - 提示 : - - 漂亮的页面需要`html`、`css`、`js`. - 可以把这一堆字段串全都写到视图中, 作为`HttpResponse()`的参数,响应给客户端. - 问题 : - - 视图部分代码臃肿, 耦合度高. - 这样定义的字符串是不会出任何效果和错误的. - - 效果无法及时查看.有错也不容易及时发现. - 设想 : - - 是否可以有一个专门定义前端页面的地方, 效果可以及时展示,错误可以及时发现,并且可以降低模块间耦合度! - 解决问题 :**模板** - - `MVT`设计模式中的`T`,`Template` - **在**`Django`**中, 将前端的内容定义在模板中, 然后再把模板交给视图调用, 各种漂亮、炫酷的效果就出现了.** **模板使用步骤** #### 1.创建模板 - 在`应用`同级目录下创建模板文件夹`templates`. 文件夹名称固定写法. - 在`templates`文件夹下, 创建`应用`同名文件夹. 例, `Book` - 在`应用`同名文件夹下创建`网页模板`文件. 例 :`index.html` ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543197951-4401608c-289e-4cc5-9657-80df3ff81d09.png) #### 2.设置模板查找路径 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543206159-ec97f9c1-113e-4ede-9b04-0d50986cb026.png) #### 3.模板接收视图传入的数据 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543211709-4b314650-2d8c-45c2-b631-cfbaab81ea8c.png) #### 4.模板处理数据 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543218634-73dbe8d7-0efe-4dfe-a46f-cb7a56489704.png) #### 5. 查看模板处理数据成果 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543222689-d3238d2d-fde8-42ee-b224-3461443e4ea8.png) ### 8)配置和静态文件 #### 配置文件 ##### BASE_DIR ```python BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ``` 当前工程的根目录,Django会依此来定位工程内的相关文件,我们也可以使用该参数来构造文件路径。 ##### DEBUG 调试模式,创建工程后初始值为**True**,即默认工作在调试模式下。 作用: - 修改代码文件,程序自动重启 - Django程序出现异常时,向前端显示详细的错误追踪信息 - 而非调试模式下,仅返回Server Error (500) **注意:部署线上运行的Django不要运行在调式模式下,记得修改DEBUG=False和ALLOW_HOSTS。** #### 静态文件 项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。 ##### STATIC_URL 为了提供静态文件,需要配置两个参数: - **STATICFILES_DIRS**存放查找静态文件的目录 - **STATIC_URL**访问静态文件的URL前缀 **示例** 1) 在项目根目录下创建static目录来保存静态文件。 2) 在bookmanager/settings.py中修改静态文件的两个参数为 ```python STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] ``` 3)此时在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配置 我们将此类添加到工程settings.py中的INSTALLED_APPS列表中,表明注册安装具备此配置属性的应用。 - **AppConfig.name**属性表示这个配置类是加载到哪个应用的,每个配置类必须包含此属性,默认自动生成。 - **AppConfig.verbose_name**属性用于设置该应用的直观可读的名字,此名字在Django提供的Admin管理站点中会显示,如 ```plain from django.apps import AppConfig class BookConfig(AppConfig): name = 'book' verbose_name = "书籍管理" ``` ## ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1641543257515-a27d05c4-aa99-4e18-a43b-ac4d2f2fea66.png) ------ ## 二、模型 ### 1)模型配置 #### 1.配置mysql ```python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': 'mysql', # 数据库用户密码 'NAME': 'book' # 数据库名字 } } ``` #### 2.定义模型类 ```python from django.db import models # Create your models here. # 准备书籍列表信息的模型类 class BookInfo(models.Model): # 创建字段,字段类型... name = models.CharField(max_length=10, verbose_name="书名",unique=True) pub_date = models.DateField(verbose_name="发布日期", null=True) read_count = models.IntegerField(default=0, verbose_name="阅读人数") comment_count = models.IntegerField(default=0, verbose_name="评论量") is_delete = models.BooleanField(default=False, verbose_name="逻辑删除") class Meta: db_table = 'bookInfo' # 指明数据库表名 verbose_name = '图书' # 在admin站点中显示的名称 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 ``` ##### **数据库表名** 模型类如果未指明表名,Django默认以**小写app应用名_小写模型类名**为数据库表名。 可通过**db_table**指明数据库表名。 ##### **关于主键** django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。 默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。 ##### **属性命名限制** - 不能是python的保留关键字。 - 不允许使用连续的下划线,这是由django的查询方式决定的。 - 定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下: ```plain 属性=models.字段类型(选项) ``` ##### **字段类型** | 类型 | 说明 | | ---------------- | ------------------------------------------------------------ | | AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 | | BooleanField | 布尔字段,值为True或False | | NullBooleanField | 支持Null、True、False三种值 | | CharField | 字符串,参数max_length表示最大字符个数 | | TextField | 大文本字段,一般超过4000个字符时使用 | | IntegerField | 整数 | | DecimalField | 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数 | | FloatField | 浮点数 | | DateField | 日期, 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add和auto_now是相互排斥的,组合将会发生错误 | | TimeField | 时间,参数同DateField | | DateTimeField | 日期时间,参数同DateField | | FileField | 上传文件字段 | | ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 | ##### **选项** | 选项 | 说明 | | ----------- | ------------------------------------------------------------ | | null | 如果为True,表示允许为空,默认值是False | | blank | 如果为True,则该字段允许为空白,默认值是False | | db_column | 字段的名称,如果未指定,则使用属性的名称 | | db_index | 若值为True, 则在表中会为此字段创建索引,默认值是False | | default | 默认 | | primary_key | 若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用 | | unique | 如果为True, 这个字段在表中必须有唯一值,默认值是False | **null是数据库范畴的概念,blank是表单验证范畴的** ##### **外键** 在设置外键时,需要通过**on_delete**选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量: - **CASCADE**级联,删除主表数据时连通一起删除外键表中数据 - **PROTECT**保护,通过抛出**ProtectedError**异常,来阻止删除主表中被外键应用的数据 - **SET_NULL**设置为NULL,仅在该字段null=True允许为null时可用 - **SET_DEFAULT**设置为默认值,仅在该字段设置了默认值时可用 - **SET()**设置为特定值或者调用特定方法 - **DO_NOTHING**不做任何操作,如果数据库前置指明级联性,此选项会抛出**IntegrityError**异常 #### 3.迁移数据库 将模型类同步到数据库中。 ##### **生成迁移文件** ```plain python manage.py makemigrations ``` ##### **同步到数据库中** ```plain python manage.py migrate ``` ### 2)数据操作 #### shell工具 Django的manage工具提供了**shell**命令,帮助我们配置好当前工程的运行环境(如连接好数据库等),以便可以直接在终端中执行测试python语句。 ```python python manage.py shell ``` #### 1.增加 ##### save ```python >>> from book.models import BookInfo,PeopleInfo >>> book = BookInfo( ... name='python入门', ... pub_date='2010-1-1' ... ) >>> book.save() >>> book ``` ##### create ```py >>> PeopleInfo.objects.create( ... name='itheima', ... book=book ... ) ``` #### 2.修改 ##### save **修改模型类对象的属性,然后执行save()方法** ```python >>> person = PeopleInfo.objects.get(name='itheima') >>> person.name = 'itcast' >>> person.save() >>> person ``` ##### update **使用模型类.objects.filter().update()**,会返回受影响的行数 ```python >>> PeopleInfo.objects.filter(name='itcast').update(name='传智播客') ``` #### 3.删除 ##### delete ```python >>> person = PeopleInfo.objects.get(name='传智播客') >>> person.delete() (1, {'book.PeopleInfo': 1}) ``` ##### objects.filter().delete() ```py >>> BookInfo.objects.filter(name='python入门').delete() (1, {'book.BookInfo': 1, 'book.PeopleInfo': 0}) ``` #### 4.查询 ##### 基础查询 **get**查询单一结果,如果不存在会抛出**模型类.DoesNotExist**异常。 **all**查询多个结果。 **count**查询结果数量。 ```python >>> BookInfo.objects.get(id=1) >>> BookInfo.objects.get(pk=2) >>> BookInfo.objects.get(pk=20) Traceback (most recent call last): File "", line 1, in 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() , , , ]> >>> BookInfo.objects.count() 4 ``` ##### 过滤查询 实现SQL中的where功能,包括 - **filter**过滤出多个结果 - **exclude**排除掉符合条件剩下的结果 - **get**过滤单一结果 对于过滤条件的使用,上述三个方法相同,故仅以**filter**进行讲解。 过滤条件的表达语法如下: ```python 属性名称__比较运算符=值 # 属性名称和比较运算符间使用两个下划线,所以属性名不能包括多个下划线 查询编号为1的图书 查询书名包含'湖'的图书 查询书名以'部'结尾的图书 查询书名为空的图书 查询编号为1或3或5的图书 查询编号大于3的图书 查询1980年发表的图书 查询1990年1月1日后发表的图书 ``` **1)相等** **exact:表示判等。** 例:查询编号为1的图书。 ```python BookInfo.objects.filter(id__exact=1) 可简写为: BookInfo.objects.filter(id=1) ``` **2)模糊查询** **contains:是否包含。** 说明:如果要包含%无需转义,直接写即可。 例:查询书名包含'传'的图书。 ```python BookInfo.objects.filter(name__contains='传') ]> ``` **startswith、endswith:以指定值开头或结尾。** 例:查询书名以'部'结尾的图书 ```python >>> BookInfo.objects.filter(name__endswith='部') ]> ``` 以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith. **3) 空查询** **isnull:是否为null。** 例:查询书名为空的图书。 ```python >>> BookInfo.objects.filter(name__isnull=True) ``` **4) 范围查询** **in:是否包含在范围内。** 例:查询编号为1或3或5的图书 ```python >>> BookInfo.objects.filter(id__in=[1,3,5]) , ]> ``` **5)比较查询** - **gt**大于 (greater then) - **gte**大于等于 (greater then equal) - **lt**小于 (less then) - **lte**小于等于 (less then equal) 例:查询编号大于3的图书 ```python BookInfo.objects.filter(id__gt=3) ``` **不等于的运算符,使用exclude()过滤器。** 例:查询编号不等于3的图书 ```python >>> BookInfo.objects.filter(id__gt=3) ]> ``` **6)日期查询** **year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。** 例:查询1980年发表的图书。 ```python >>> BookInfo.objects.filter(pub_date__year=1980) ]> ``` 例:查询1990年1月1日后发表的图书。 ```python >>> BookInfo.objects.filter(pub_date__gt='1990-1-1') ]> ``` ##### F和Q对象 **F对象** 之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。 语法如下: ```py F(属性名) ``` 例:查询阅读量大于等于评论量的图书。 ```py >>> from django.db.models import F >>> BookInfo.objects.filter(readcount__gt=F('commentcount')) ]> ``` 可以在F对象上使用算数运算。 例:查询阅读量大于2倍评论量的图书。 ```py >>> BookInfo.objects.filter(readcount__gt=F('commentcount')*2) ]> ``` **Q对象** **多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。** 例:查询阅读量大于20,并且编号小于3的图书。 ```py >>> BookInfo.objects.filter(readcount__gt=20,id__lt=3) ]> 或者 >>> BookInfo.objects.filter(readcount__gt=20).filter(id__lt=3) ]> ``` **如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符**,Q对象被义在django.db.models中。 语法如下: ```plain Q(属性名__运算符=值) ``` 例:查询阅读量大于20的图书,改写为Q对象如下。 ```py BookInfo.objects.filter(Q(readcount__gt=20)) ``` Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或。 例:查询阅读量大于20,或编号小于3的图书,只能使用Q对象实现 ```py >>> BookInfo.objects.filter(Q(readcount__gt=20)|Q(id__lt=3)) , , ]> ``` Q对象前可以使用~操作符,表示非not。 例:查询编号不等于3的图书。 ```py >>> BookInfo.objects.filter(~Q(id=3)) , , ]> ``` ##### 聚合函数 使用aggregate()过滤器调用聚合函数。聚合函数包括:**Avg**平均,**Count**数量,**Max**最大,**Min**最小,**Sum**求和,被定义在django.db.models中。 例:查询图书的总阅读量。 ```py >>> from django.db.models import Sum >>> BookInfo.objects.aggregate(Sum('readcount')) {'readcount__sum': 126} ``` 注意aggregate的返回值是一个字典类型,格式如下: ```py {'属性名__聚合类小写':值} 如:{'readcount__sum': 126} ``` 使用count时一般不使用aggregate()过滤器。 例:查询图书总数。 ```py BookInfo.objects.count() ``` 注意count函数的返回值是一个数字。 ##### 排序 使用**order_by**对结果进行排序 ```py # 默认升序 >>> BookInfo.objects.all().order_by('readcount') , , , ]> # 降序 >>> BookInfo.objects.all().order_by('-readcount') , , , ]> ``` ##### 级联查询 **关联查询** ```py 查询书籍为1的所有人物信息 查询人物为1的书籍信息 ``` **由一到多的访问语法**: 一对应的模型类对象.多对应的模型类名小写_set 例: ```py >>> book = BookInfo.objects.get(id=1) >>> book.peopleinfo_set.all() , , , , ]> ``` **由多到一的访问语法**: 多对应的模型类对象.多对应的模型类中的关系类属性名 例: ```py person = PeopleInfo.objects.get(id=1) person.book ``` 访问一对应的模型类关联对象的id语法: 多对应的模型类对象.关联类属性_id 例: ```py >>> person = PeopleInfo.objects.get(id=1) >>> person.book_id 1 ``` **关联过滤查询** **由多模型类条件查询一模型类数据**: 语法如下: ```plain 关联模型类名小写__属性名__条件运算符=值 ``` **注意:如果没有"__运算符"部分,表示等于。** ```plain 查询图书,要求图书人物为"郭靖" 查询图书,要求图书中人物的描述包含"八" ``` 例: 查询图书,要求图书人物为"郭靖" ```py >>> book = BookInfo.objects.filter(peopleinfo__name='郭靖') >>> book ]> ``` 查询图书,要求图书中人物的描述包含"八" ```py >>> book = BookInfo.objects.filter(peopleinfo__description__contains='八') >>> book , ]> ``` **由一模型类条件查询多模型类数据**: 语法如下: ```plain 一模型类关联属性名__一模型类属性名__条件运算符=值 ``` **注意:如果没有"__运算符"部分,表示等于。** ```plain 查询书名为“天龙八部”的所有人物 查询图书阅读量大于30的所有人物 ``` 例: 查询书名为“天龙八部”的所有人物。 ```py >>> people = PeopleInfo.objects.filter(book__name='天龙八部') >>> people , , , ]> ``` 查询图书阅读量大于30的所有人物 ```py >>> people = PeopleInfo.objects.filter(book__readcount__gt=30) >>> people , , , , , , , ]> ``` ##### 查询集QuerySet **1 概念** Django的ORM中存在查询集的概念。 查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。 当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表): - all():返回所有数据。 - filter():返回满足条件的数据。 - exclude():返回满足条件之外的数据。 - order_by():对结果进行排序。 对查询集可以再次调用过滤器进行过滤,如 ```py >>> books = BookInfo.objects.filter(readcount__gt=30).order_by('pub_date') >>> books , ]> ``` 也就意味着查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果。 **从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句。** **判断某一个查询集中是否有数据**: - exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。 **2 两大特性** **2.1惰性执行** 创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用 例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集books ```py books = BookInfo.objects.all() ``` 继续执行遍历迭代操作后,才真正的进行了数据库的查询 ```py for book in books: print(book.name) ``` **2.2缓存** 使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。 **情况一**:如下是两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载。 ```py from book.models import BookInfo [book.id for book in BookInfo.objects.all()] [book.id for book in BookInfo.objects.all()] ``` **情况二**:经过存储后,可以重用查询集,第二次使用缓存中的数据。 ```py books=BookInfo.objects.all() [book.id for book in books] [book.id for book in books] ``` **3 限制查询集** 可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句。 注意:不支持负数索引。 **对查询集进行切片后返回一个新的查询集,不会立即执行查询。** 如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()如果没有数据引发DoesNotExist异常。 示例:获取第1、2项,运行查看。 ```py >>> books = BookInfo.objects.all()[0:2] >>> books , ]> ``` **4.分页** [文档](https://docs.djangoproject.com/en/1.11/topics/pagination/) ```py #查询数据 books = BookInfo.objects.all() #导入分页类 from django.core.paginator import Paginator #创建分页实例 paginator=Paginator(books,2) #获取指定页码的数据 page_books = paginator.page(1) #获取分页数据 total_page=paginator.num_pages ``` ## 三、视图 ### 1)HttpRequest对象 - 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取; - 查询字符串(query string),形如key1=value1&key2=value2; - 请求体(body)中发送的数据,比如表单数据、json、xml; - 在http报文的头(header)中。 #### 1.URL路径参数 - 如果想从URL中获取值`http://127.0.0.1:8000/18/188/` - 应用中`urls.py` ```python from django.urls import path from book.views import goods urlpatterns = [ path('//',goods) ] ``` - 视图中函数: 参数的位置不能错 ```python from django.http import JsonResponse def goods(request,cat_id,id): return JsonResponse({'cat_id':cat_id,'id':id}) ``` #### 2.Django中的QueryDict对象 HttpRequest对象的属性GET、POST都是QueryDict类型的对象 与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况 - 方法get():根据键获取值 如果一个键同时拥有多个值将获取最后一个值 如果键不存在则返回None值,可以设置默认值进行后续处理 ```python get('键',默认值) ``` - 方法getlist():根据键获取值,值以列表返回,可以获取指定键的所有值 如果键不存在则返回空列表[],可以设置默认值进行后续处理 ```python getlist('键',默认值) ``` #### 3. 查询字符串Query String 获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2),可以通过request.GET属性获取,返回QueryDict对象。 ```python # /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获取请求中的查询字符串数据。** #### 4 请求体 请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。 可以发送请求体数据的请求方式有**POST**、**PUT**、**PATCH**、**DELETE**。 **Django默认开启了CSRF防护**,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如: ##### 4.1 表单类型 前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。 ```python 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') ``` ##### 4.2 非表单类型 非表单类型的请求体数据,Django无法自动解析,可以通过**request.body**属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。**request.body返回bytes类型。** 例如要获取请求体中的如下JSON数据 ```json {"a": 1, "b": 2} ``` 可以进行如下方法操作: ```python 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') ``` #### 5.验证path中路径参数 ##### 默认转换器 系统为我们提供了一些路由转换器位置在`django.urls.converters.py` ```python DEFAULT_CONVERTERS = { 'int': IntConverter(), # 匹配正整数,包含0 'path': PathConverter(), # 匹配任何非空字符串,包含了路径分隔符 'slug': SlugConverter(), # 匹配字母、数字以及横杠、下划线组成的字符串 'str': StringConverter(), # 匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式 'uuid': UUIDConverter(), # 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00 } ``` 我们可以通过以下形式来验证数据的类型 ```python path('//',goods), ``` ##### 自定义转换器 ##### `http://127.0.0.1:8000/18500001111/`默认的路由转换器中,没有专门用来匹配手机号的路由转换器。所以在使用path()实现需求时,就无法直接使用默认的路由转换器。如果默认的路由转换器无法满足需求时,我们就需要**自定义路由转换器。**在任意可以被导入的python文件中,都可以自定义路由转换器。 - 比如:在工程根目录下,新建`converters.py`文件,用于自定义路由转换器 ```python class MobileConverter: """自定义路由转换器:匹配手机号""" # 匹配手机号码的正则 regex = '1[3-9]\d{9}' def to_python(self, value): # 将匹配结果传递到视图内部时使用 return int(value) def to_url(self, value): # 将匹配结果用于反向解析传值时使用 return str(value) ``` - 注册自定义路由转换器 - - 在总路由中,注册自定义路由转换器 ```python from django.urls import register_converter # 注册自定义路由转换器 # register_converter(自定义路由转换器, '别名') register_converter(MobileConverter, 'mobile') urlpatterns = [] ``` - 使用自定义路由转换器 ```python # 测试path()中自定义路由转换器提取路径参数:手机号 http://127.0.0.1:8000/18500001111/ path('/',register) ``` #### 6.请求头 可以通过**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). 具体使用如: ```python def get_headers(request): print(request.META['CONTENT_TYPE']) return HttpResponse('OK') ``` #### 7.其他常用HttpRequest对象属性 - **method**:一个字符串,表示请求使用的HTTP方法,常用值包括:'GET'、'POST'。 - **user:请求的用户对象。** - path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。 - encoding:一个字符串,表示提交的数据的编码方式。 - - 如果为None则表示使用浏览器的默认设置,一般为utf-8。 - 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。 - FILES:一个类似于字典的对象,包含所有的上传文件。 ### 2)HttpResponse对象 视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。 #### 1.HttpResponse 可以使用**django.http.HttpResponse**来构造响应对象。 ```py HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码) ``` 也可通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码: - content:表示返回的内容。 - status_code:返回的HTTP响应状态码。 响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置: ```py response = HttpResponse() response['itcast'] = 'Python' # 自定义响应头Itcast, 值为Python ``` ```py response = HttpResponse() response['itcast'] = 'Python' # 自定义响应头Itcast, 值为Python ``` 示例: ```py 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 ``` #### 2.HttpResponse子类 Django提供了一系列HttpResponse的子类,可以快速设置状态码 - HttpResponseRedirect 301 - HttpResponsePermanentRedirect 302 - HttpResponseNotModified 304 - HttpResponseBadRequest 400 - HttpResponseNotFound 404 - HttpResponseForbidden 403 - HttpResponseNotAllowed 405 - HttpResponseGone 410 - HttpResponseServerError 500 #### 3.JsonResponse 若要返回json数据,可以使用JsonResponse来构造响应对象,作用: - 帮助我们将数据转换为json字符串 - 设置响应头**Content-Type**为**application/json** ```py from django.http import JsonResponse def response(request): return JsonResponse({'city': 'beijing', 'subject': 'python'}) ``` #### 4.redirect重定向 ```py from django.shortcuts import redirect def response(request): return redirect('/get_header') ``` ### 状态保持 #### 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基于**域名安全**,**不同域名的Cookie是不能互相访问的**,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。 - 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。 ##### 1.1设置Cookie 可以通过**HttpResponse**对象中的**set_cookie**方法来设置cookie。 ```python HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期) ``` - **max_age**单位为秒,默认为**None** 。如果是临时cookie,可将max_age设置为None。 ```python def cookie(request): response = HttpResponse('ok') response.set_cookie('itcast1', 'python1') # 临时cookie response.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小时 return response ``` ##### 1.2读取Cookie 可以通过**HttpResponse**对象的**COOKIES**属性来读取本次请求携带的cookie值。**request.COOKIES为字典类型**。 ```python def cookie(request): cookie1 = request.COOKIES.get('itcast1') print(cookie1) return HttpResponse('OK') ``` ##### 1.3删除Cookie 可以通过**HttpResponse**对象中的delete_cookie方法来删除。 ```python response.delete_cookie('itcast2') ``` #### Session ##### 1.启用Session **Django项目默认启用Session。** 可以在settings.py文件中查看,如图所示 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642129766515-bd434a42-83e9-4636-a195-52805f456466.png) ##### 2.存储方式 在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。 ###### 2.1 数据库 存储在数据库中,如下设置可以写,也可以不写,**这是默认存储方式**。 ```python SESSION_ENGINE='django.contrib.sessions.backends.db' ``` 如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。 存储在数据库中,如下设置可以写,也可以不写,**这是默认存储方式**。 ```python SESSION_ENGINE='django.contrib.sessions.backends.db' ``` 如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642129951852-c3809cd5-6ede-4766-8d15-a7f89594bafb.png) 数据库中的表如图所示 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642129986513-efa4ca1b-3a7a-41e1-a545-97cfbe7cf1b6.png) 表结构如下 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642130030666-72b652de-f66c-4c56-ab3b-6c0a97920ee3.png) 由表结构可知,操作Session包括三个数据:键,值,过期时间。 ###### 2.2 本地缓存 存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。 ```python SESSION_ENGINE='django.contrib.sessions.backends.cache' ``` ###### 2.3 混合存储 优先从本机内存中存取,如果没有则从数据库中存取。 ```python SESSION_ENGINE='django.contrib.sessions.backends.cached_db' ``` ###### 2.4 Redis 在redis中保存session,需要引入第三方扩展,我们可以使用**django-redis**来解决。 1) 安装扩展 ```python pip install django-redis ``` 2)配置 在settings.py文件中做如下设置 ```python 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' ``` ##### 3.Session操作 通过HttpRequest对象的session属性进行会话的读写操作。 **1)以键值对的格式写session。** ```python request.session['键']=值 ``` **2)根据键读取值。** ```python request.session.get('键',默认值) ``` **3)清除所有session,在存储中删除值部分。** ```python request.session.clear() ``` **4)清除session数据,在存储中删除session的整条数据。** ```python request.session.flush() ``` **5)删除session中的指定键及值,在存储中只删除某个键及对应的值。** ```python del request.session['键'] ``` **6)设置session的有效期** ```python request.session.set_expiry(value) ``` - 如果value是一个整数,session将在value秒没有活动后过期。 - 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。 - 如果value为None,那么session有效期将采用系统默认值, **默认为两周**,可以通过在settings.py中设置**SESSION_COOKIE_AGE**来设置全局默认值。 ### 3)类视图 一个视图,是否可以处理多种逻辑,比如get和post请求逻辑。 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642150229301-ed75ccb9-c334-46e2-a11f-0bcbd2152d5a.png) #### 注册视图处理get和post请求 以函数的方式定义的视图称为**函数视图**,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。 ```python def register(request): """处理注册""" # 获取请求方法,判断是GET/POST请求 if request.method == 'GET': # 处理GET请求,返回注册页面 return render(request, 'register.html') else: # 处理POST请求,实现注册逻辑 return HttpResponse('这里实现注册逻辑') ``` #### 类视图使用 在Django中也可以使用类来定义一个视图,称为**类视图**。 使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示 ```python 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()**方法来添加**。![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642150413556-c88b3b4b-f28a-4ad7-bfd4-dd7dee87f6e2.png) #### 类视图原理(了解) ```python @classonlymethod def as_view(cls, **initkwargs): """ Main entry point for a request-response process. """ ...省略代码... def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # 调用dispatch方法,按照不同请求方式调用不同请求方法 return self.dispatch(request, *args, **kwargs) ...省略代码... # 返回真正的函数视图 return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) ``` #### 类视图的多继承重写dispatch ```python class CenterView(View): def get(self,request): return HttpResponse("OK") def post(self,request): return HttpResponse("OK") ``` 使用面向对象多继承的特性。 ```python # LoginRequireMixin为登陆验证,且必须在view前被调用 class CenterView(LoginRequireMixin,View): def get(self,request): return HttpResponse("OK") def post(self,request): return HttpResponse("OK") ``` ### 4)中间件 Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。 我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。 [Django中间件官方文档](https://docs.djangoproject.com/en/1.11/topics/http/middleware/) #### 中间件的定义方法 Django在中间件中预置了六个方法,这六个方法会在不同的阶段自动执行,对输入或输出进行干预。 ##### 1.1 初始化方法: 启动Django程序,初始化中间件时,自动调用一次,用于确定是否启用当前中间件 ```python def __init__(self, get_response=None): pass ``` ##### **1.2 处理请求前的方法:(重要)** 在处理每个请求前,自动调用,返回None或HttpResponse对象 ```python def process_request(self, request): pass ``` ##### 1.3 处理视图前的方法:(重要) **在处理每个视图前,自动调用,返回None或HttpResponse对象** ```python def process_view(self, request, view_func, view_args, view_kwargs): pass ``` ##### 1.4 处理模板响应前的方法: 在处理每个模板响应前,自动调用,返回实现了render方法的响应对象 ```python def process_template_response(self, request, response): pass ``` **1.5 处理响应后的方法:(重要)** **在每个响应返回给客户端之前,自动调用,返回HttpResponse对象** ```python def process_response(self, request, response): pass ``` **1.6 异常处理:** 当视图抛出异常时,自动调用,返回一个HttpResponse对象 ```python def process_exception(self, request,exception): pass ``` #### 定义中间件 ```python # 导入中间件的父类 from django.utils.deprecation import MiddlewareMixin class TestMiddleware1(MiddlewareMixin): """自定义中间件""" def process_request(self, request): """处理请求前自动调用""" print('process_request1 被调用') def process_view(self, request, view_func, view_args, view_kwargs): # 处理视图前自动调用 print('process_view1 被调用') def process_response(self, request, response): """在每个响应返回给客户端之前自动调用""" print('process_response1 被调用') return response # 在setting文件中注册 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.TestMiddleware1', # 添加中间件 ] ``` #### 多个中间件的执行顺序 - 在请求视图被处理**前**,中间件**由上至下**依次执行 - 在请求视图被处理**后**,中间件**由下至上**依次执行 ![img](https://cdn.nlark.com/yuque/0/2022/png/22668255/1642151211164-4491ca50-7307-4ab8-b8a3-57f61ffd0c58.png)