目录:1.关于web框架 2.wsgiref模块 3.自己写一个web框架 4.http协议 5.django
关于web这一部分的内容还有待进一步整理补充
关于web框架:
web应用程序是一种可以通过web访问的应用程序
有两种模式C/S和B/S,一般是B/S模式
Google浏览器检查的时候network里面:
preview是显示浏览器渲染后的预览效果
response是浏览器得到的响应的具体内容,就是代码样式的
web框架是一种开发框架,用来支持动态网站、网络应用和网络服务的开发
wsgiref模块就是python基于wsgi协议开发的服务模块,本质上是一个web框架
1.按照http请求协议解析数据
2.这部分是我们自己写的,上面的跟下面的都用wsgiref模块来是实现
3.按照http响应协议封装数据
实例:
from wsgiref.simple_server import make_server
def application(environ, start_response):
# 按照http协议解析的数据就是environ
# 按照http协议组装的数据的格式就是start_response
path = environ.get('PATH_INFO') # 这个environ是字典格式
print(path)
start_response('200 OK', [('Content-Type', 'text/html')]) # 这里第二个元素这个列表可以为空,但是必须要有这个列
data = b''
if path == '/login':
with open('login.html', 'rb') as f:
data = f.read()
elif path == '/index':
with open('index.html', 'rb') as f:
data = f.read()
return [data] # 这里必须用列表的格式
# 封装socket
http = make_server('127.0.0.1', 8060, application)
# 等待用户连接 conn,addr = sock.accept()
http.serve_forever()
自己写一个web框架:
在pycharm -----> day49-web -----> 03文件夹
自己写的这个03就是一个web框架
1.main.py是主函数入口,启动文件,封装了socket
2.urls.py:路径与视图函数的映射关系------url控制器
3.views.py是整个视图函数部分,固定有一个形参environ-----视图函数
4.图片、html文件等放在templates里面-------模板
5.models.py在项目启动前,在数据库中创建表结构,单独执行的-------与数据库相关
http协议
hyper text transfer protocol 超文本传输协议
用于万维网(world wide web)服务器与本地浏览器之间传输超文本的传送协议
是一个属于应用层的面向对象的协议
特点:
基于TCP/IP协议
基于请求-响应模式
请求是从客户端发出的,服务端响应该请求并返回,也就是说先从客户端建立通信
无状态保存
http协议本身不对请求和响应之间的通信状态进行保存
无连接
限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后就断开连接
http的请求协议与响应协议
http协议包含 由浏览器发送数据到服务器需要遵循的请求协议
和 服务器发送数据到浏览器需要遵循的响应协议
用于http协议交互的信被称为http报文,请求报文和响应报文
请求协议:
请求格式
请求首行:请求方法+URL+协议版本(HTTP/1.1),用\r\n与请求头分割
请求头,有多个,用\r\n分割,一直到遇到\r\n\r\n,这后面的内容就是请求体了
请求体,只有post请求才有请求体
请求方法
get 提交的数据会被放在url之后,以?分割url和传输数据,参数之间用&连接
提交的数据有大小限制
有安全问题
没有请求体
常用于做查询操作的时候
默认的,包括a标签也是get方法
post 把提交的数据放在http包的body中
提交数据没有大小限制
常用于数据库更改的时候
响应协议:
响应格式
响应首行:协议版本(HTTP/1.1)+状态码+OK(状态码的原因短语)
响应头
响应体
响应状态码
1XX Informational(信息性状态码) 接收的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求,服务器发出的,
举个例子就是访问的网站换域名了,访问原来的网址自动跳转到新的
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错
django
是一种web框架
MTV模型的
M代表model模型
T代表Template模板
V代表view视图
一个django project
django-admin startproject mysite
下面包含
manage.py 与django进行交互的
mysite/
__init__.py
settings.py 配置文件
urls.py 控制器/路由
wsgi.py
在mysite目录下创建应用
python manage.py startapp blog
下面包含
blog:
__init__.py
admin.py
apps.py
migrations
__init__.py
models.py
tests.py
views.py
启动django项目
python manage.py runserver 8080
第一个简单实例在新项目django_01里面,settings里面TEMPLATES的DIRS加一句os.path.join(BASE_DIR, 'templates')
静态文件配置在新项目django_02里面,重点在于
先加一个static包,盛放jquery文件,这里static就固定用这个名字,虽然可以改,但是不要改了
再在settings里面加了一个
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
这样在客户端访问static/这个地址的时候才能访问的到
这样配置完了以后写html的时候就可以用jquery文件了,<script src="/static/jquery-3.3.1.js"></script>
然后,控制html的css文件跟js文件也可以单独拿出来放到static里面,同一个功能的可以放到同一个包里
html里面<link rel="stylesheet" href="/static/app01/timer.css">
<script src="/static/app01/timer.js"></script>引入
要注意引入时候的先后顺序
路由层(URLconf) django_03
from django.urls import path,re_path
path是django2版本的语法,re_path是django1版本的语法,2版本两种都支持
URL配置就像django所支撑网站的目录,本质是url与要被该url调用的视图函数之间的映射表
就是通过这个来告诉django,对于客户端发来的某个url调用哪一段逻辑代码对应执行
说白了就是哪一个路径由哪一个视图函数来处理
注意:
若要从URL 中捕获一个值,只需要在它周围放置一对圆括号,括号是分组的意思,会默认传值,写视图函数的时候要多写一个形参,一个括号多写一个
不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的”,字符串中任何字符都不应该转义
有名分组:
使用命名的正则表达式组来捕获URL中的值并以关键字参数传递给视图
(?P<name>pattern) name是组的名称,也就是关键字参数时候关键字key pattern是正则匹配规则
分发:
把app01的功能单独出来,放到app01-->urls里面,就是在app01里面再写一个urls.py
然后在主urls里面先引入include,再像下面这样写
re_path(r"app01/", include("app01.urls")),
登录验证的实例在django_03
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
user = request.POST.get('user') # request.POST request.GET拿到的都是字典格式,必须大写
pwd = request.POST.get('pwd')
if user == 'yuan' and pwd == '123':
return HttpResponse('登录成功') # 必须要有返回
else:
return HttpResponse('用户名或密码错误')
反向解析
在使用django项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中或用于处理服务器端的导航
硬编码或者设计专门的url生成机制会容易产生过期的url
所以在需要url的地方,对于不同层级,django提供不同的工具用于url反查:
在模板中,使用url模板标签
path('login/', views.login, name="Log") # 使用name=给url起别名
<form action="{% url 'Log' %}" method="post"> # 使用反向解析的固定格式,与render有关
在python代码中,使用from django.urls import reverse()函数
也是先在url里面起别名
re_path(r'^articles/2003/$', views.special_case_2003, name="s_c_2003"),
再在views.py里面引入reverse
url = reverse("s_c_2003")
命名url模式时,需要确保使用的名称不会与其他应用中的名称冲突
名称空间namespace
名称空间是表示标识符的可见范围
一个标识符可在多个名称空间中定义,它在不同名称空间中的含义互不相干
由于别名name没有作用域,django在反解url时,会在项目全局顺序搜索,当查到第一个name指定url时就返回
可以在分发的时候设定namespace是“app01”,放在include里面,限定名称空间,格式是include(("app01.urls","app01"))
在使用反解的时候reverse(app01:index)
django2.0版本的path
特有的功能,规则如下:
使用尖括号<>从url中捕获值,捕获到的值字符串格式
捕获值中可以包含一个转化器类型,比如使用<int:name>捕获一个整数变量
如果没有转化器,将匹配任何字符串,当然也包含了/字符
无需添加前导斜杠
django默认支持以下5个path转化器(Path Converters)
str 匹配除了路径分隔符/之外的非空字符串,默认值
int 匹配正整数,包含0
slug 匹配字母、数字、横杠和下划线组成的字符串
uuid 匹配格式化的uuid
path 匹配任何非空字符串,包含路径分隔符,?不行,因为?是路径分隔符
注册自定义转化器
三点要求:
regex 类属性,字符串类型
to_python(self, value)方法,value是由雷属性regex所匹配到的字符串,返回具体的python变量值
以供django传递到对应的视图函数中
to_url(self, value)方法,和to_python相反,value是一个具体的python变量值,返回其字符串,通常用于url反向引用
实例:
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
使用register_converter 将其注册到URL配置中:
from django.urls import register_converter, path
from . import converters, views
register_converter(converters.FourDigitYearConverter, 'yyyy')
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<yyyy:year>/', views.year_archive),
...
]
视图层
在django_04
一个视图函数,简称视图,是一个简单的python函数,它接受web请求并返回web响应
响应可以是任何东西,但是必须有
我们约定俗成的将视图放在项目或应用程序目录中名为views.py的文件中
也约定俗成的将request作为第一个参数传进去
url:协议://IP:port/路径path?请求数据
请求对象HttpRequest
通常称之为request
属性:
request.method 拿到的就是get或post
request.GET 拿到的是字典,可以用get拿数据
request.POST 拿到的是字典,可以用get拿数据
request.path 拿到的是/index/这一部分,只拿路径,不拿数据部分
常用方法:
HttpRequest.get_full_path() 返回path,这个会连同数据一起拿到,而上面的path只拿路径
HttpRequest.is_ajax()
响应对象HttpResponse
主要由三种形式
1.HttpResponse() 括号内直接跟一个具体的字符串作为相应体,可以渲染标签
2.render()
render(request, template_name,[context])
request用于生成响应的请求对象
template_name是要使用的模板的完整名称,就是那个页面html文件
可选参数context是提交到模板上下文中的一个字典,默认是一个空字典,如果字典中的某个值是可调用的,
视图将会在渲染模板之前调用它,模板语法{'ctime':ctime},在html里面{
{ ctime }}
3.redirect()
传递要重定向的一个硬编码的url
return redirect('/some/url')
也可以传递一个完成的url
return redirect('http://example.com')
模板层
在django_05
模板语法只有两个:{
{}} 0.渲染变量1.深度查询,使用句点符,就是用.,一层层都是用点来查
2.过滤器
{%%} 0.渲染标签
在django模板中遍历复杂数据结构的关键是句点字符,语法:{
{var_name}}
return render(request, 'index.html', locals()) # locals()用于局部变量比较多的时候,也是按照键值对字典的方式
对于复杂数据结构,用.来查询
<p>{
{ l.1 }}</p><p>{
{ info.name }}</p><p>{
{ alex.name }}</p><p>{
{ alex.age }}</p><p>{
{ person_list.1.age }}</p>
过滤器
语法:{
{obj|filter_name:param}}
default
{
{value|default:"nothing"}}如果一个变量是false或者为空,显示给定的nothing,否则,显示变量的值
length
{
{value|length}}返回值的长度,它对字符串和列表都起作用
如果value是['a','b','c','d'],那么输出4
filesizeformat
{
{value|filesizeformat}}将值格式化为一个人类可读的文件尺寸
如果value是123456789,输入将会是117.7MB
date
{
{value|data:"Y-m-d"}}如果value=datetime.datetime.now()
slice切片,顾头不顾尾
{
{value|slice:"2:-1"}}如果value="hello world"
truncatechars
truncatewords
{
{value|truncatechars:9}}如果字符串字符多于指定的字符数量,那么将会被截断,截断的字符串将会以可翻译的省略号序列("...")结尾
参数是要截断的字符数,注意后面的三个...会占空
safe
告诉django这段代码不必转义
value="<a href="">点击</a>"
{
{value|safe}}
add
{
{ l.1|add:100 }}加法
标签
看起来是这样的{% tag %}
for标签
遍历每一个元素
{% for i in l %}
<p>{
{ i }}</p>{% endfor %}
遍历每一个字典
{% for i, v in info.items %}
<p>{
{ forloop.counter }} { { i }}:{ { v }}</p> # 使用forloop加序号,作用类似于enumerate,但是从1开始,counter0就从0开始{% endfor %}
for...empty
for标签带有一个可选的{% empty %}从句,以便在给出的组是空或者没找到时可以有所操作
{% for person in person_list %}
<p>{
{ person.name }}</p>
{% empty %}
<p>sorry,no person here</p>
{% endfor %}
if标签
{% if %}会对一个变量求值,如果它的值是True(存在、不为空且不是boolean类型的false值),输出对应的内容
{% if user %}
<p><a href="#">hi{
{ user }}</a></p>{% else %}
<p><a href="#">登录</a></p>
{% endif %}
with标签
使用一个简单的名字缓存一个复杂的变量,当你需要使用一个昂贵的方法(比如访问数据库)很多次时非常有用
{% with person_list2.1.name as n %}
{
{ n }}{
{ n }}{
{ n }}{
{ n }}{% endwith %}
csrf_token
用于跨站请求伪造保护,加在html的form表单里面
<form action="" method="post">
{% csrf_token %}
<input type="text" name="user">
<input type="submit">
</form>
自定义标签和过滤器
1.在settings中INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag
2.在app中创建templatetags模块,模块名只能是templatetags,是一个包
3.创建任意.py文件,如my_tags.py
from django import template
from django.utils.safestring import mark_safe
register = template.Library() #register的名字是固定的,不可改变
@register.filter 自定义的过滤器最多只能传两个值进去
def filter_multi(v1,v2):
return v1 * v2
<br>
@register.simple_tag 自定义标签 没有传参的限制
def simple_tag_multi(v1,v2):
return v1 * v2
<br>
@register.simple_tag
def my_input(id,arg):
result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
return mark_safe(result)
4.在使用自定义simple_tag和filter的html文件中导入之前创建的my_tags.py
{% load my_tags %}
5.使用simple_tag和filter,filter可以用在if等语句之后,simple_tag不可以
{% if i|multi_filter:20 > 300 %}
<p> >300 </p>
{% else %}
<p> {
{ i|multi_filter:20 }} </p>{% endif %}
继承
django_05的base.html,include也在里面,include是把部分内容单独拿出来,用include引入
{% include 'advertise.html' %}
base是一个html模板,在其他html中用{% extends 'base.html' %}继承,这一句必须写在首行
这样新的html就拥有相同的样式,
然后再base中放空盒子用来给新html添加内容,空盒子如下:
{% block con %}
{% endblock %}
盒子可以放多个,con可以随便写,title也可以这样写,block盒子越多越好
endblock也可以起名字,这样更可读
在新html中,把要添加的内容放到盒子中间
模型层
ORM是对象-关系-映射的简称,实现了数据模型与数据库的解耦
即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库
只能对表进行操作,不能对数据库进行操作
单表操作django_06
创建表
1.在app01的models.py里面,固定格式如下:
from django.db import models
# Create your models here.
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
pub_date = models.DateField()
price = models.DecimalField(max_digits=8, decimal_places=2) # 这个是最大是八个数,有两位是小数
publish = models.CharField(max_length=32)
2.配置settings
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'bms', # 要连接的数据库,连接前需要创建好
'USER':'root', # 连接数据库的用户名
'PASSWORD':'', # 连接数据库的密码
'HOST':'127.0.0.1', # 连接主机,默认本级
'PORT':3306 # 端口 默认3306
}
}
3.在项目名称下的__init__.py里面加上,而不是app的__init__.py里面
import pymysql
pymysql.install_as_MySQLdb()
上面这两步2 3是为了在本机生成一个表,如果不进行上面这两步,那么就在项目中生成mysql表
4.还要确保配置文件settings中的INSTALLED_APPS中写入了app的名称
5.最后通过两条数据库迁移命令即可在指定的数据库中创建表 :
python manage.py makemigrations
python manage.py migrate
1 4 5必写,2 3是为了在本地生成表
添加表记录
# 添加表记录
# 方式一
book_obj = Book(id=1, title='python红宝书', price=100, pub_date='2012-12-12', publish='人民出版社')
book_obj.save()
# 方式二,返回值是当前生成的记录的对象,可以操作对象取值,推荐
book_obj2 = Book.objects.create(title='php', price=100, pub_date='2013-12-12', publish='人民出版社')
查询表记录
<1> all(): 查询所有结果
book_list = Book.objects.all()
print(book_list) # 返回QuerySet数据类型,django独有的,有点像列表,可以进行for循环、索引、切片等
for i in book_list:
print(i.title, i.price)
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,条件可以加多个,用,分割
# filter() 调用者是objects管理器,返回值是queryset对象,该对象可以使用first last方法
book_list = Book.objects.filter(price=100)
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,
如果符合筛选条件的对象超过一个或者没有都会抛出错误。
book_obj = Book.objects.get(title='php')
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象,排除
调用者是objects管理器,返回值是queryset对象,该对象可以使用first last方法
book_obj = Book.objects.exclude(title='go')
<5> order_by(*field): 对查询结果排序
调用者是queryset对象,返回值是queryset对象
ret = Book.objects.all().order_by('id')
按照id升序排列,加一个-就是'-id'就是降序排列
<6> reverse(): 对查询结果反向排序
<8> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
ret = Book.objects.all().count()
调用者是queryset对象,返回值是int类型
<9> first(): 返回第一条记录
<10> last(): 返回最后一条记录
# first() last() 调用者是queryset对象,返回值是model对象
theFirst = Book.objects.all().first()
theLast = Book.objects.all().last()
<11> exists(): 如果QuerySet包含数据,就返回True,否则返回False
ret = Book.objects.all().exists() 只要有就行
调用者是queryset对象,返回值是布尔值
<12> values(*field): 返回一个ValueQuerySet 一个特殊的QuerySet,运行后得到的并不是一系列
model的实例化对象,而是一个可迭代的字典序列
调用者是queryset对象
ret = Book.objects.all().values('price')
取值是ret[0].get('price')
<13> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
调用者是queryset对象
ret = Book.objects.all().values_list('price')
<14> distinct(): 从返回结果中剔除重复纪录,配合value或value_list使用
ret = Book.objects.all().values('price').distinct()
调用者是queryset对象,返回值看是value还是value_list
基于双下划线的模糊查询
Book.objects.filter(price__in=[100,200,300])
Book.objects.filter(price__gt=100) # 大于
Book.objects.filter(price__lt=100) # 小于
Book.objects.filter(price__range=[100,200])
Book.objects.filter(title__contains="python") # 有python就行,在哪不重要,区分大小写
Book.objects.filter(title__icontains="python") # 有python就行,在哪不重要,不区分大小写
Book.objects.filter(title__startswith="py") 以py开头的
Book.objects.filter(pub_date__year=2012)
删除表记录,通用于单表和多表
删除方法就是 delete()。它运行时立即删除对象,返回任何值是删除的记录数。可以一次删除多个对象
调用者是queryset对象,model对象也可以调用delete
修改表记录,通用于单表和多表
Book.objects.filter(title__startswith="py").update(price=120),返回值是修改的记录数
调用者只能是queryset对象
练习:
1 查询老男孩出版社出版过的价格大于200的书籍
Book.objects.filter(publish='老男孩出版社', price__gt=200)
2 查询2017年8月出版的所有以py开头的书籍名称
Book.objects.filter(title__startswith='py', pub_date__year=2017, pub_date__month=8).values('title')
3 查询价格为50,100或者150的所有书籍名称及其出版社名称
Book.objects.filter(price__in=[50,100,150]).values('title', 'publish')
4 查询价格在100到200之间的所有书籍名称及其价格
Book.objects.filter(price__range=[100,200]).values('title', 'price')
5 查询所有人民出版社出版的书籍的价格(从高到低排序,去重)
Book.objects.filter(publish='人民出版社').values('price').distinct().order_by('-price')
图书管理系统的作业
实现功能:book单表的增删改查,写在django_07
多表操作django_08
一对多
一旦确定表关系是一对多,创建关联字段,关联字段必须在多的表中
多对多
一旦确定表关系是多对多,创建第三张关系表,三个字段,id和另外两张表的关联字段
一对一
其实就是把本来的一张表,其中几个字段拿出来单独写一张表,就变成一对一的关系了
在两张表的任意一张表中建立关联字段加上unique
建关联字段是为了进行查询,加外键约束条件是为了保护数据安全,避免有关联的内容被删除导致其他数据查不到
创建表
实例:用ORM生成关联的表模型
from django.db import models
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday=models.DateField()
telephone=models.BigIntegerField()
addr=models.CharField( max_length=64)
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
email=models.EmailField()
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方,生成字段
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
注意:
表的名字是自动生成的
id字段是自动添加的
对于外键字段,django会在字段名上添加_id来创建数据库表中的列名
一对一和一对多一定要加上on_delete=models.CASCADE,只要是django2.0版本
外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None
添加表记录
一对多:
方式1:
publish_obj=Publish.objects.get(nid=1)
book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj)
方式2:
book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1)
book_obj.publish是与这本书关联的出版社对象
book_obj.publish_id是与这本书关联的出版社的id
多对多:
# 当前生成的书籍对象
book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)
# 为书籍绑定的做作者对象
yuan=Author.objects.filter(name="yuan").first() # 在Author表中主键为2的纪录
egon=Author.objects.filter(name="alex").first() # 在Author表中主键为1的纪录
# 绑定多对多关系,即向关系表book_authors中添加纪录
book_obj.authors.add(yuan,egon) #将某些特定的 model 对象添加到被关联对象集合中 book_obj.authors.add(*[])
可以直接放1,2
可以放*[1,2]
多对多关系其它常用API:
book_obj.authors.remove() #将某个特定的对象从被关联对象集合中去除。book_obj.authors.remove(*[])
book_obj.authors.clear() #清空被关联对象集合
book_obj.authors.set() #先清空再设置
book.authors.all() 是于这本书关联的所有作者对象的集合
跨表查询:
1.基于对象的查询
2.基于双下划线的查询
3.聚合与分组查询
4.F查询与Q查询
基于对象的跨表查询,就是mysql里的子查询
关联属性在A表中,通过A找B就是正向查询,通过B找A就是反向查询
实例:
一对多:正向查询按字段查,反向查询按表名查,表名小写_set
# 查询麦田这本书出版社的名字,一对多的正向查询,按字段
book_obj = Book.objects.filter(title='麦田').first()
print(book_obj.publish.name)
# 查询人民出版社出版的书籍名称,一对多的反向查询,按表名,小写表名_set
publish = Publish.objects.filter(name='人民出版社').first()
book_list = publish.book_set.all()
print(book_list)
多对多:正向查询按字段查,反向查询按表名查,表名小写_set
# 查询麦田的所有作者名称,多对多的正向查询
book_obj = Book.objects.filter(title='麦田').first()
author_list = book_obj.authors.all()
for author in author_list:
print(author.name)
# 查询alex出版过的所有书籍名称,多对多的反向查询
alex = Author.objects.filter(name='alex').first()
book_list = alex.book_set.all()
for book in book_list:
print(book.title)
一对一:正向查询按字段,反向查询按表名
# 查询alex的手机号,一对一的正向查询
alex = Author.objects.filter(name='alex').first()
print(alex.authorDetail.telephone)
# 查询手机号为110的作者的名字跟年龄,一对一的反向查询
ad = AuthorDetail.objects.filter(telephone=110).first()
print(ad.author.name)
print(ad.author.age)
基于双下划线的跨表查询,就是mysql里的join,相对用的较多
正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表
实例:
# 一对多
# 一对多查询的正向查询:查询麦田这本书出版社的名字
# ret = Book.objects.filter(title='麦田').values('publish__name')
# print(ret)
# 一对多的反向查询:查询麦田这本书出版社的名字
# ret = Publish.objects.filter(book__title='麦田').values('name')
# print(ret)
# 多对多
# 多对多的正向查询:查询麦田的所有作者名称
# ret = Book.objects.filter(title='麦田').values('authors__name')
# print(ret)
# 多对多的反向查询:查询麦田的所有作者名称
# ret = Author.objects.filter(book__title='麦田').values('name')
# print(ret)
# 一对一
# 一对一的正向查询:查询alex的手机号
# ret = Author.objects.filter(name='alex').values('authorDetail__telephone')
# print(ret)
# 一对一的反向查询:查询alex的手机号
ret = AuthorDetail.objects.filter(author__name='alex').values('telephone')
print(ret)
# 进阶练习 连续跨表查询
# 查询手机号以110开头的作者出版过的所有书籍名称以及书籍出版社的名称
# 方式一:
# ret = Book.objects.filter(authors__authordetail__telephone__startswith=110).values('title', 'publish__name')
# print(ret)
# 方式二:
ret = Author.objects.filter(authorDetail__telephone__startswith=110).values('book__title', 'book__publish__name')
print(ret)
聚合查询
# 聚合查询 aggregate返回值是一个字典,而不是queryset格式的数据,键的名称可以自定义
# 可以放多个参数 ,默认是price__avg
# 查询所有书籍的平均价格
from django.db.models import Avg, Max, Min, Count
ret = Book.objects.all().aggregate(Avg('price'))
print(ret)
分组查询
单表分组查询
单表模型.objects.values('group by的字段').annotate(聚合函数(‘统计字段))
annotate函数的返回值是queryset数据类型
# 查询每一个部门的名称以及员工的平均薪水
from django.db.models import Avg, Max, Min, Count
ret = Emp.objects.values('dep').annotate(Avg('salary'))
print(ret)
Emp.objects.all()翻译成sql语句就是select * from Emp
多表/跨表分组查询
每一个后的表模型.objects.values('主键').annotate(聚合函数(关联表__统计字段))
每一个后的表模型.objects.annotate(聚合函数(关联表__统计字段)),不加values的话会默认有一个all()
最好用主键分组,下面的例子有的不是用主键分组的,不太好
# 查询每一个出版社名称及出版的书籍的个数
from django.db.models import Avg, Max, Min, Count
ret = Publish.objects.values('name').annotate(c=Count('book__title'))
print(ret)
# 查询每一个作者的名字以及出版过的书籍的最高价格
from django.db.models import Avg, Max, Min, Count
ret = Author.objects.values('name').annotate(m=Max('book__price'))
print(ret)
# 查询每一个书籍的名称以及对应的作者个数
from django.db.models import Avg, Max, Min, Count
ret = Book.objects.values('title').annotate(Count('authors__name'))
print(ret)
# 统计每一本以‘红’开头的书籍的作者个数
from django.db.models import Avg, Max, Min, Count
# ret = Book.objects.filter(title__startswith='红').values('pk').annotate(Count('authors__name'))
# print(ret)
# 统计不止一个作者的书籍
ret = Book.objects.values('pk').annotate(c=Count('authors__name')).filter(c__gt=1).values('title', 'c')
print(ret)
F查询与Q查询
F查询:
# 查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))
#将每一本书的价格提高30元:
Book.objects.all().update(price=F("price")+30)
# Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)
Q查询:
from django.db.models import Q
Q对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询
bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
查询函数可以混合使用Q 对象和关键字参数,但是,Q对象,它必须位于所有关键字参数的前面
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
title__icontains="python")
基于多表的图书管理在django_09
像服务器发送请求的途径:
1.浏览器的地址栏,默认是get请求
2.form表单:
get请求
post请求
3.a标签,默认get请求
1.2.3都是同步请求
4.ajax:
特点:
1.异步请求
2.局部刷新
请求方式:
get请求
post请求
Ajax django_10
AJAX(Asynchronous Javascript And XML)即异步Javascript和XML
使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)
特点:
1.异步请求
2.局部刷新
同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
登录验证的时候用到了json
在html中
var ret =JSON.parse(data); // js的反序列化json字符串方法
if (ret.user){
location.href = 'http://www.baidu.com' // 跳转页面
}else{
$('.error').html(ret.msg).css({'color':'red', 'margin-left':'10px'})
}
ContentType请求头
指定请求体的编码类型
1.application/x-www-form-urlencoded,只要是post类型,无论是原生form还是ajax的form都默认是这种类型
2.multipart/form-data,使用表单上传文件时,必须设置enctype=multipart/form-data
3.application/json 用来告诉服务端消息主体是序列化后的 JSON 字符串
基于form表单上传文件
基于ajax上传文件
都在file_put里面
分页器 在django_11
from django.core.paginator import Paginator
forms组件 在django_12
如果所有的字段都校验成功的话,那么form.cleaned_data里面有所有的校验成功的键值对,是一个字典
如果传进来的值有检验函数未定义的部分,那么没有影响,只要规定的部分正确就行
如果传进来的值缺少校验函数定义的部分,那么就当做空值,那就是false,即可多不可少
form.errors也是一个字典,里面是匹配错误的,键是匹配的键,值是错误信息
form表单的name属性值一定要与forms组件字段名称保持一致才能进行校验
forms组件的渲染标签功能
forms组件渲染错误信息<form action="" method="post" novalidate>
forms组件参数设置
class UserForm(forms.Form):
name = forms.CharField(min_length=4, label='用户名', error_messages={'required': '该字段不能为空'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
pwd = forms.CharField(min_length=4, label='密码',
widget=widgets.PasswordInput(attrs={'class': 'form-control'}), # 设置成密文
error_messages={'required': '该字段不能为空'})
r_pwd = forms.CharField(min_length=4, label='确认密码', error_messages={'required': '该字段不能为空'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
email = forms.EmailField(label='邮箱', error_messages={'required': '该字段不能为空','invalid': '格式错误'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
tel = forms.CharField(label='手机号', error_messages={'required': '该字段不能为空'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
会话跟踪技术 django_13
cookie
具体一个浏览器针对一个服务器存储键值对
一般默认有效时间是两周
Cookie大小上限为4KB;
一个服务器最多在客户端浏览器上保存20个Cookie;
一个浏览器最多保存300个Cookie;
response.set_cookie(key,value) 设置
request.COOKIE.get(key) 读取
path='/', 设置Cookie生效的路径,不设置的话任何路径都能拿到cookie,设置后只有这个路径可以
删除cookie:
response.delete_cookie("cookie_key",path="/",domain=name)
在浏览器内ctrl+shift+delete就能清除cookie
session
流程:
1.浏览器向服务器发送第一次请求
2.服务器设置session,生成随机字符串,并将随机字符串与设置的session对应,并将随机字符串作为cookie返回给浏览器
3.浏览器携带cookie向服务器发送第二次请求,服务器根据cookie中的随机字符串去找到对应的session值
1.生成随机字符串作为session-key
2.设置cookie
3.在django_session表中创建一条记录,以session-key随机字符串为键,以数据的字典格式为值
语法:
1、设置Sessions值
request.session['session_name'] ="admin"
2、获取Sessions值
session_name = request.session["session_name"]
3、删除Sessions值
del request.session["session_name"]
4、flush()
request.session.flush()
删除当前的会话数据并删除会话的Cookie。
这用于确保前面的会话数据不可以再次被用户的浏览器访问
session的数据,然后之前有该浏览器的数据,那就是会更新,如果没有那就创建
session的配置
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
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,默认修改之后才保存(默认)
用户认证组件 django_14
逻辑更严谨
用户认证推荐用这套组件,功能是用session记录登录验证状态
使用前提:要有一张用户表,是django自带的auth-user表
创建超级用户:
auth模块
from django.contrib import auth
auth模块三个常用方法:
authenticate()
用户认证,一般需要username password两个关键字参数
如果认证消息有效,会返回一个user对象
login()
接收request 和 一个认证了的user对象两个参数
返回一个request.user对象,给已通过认证的用户附上session id等信息
logout()
注销用户
接收一个request参数,无返回值
调用该函数,当前请求的全部session信息会全部被清除
user对象
属性:username password,password是用哈希算法保存到auth-user表中的
方法:
is_authenticated
用于检查用户是否通过了认证,如果是真正的user对象,返回值为True
request.user.is_authenticated
from django.contrib.auth.decorators import login_required
这是django设计好的一个装饰器,用于处理一种情况:
用户登录后才能访问某些页面
如果没有登录就访问该页面的话会跳转到登陆页面
在登录页面中登录后再在动跳转到之前要访问的地址
若用户没有登录,则会跳转到django默认的登录URL '/accounts/login/ '
(这个值可以在settings文件中通过LOGIN_URL进行修改)。
并传递当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。
创建用户
使用create_user这个辅助函数来创建用户
from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='',email='')
check_password()
用户要修改密码的时候,让用户先输入原来的密码,通过检查后返回True
修改密码set_password()
user = User.objects.get(username='')
user.set_password(password='')
user.save
匿名用户对象
id永远为None
username永远为空字符串
get_username() 永远返回空字符串
is_staff 和 is_superuser永远为false
is_active 永远为false
groups和user_permissions永远为空
is_anonymous()返回True而不是False
is_authenticated() 返回False而不是True
set_password() check_password() save() 和delete() 引发NotImplementedError
总结
如果未登陆 request.user就是AnonymousUser
如果登陆了就是登陆对象
request.user是一个全局变量
中间件
浏览器发请求给服务器,先到wsgiref,wsgiref处理成request发送到中间件,七个中间件依次处理process_request
然后交给路由控制,分发到对应的视图函数处理,可能就要去与数据库和模板进行交互了,拿到处理结果交给中间件,交给process_response处理
处理完了交给wsgiref进行数据封装,最后把封装好的数据交给浏览器,浏览器渲染显示在页面上
介于request与response之间的一道处理过程,可以在全局上改变django的输入输出
中间件的四个方法:
process_request,不能有返回值
process_view可以用来调用视图函数,process_request--->路由控制--->process_view--->视图函数
如果有返回值,会越过其他的process_view以及视图函数,但是所有的process_response都还会执行。
process_exception 如果视图函数出错,这个方法才会执行,也是像response一样倒着执行
process_response,必须有返回值
应用案例
做IP访问频率限制
URL访问过滤