Django
12.1web框架
1.web框架的本质及自定义web框架
- web应用本质上就是一个socket服务端,而用户的浏览器就是sockcet客户端,基于请求做出响应,客户都是先请求,服务端做出相应的相应,按照http协议的请求协议发出请求,服务端按照http响应请求,这样的网络通信,我们就可以自己实现Web框架了
2.MVC和MTV框架
- Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器©和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:
-
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
- M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
- T 代表模板 (Template):负责如何把页面展示给用户(html)。
- V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:
12.2Django下载安装
1.下载Django:
pip3 install django==1.11.9
2.创建一个django文件
django-admin startproject mysite 创建了一个名为"mysite"的Django 项目
- 当前目录下会生成mysite的工程,目录结构如下:(大家注意昂,pip下载下来的django你就理解成一个模块,而不是django项目,这个模块可以帮我们创建django项目)
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库,启动关闭项目与项目交互等,不管你将框架分了几个文件,必然有一个启动文件,其实他们本身就是一个文件。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。路由分发
- wsgi.py ---- runserver命令就使用wsgiref模块做简单的web server,后面会看到renserver命令,所有与socket相关的内容都在这个文件里面了
3.在mysite目录下创建应用
python manage.py startapp blog
#通过执行manage.py文件来创建应用,执行这句话一定要注意,你应该在这个manage.py的文件所在目录下执行这句话,
因为其他目录里面没有这个文件
- models.py :之前我们写的那个名为model的文件就是创建表用的,这个文件就是存放与该app(应用)相关的表结构的
- views.py :存放与该app相关的视图函数的
4.启动django项目
python manage.py runserver 8080
# python manage.py runserver 127.0.0.1:8080,本机就不用写ip地址了
如果连端口都没写,默认是本机的8000端口
5.通过pycharm创建django项目
12.3HTTP协议
1.简介
-
超文本传输协议,是基于TCP/IP协议之上的应用层协议,是的一个客户端终端(用户)和服务器端(网站)请求和应答的标准。
例如:在浏览器地址栏键入URL,按下回车之后会经历以下流程: 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址; 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接; 浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求, 该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器; 服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器; 释放 TCP连接; 浏览器将该 html 文本并显示内容;
-
无状态:使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产 生。协议本身并不保留之前一切的请求或响应报文的信息。
-
无连接:限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。
2.请求方法
-
GET:向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
-
POST:向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
-
HEAD:与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。
-
PUT:向指定资源位置上传其最新内容。
-
DELETE:请求服务器删除Request-URI所标识的资源
-
TARCE:回显服务器收到的请求,主要用于测试或诊断。
-
OPTIONS:这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用’*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
-
CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
注意事项:
- 方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed),当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。
- HTTP服务器至少应该实现GET和HEAD方法,其他方法都是可选的。当然,所有的方法支持的实现都应当匹配下述的方法各自的语义定义。此外,除了上述方法,特定的HTTP服务器还能够扩展自定义的方法。例如PATCH(由 RFC 5789 指定的方法)用于将局部修改应用到资源*.
3.HTTP状态码
- 1xx :请求已被服务器接收,继续处理
- 2xx:请求成功
- 3xx:重定向,需要猴戏操作从才能完成这一请求
- 4xx:请求语法错误或无法被执行
- 5xx:服务器错误
4.http请求和响应格式
- 请求协议
- 响应协议
12.4 url路由系统
1.url 配置
- 基本格式
from django.conf.urls import url
#循环urlpatterns,找到对应的函数执行,匹配上一个路径就找到对应的函数执行,就不再往下循环了,并给函数传一个参数request,和wsgiref的environ类似,就是请求信息的所有内容
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
#Django 2.0版本中的路由系统已经替换成下面的写法,但是django2.0是向下兼容1.x版本的语法的(官方文档):
from django.urls import path
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
'''
参数说明
正则表达式:一个正则表达式字符串
views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
参数:可选的要传递给视图函数的默认参数(字典形式)
别名:一个可选的name参数
'''
2.分组命名匹配
- 有名分组
from app01 import views
# url 文件
urlpatterns = [
url(r'^admin/', admin.site.urls),
url('^book/(?P<year>\d+)/(?P<month>\d+)', views.book)
]
# views 文件
def book(request,month,year):
request.GET
request.POST
request.method
request.path
request.path_info
request.get_full_path
request.body
# return HttpResponse('xxx') 返回字符串
# return render(request,'book.html') 返回html文件
# return redirect('/index/') 重订项
ret = HttpResponse('xxx')
ret['xx'] = '00'
ret.status_code = 201
return ret #返回新的状态参数
-
无名分组
url('^book/(\d)/(\d+)',views.book) # 位置传参 def book(request, n,m): request.GET request.POST request.method request.path request.path_info request.get_full_path() request.body return HTTPResponse('xxx') return render(request, 'book.html',{'title':'xx'}) rerturn redirect('/index/')
3.url分发命名空间
from django.conf.urls import url, include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# url(r'^index/', views.index,name='index'),
url(r'^app01/', include('app01.urls',namespace='app01')),
url(r'^app02/', include('app02.urls',namespace='app02')),
#/app01/index/
]
app01/urls.py文件内容
from app01 import views
urlpatterns = [
# /index/
url(r'^index/', views.index, name='index'),
url(r'^book/list/', views.book_list, name='book_list'),
url(r'^book/add/', views.book_add, name='book_add'),
url(r'^book/edit/(\d+)/', views.book_edit, name='book_edit'), # /book/edit/2/
url(r'^book/del/(?P<n>\d+)/', views.book_del, name='book_del'), # /book/edit/2/
]
app02/urls.py 文件内容
from app02 import views
urlpatterns = [
url(r'^index/', views.index, name='index'),
]
app01/views.py 文件内容
def index(request):
print(reverse('app01:index'))
# return HttpResponse('app01的路径:' + reverse('index'))
return HttpResponse('app01的路径:' + reverse('app01:index'))
app02/views.py
def index(requset):
print(reverse('app02:index'))
return HttpResponse('app02的路径: ' + reverse('app02:index'))
4.urls.py 的别名和反向解析
from django.conf.urls import url
from django.contrib import admin
from app01 import views
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^book/list/', views.book_list, name='book_list'),#为路径名称命名
url(r'^book/add/', views.book_add,name='book_add'),
url(r'^book/edit/(\d+)/', views.book_edit, name='book_edit'), # /book/edit/2/
url(r'^book/del/(?P<n>\d+)/', views.book_del,name='book_del'), # /book/edit/2/
]
视图部分
from django.urls import reverse
print(reverse('book_list')) #/book/list/
print(reverse('book_edit',args=(2,))) #/book/edit/1/ url的无名分组参数
print(reverse('book_edit',kwargs={'n': 1,})) #/book/edit/1/ 有名分组参数
return redirect('book_list')
return redirect(reverse('book_list'))
模板部分
#模板渲染的时候,被django解析成了这个名字对应的那个url,这个过程叫做反向解析
#无参数的
<a class="btn btn-primary" href="{% url 'book_add' %}">添加书籍</a>
# 无名分组参数的
<a href="{% url 'book_edit' books.id 3 4 %}" class="btn btn-warning">编辑</a>
# 有名分组
<a href="{% url 'book_del' books.id %}" class="btn btn-danger">删除</a>
<a href="{% url 'book_del' n=books.id %}" class="btn btn-danger">删除</a>
12.4Django中的视图函数
1.视图函数view
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中。
2.CBV和FBV
-
FBV:就是在视图里使用函数处理请求。只使用函数定义
-
CBV:就是在视图里使用类处理请求。
from ajang.views import View
#类中封装了与http方法相同的类方法
class Book(View):
def get(self,request):
pass
def post(self.request):
pass
#url写法
url('^book/(\d)/(\d+)',Views.Book.as-view())
url.py
url('^book/(\d)/(\d+)',Views.Book.as-view())
views.py
from django.shortcuts import render, HttpResponse
from django.views import View
# Create your views here.
class Book(View):
def get(self,request, year, month):
return HttpResponse(year+ month + '书籍')
def post(self,request,year, month):
pass
- dispatch 方法
# 重点: 反射,将请求方法分发到同名的类方法去处理
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方法,在请求方法分发给对应的类方法执行前后,进行修饰
from django.shortcuts import render, HttpResponse
from django.views import View
# Create your views here.
# request.method
class Book(View):
def dispatch(self, request, *args, **kwargs):
print('客官,来玩呀')
ret = super().dispatch( request, *args, **kwargs)
print('客官,常来呀,真快')
return ret
def get(self,request, year, month):
print('客官,你要快点')
return HttpResponse(year+ month + '书籍')
def post(self,request,year, month):
pass
3.request对象
- path_info: 返回用户访问url,不包括域名
- method:请求中使用的HTTP方法的字符串,全是大写
- GET:包含所有HTTP GET参数的类字典对象
- POST:包含所有HTTP POST参数的类字典对象
- body: 请求体,byte类型 request.POST的数据就是从body里面提取的
4.reponse对象
- HttpResponse.content:响应内容
- HttpResponse.charset:响应内容的编码
- HttpResponse.status_code:响应的状态码
12.5模板渲染
1.语法
- 模板渲染中的特殊符号: {{ }} {% %}
# views文件
from django.shortcuts import render, HttpResponse
from django.views import View
class Book(View):
def dispatch(self, request, *args, **kwargs):
print('123')
ret =super().dispatch(request, *args, **kwargs)
print('346')
return ret
def get(self,request,year,month):
print('789')
return HttpResponse(year + month + '书籍')
def home(request):
username = 'wf'
num =100
li = [1,23,4]
dic = {'2':2,'3':3,'4':4}
class A:
def __init__(self):
self.user = 'eee'
def get_name(self):
return self.user
a = A()
d1 ={'username':username,
'num':num,
'li':li,
'dic':dic,
'a':a,}
return render(request,'book.html',d1)
book.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{# <link href="bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">#}
<title>Title</title>
<link rel="icon" href="">
</head>
<body>
<h1 style="color: gold">{{ username }}就巨强</h1>
<p>{{ num }}</p>
<p>{{ li.1 }}</p>
<p>{{ dic.2 }}</p>
<p>{{ a.get_name }}</p>
</body>
{#<script src="jquery.js"></script>#}
{#<script src="bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>#}
</html>
2.过滤器
基本语法
{{value|filter_name:参数}}
注意:管道符两侧不能有空格,有参数冒号后面加参数,无参不需要加冒号
# default 默认值
<p>
{{ username|default:'nothing' }}
</p>
# length 长度(字符串或列表)
<p>
{{ username|length }}
</p>
# fliesizeformat 战术可读的大小,自动计算单位
<p>
{{ flie_size|fliesizeformat }}
</p>
# slice 切片
<p>
{{ li|slice:'0:4'}}
</p>
# date 日期格式化
<p>
{{ current_time|date:"Y-m-d H:i:s" }}
</p>
# safe过滤器,将标签字符串识别为一个标签效果
value = "<a href='#'>点我</a>"
<p>value
{{ value|safe }}
</p>
# truncatechars 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
<p>
{{ msg|truncatechars:7 }}
</p>
# truncatewords 在一定数量的字后截断字符串,是截多少个单词。
<p>
{{ msg|truncatewords:2 }}
</p>
# cut 移除所有的相同字符
<p>
{{ msg|cut:' ' }}
</p>
# join 字符串的拼接
<p>
{{ l1|join:'+' }}
</p>
views.py
def xx(request):
a_tag ='<a herf="">神奇的网站</a>'
return render(request,'xx.html',{'a-tag':a_tag})
3.标签
-
语法:{% 标签逻辑 %}
-
for循环标签
/*循环列表*/
<ul>
{% for i in l1 %}
<li>{{ i }}</li>
{% empty %}
<span>啥也没有</span>
{% endfor %}
{#如果l1为空,或者后台没有给l1数据,那么就展示empty下面的内容#}
</ul>
<ul>
{% for key,v in dic.items %}
<li>{{ key }}--{{ v }}</li>
{% endfor %}
</ul>
{#循环嵌套#}
<ul>
{% for i in dic %}
<li>{{ i }}--{{ forloop.counter }}</li>
{% if forloop.last %}
{% for ii in i %}
<li>{{ ii }}--{{ forloop.counter }}--{{ forloop.parentloop.counter }}</li>
{% endfor %}
{% endif %}
{% endfor %}
</ul>
- if 判断标签
- if语句支持and or == < > != <= >= in not in is is not 判断,注意条件两边都有空格
{% if forloop.last %}
{% endif %}
多条件判断
{% if number == 100 %}
<h1>你好</h1>
{% elif number == 101 %}
<h1>我好</h1>
{% else %}
<h1>他也好</h1>
{% endif %}
和过滤可以配合使用
forloop.counter 当循环的索引值(从1开始),forloop是循环的索引值(从1开始).forloop循环器,通过点来使用功能
forloop.conter0 当循环的索引值(从0开始)
forloop.revcounter 当前循环的倒叙索引值(从1开始)
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(布尔值)
forloop.last 当前循环是不是最后一次循环(布尔值)
forlooop.parentloop 本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数
views.py
def index(requst):
l1=[1,2,3]
d1 = {'k1': 'v1', 'k2': 'v2'}
return render(request, 'index.html', {'l1': l1, 'd1': d1})
4.组件
index.html
{% include 'zujian.html' %}
zujian.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="color:red;">这是导航栏</h1>
</body>
</html>
5.静态文件配置
- 带项目根目录下创建的文件夹,比如名称为count
settings.py文件中写上如下内容
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'count')
]
视图文件中使用
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{# <style>#}
{# .c1{#}
{# height: 200px;#}
{# width: 200px;#}
{# background-color: red;#}
{# }#}
{# </style>#}
两种引入方式:
{# <link rel="stylesheet" href="/abc/css/xx.css">#}
<link rel="stylesheet" href="{% static 'css/xx.css' %}">
</head>
<body>
<div class="c1"></div>
<h1>某某大学校花,如下:</h1>
<img src="/abc/imgs/1.png" alt="">
<div>联系方式: 01284308</div>
<div>价位: 一瓶红牛</div>
</body>
<script src="/abc/js/xx.js"></script>
</html>
views.py
def person(request):
return render(request,'person.html')
12.6模型层单表操作
1.ORM(object relational mapping)
- ORM是“对象-关系-映射”的简称。通过简单的配置就可以轻松更换数据库.
app01/models.py
class Book(models.Model):
# id = models.AutoField(primary_key=True) # id int primary key auto_increment,
title = models.CharField(max_length=32) # title varchar(32)
price = models.DecimalField(max_digits=5,decimal_places=2) #price decimal(5,2) 999.99
pub_date = models.DateField() # pub_date date
publish = models.CharField(max_length=32)
创建字段的对应方法
_data_types = {
'AutoField': 'integer AUTO_INCREMENT',
#一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段;
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
# A true/false field. admin 用 checkbox 来表示此类字段.
'CharField': 'varchar(%(max_length)s)',
#字符串字段, 用于较短的字符串.
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
#一个日期字段.
'DateTimeField': 'datetime',
#一个日期时间字段.
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
#一个浮点数. 必须 提供两个参数:
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
#一个文件上传字段.
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
#用于保存一个整数.
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
#类似 BooleanField, 不过允许 NULL 作为其中一个选项.
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
#一个容量很大的文本字段.
'TimeField': 'time',
'UUIDField': 'char(32)',
}
class t1(models.Model):
# defauit 在使用orm操作添加数据时生效.
name = models.CharField(max_length=12, default='张三')
sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))
d1 = models.DateTimeField(auto_now_add=True,null=True) #自动添加创建记录的时间
d2 = models.DateTimeField(auto_now=True,null=True) #自动添加创建记录的时间,更新记录是也能自动更新为最新时间
auto_now 自动更新时间只有在save更新时才生效,update不生效
所以如果要做更新时间的处理,那么最好手动获取当前时间并修改
执行数据库同步指令,在项目根目录下面执行
python manage.py makemigrations
pyhton manage.py migrate
2.单表操作
- 创建
方式一
obj = model.Book(
title = '西游记',
price = 2,
# pub-date = '2000-12-13'
pub_date = datetime.datetime.now(), # 时间日期类型
publish = '31期红浪漫出版社'
)
obj.save()
方式2
obj = models.Book.objects.create( # obj是当前创建的新的记录对象
title='金瓶梅前传',
price=9.9,
# pub_date='2000-08-12', #这样的格式字符串
pub_date=datetime.datetime.now(), # 时间日期类型
publish='31期夕阳红出版社',
)
# 批量添加
obj_list = []
for i in range(1,4):
obj = models.Book(
title='少妇白洁' + str(i),
price=i,
pub_date=f'2000-08-1{i}',
publish='31期夕阳红出版社',
)
obj_list.append(obj)
models.Book.objects.bulk_create(obj_list)
mysql配置连接数据库
# 1.创建库 create database ceshi01
# 2.修改配置 setting.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'ceshi01',
'HOST':'127.0.0.1',
'PORT':3306,
'USER':'root',
'PASSWORD':''
}
}
# 3.在项目朱目录下面的inin文件中写上如下内容
import pymysql
pymysql.install_as_MySQLdb()
- 简单
#all() 获取表中所有数据,结果为querset类型
ret = models.Book.objects.all()
# 获取部分数据,结果为querset类型
ret = models.Book.objects.filter(title='西游记')
# get() 获取单条数据
# 找不到: Book matching query does not exist.
# 多余三个 : get() returned more than one Book
ret = models.Book.objects.get(title='')
十三个必知必会的查询接口
<1> all(): 查询所有结果,结果是queryset类型
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,结果也是queryset类型 Book.objects.filter(title='linux',price=100) #里面的多个条件用逗号分开,并且这几个条件必须都成立,
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。 Book.objects.get(id=1)
<4> exclude(**kwargs): 排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作昂,用这个exclude,返回值是queryset类型 Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用,Book.objects.all().exclude(id=6)
<5> order_by(*field): queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型 models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
<6> reverse(): queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
<7> count(): queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
<8> first(): queryset类型的数据来调用,返回第一条记录 Book.objects.all()[0] = Book.objects.all().first(),得到的都是model对象,不是queryset
<9> last(): queryset类型的数据来调用,返回最后一条记录
<10> exists(): queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据
<11> values(*field): 用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
<12> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<13> distinct(): values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
创建字段时的一些参数讲解
(1)null
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
(1)blank
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
(2)default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
(3)primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
(4)unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))
数据库里面存的是1或者2
通过model模型类对象.get_属性名称_display()可以获取到数字对应的文本内容
(6)db_index
如果db_index=True 则代表着为此字段设置数据库索引。
DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
(7)auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
(8)auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
filter基于双下划线的模糊查询
#按日期查询
ret = models.Book.objects.filter(pub_date__year='2000', pub_date__month='8',pub_date__day='12')
print(ret)
#关键字查询
ret = models.Book.objects.filter(title__startswith='少妇')
ret = models.Book.objects.filter(title__istartswith='py')
ret = models.Book.objects.filter(title__endswith='2')
ret = models.Book.objects.filter(title_icontains='python')
#不区分大小写
ret = models.Book.objects.filter(title__icontains='python')
print(ret)
ret = models.Book.objects.filter(price__in=[3,4]) # or 等于3或者等于4
ret = models.Book.objects.filter(price__range=[1,3]) #between 1 and 3
print(ret)
ret = models.Book.objects.filter(pub_date__year=2018)
ret = models.Book.objects.filter(pub_date__year='2018')
# 价格大于3的
# ret = models.Book.objects.filter(price__gt=3)
# 价格大于等于3的
# ret = models.Book.objects.filter(price__gte=3)
# 价格小于3的
ret = models.Book.objects.filter(price__lt=3)
# 价格小于等于3的
# ret = models.Book.objects.filter(price__lte=3)
- 删除
models.Book.objects.filter(title='西游记').delete()
models.Book.objects.get(id=3).delete()
query类型数据和模型类对象都可以调用delete方法来进行删除
- 修改
# 修改
# 方式1
# models.Book.objects.filter(id=4).update(
# title='少妇白洁1',
# price=4,
# )
# 方式2
obj = models.Book.objects.get(id=6)
obj.price = 18
obj.save()
obj.update() 模型类对象不能直接使用update方法
python中执行原生sql语句
#查看原生sql
方式一
from dhango.db import connection #通过这种方式也能 执行sql语句
coursor = connection.coursor()
coursor.execute('select * from app01:')
print(coursor.fetchall())
方式二
ret = models.Author.objects.raw('select * from app01_author;')
print(ret)
3.orm多表操作
- 增加和删除
增加
一对一
models.AuthorDetail.objects.create(
birthday='2018-01-01',
telephone='13800000000',
addr='北京'
)
ad_obj = models.AuthorDetail.objects.get(id=1)
models.Author.objects.create(
name='明皓',
age=38,
# ad=ad_obj,
ad_id=2,
)
ad_obj = models.AuthorDetail.objects.get(id=4)
obj= models.Author(
name='杨浩',
age=47,
ad=ad_obj,
# ad_id=3,
)
obj.save()
一对多
models.Book.objects.create(
title='金瓶梅第二部',
publishDate='2018-08-08',
price=22,
# publishs=models.Publish.objects.get(id=1),
publishs_id=1,
)
多对多
obj = models.Book.objects.filter(id=1).first()
a1 = models.Author.objects.get(id=1)
a2 = models.Author.objects.get(id=2)
obj.authors.add(a1,a2)
obj.authors.add(1,2)
obj.authors.add(*[1,2])
删除和更新
一对一和一对多 ,基本和单表一样(级联删除)
models.Author.objects.get(id=1).delete()
models.AuthorDetail.objects.get(id=2).delete()
models.Book.objects.get(id=1).delete()
多对多删除
ret = models.Book.objects.get(id=2)
ret.authors.clear() # 清空该书籍对应的第三张表中的记录
ret.authors.remove(3,4) #指定删除该书和哪些作者的关系
修改
# 修改
# 一对一和一对多
models.Author.objects.filter(name='精华').update(
name='敬华',
age=50,
# ad=models.AuthorDetail.objects.get(id=1),
ad_id=1,
)
# 多对多修改
obj = models.Book.objects.get(id=2)
obj.authors.set(['3',]) # 值是字符串,clear + add
查询
基于对象的跨表查询;
表关联查询;
关联属性在a表,那么通过a表数据查询b表中数据时,叫做正向查询;
正向查询使用对象,关联属性名称
关联属性在b表,那么通过b表数据查询a表中数据时,叫做正向查询;
反向查询使用对象,关联模型类名小写
#一对一
#正向查询 查看敬华作者的家庭住址
# select app01_authordetail.addr from app01_author inner join app01_authordetail on app01_author.ad_id =app01_atuhordetail.id where app01_author.name='敬华'
obj = models.Athor.object.get(name-'敬华')
print(obj.ad.dddr) # 北京协和
# 反向查询
obj = models.AuthorDetail.objects.get(addr='北京协和')
print(obj.author.name)
# 一对多
# 正向查询 查询金鳞岂是池中物这本书的出版社
obj = models.Book.objects.get(title='金鳞岂是池中物')
print(obj.publishs.name) #马哥出版社
#反向查询 查询马哥出版社出版的书籍有哪些
ret = models.Publish.objects.get(name='马哥出版社')
books = ret.book.set.all() #<QuerySet [<Book: Book object>, <Book: Book object>]>
print(book.values('title'))
# 多对多
#正向查询 查询金瓶梅第二部是谁写的
# select app01_author.name from app01_book inner join app01_book_authors on app01_book.id =app01_book_authors.book_id inner join app01_author on app01_author.id = app01_book_authors.author_id
obj = models.Book.objects.get(title='金瓶梅第二部')
print(obj.authors.all().values('name'))
# 反向查询 查看敬华写了那些书
obj = models.Author.objects.get(name='敬华')
print(obj.book_set.all().values('title'))
基于双下划线的跨表查询
一对一
#查看敬华作者的家庭住址
#正向写法
ret = models.Author.objects.filter(name='敬华').values('ad__addr')
#反向写法
ret = models.AuthorDetail.objects.filter(author__name='敬华').values('addr')
print(ret) #<QuerySet [{'addr': '北京'}]>
一对多
#查询金鳞岂是池中物这本书的出版社
ret = models.Book.objects.filter(title='金鳞岂是池中物').values('publishs__name')
print(ret) #<QuerySet [{'publishs__name': '小马哥出版社'}]>
ret = models.Publish.objects.filter(book__title='金鳞岂是池中物').values('name')
print(ret) #<QuerySet [{'name': '小马哥出版社'}]>
#多对多
#查询金瓶梅第二部是谁写的
ret = models.Book.objects.filter(title='金瓶梅第二部').values('authors__name')
print(ret)
ret = models.Author.objects.filter(book__title='金瓶梅第二部').values('name')
print(ret) #<QuerySet [{'name': '敬华'}, {'name': '杨浩'}]>
聚合查询
from django.db.models import Avg,Max,Min,Count,Sum
# 聚合查询
# ret = models.Book.objects.all().aggregate(Avg('price'))
# ret = models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
# print(ret,type(ret)) #{'price__avg': 15.0} <class 'dict'>
注意结果为字典类型.
分组查询
sql_mode模式
https://www.cnblogs.com/clschao/articles/9962347.html
分组查询
# # 分组查询
# # 统计一下每个出版社出版书的平均价格
# '''
# # select publishs_id,avg(price) from app01_book group by publishs_id;
# # select avg(app01_book.price) from app01_book inner join app01_publish on
# # app01_book.publishs_id = app01_publish.id group by app01_publish.name;
# '''
# # ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
# # print(ret)
# # ret = models.Publish.objects.annotate(a=Avg('book__price'))
# # print(ret.values('a','name'))
#
# # print(ret)
# # <QuerySet [<Publish: Publish object>, <Publish: Publish object>]>
# # print(ret.values('a','name'))
#
# # sql_mode
# # only_full_group_by
#
# # 查询一下点赞数大于评论数的书籍
# # 针对本表不同字段数据进行对比时或者本表字典做一些统一修改时使用F查询
# # models.Book.objects.filter(dianzan__gt=comment)
# # ret = models.Book.objects.all()
# # book_list = []
# # for i in ret:
# # if i.dianzan > i.comment:
# # book_list.append(i)
F查询
from django.db.models import F
# 点赞数大于评论数的
# ret = models.Book.objects.filter(dianzan__gt=F('comment'))
# print(ret)
# 所有书籍上调10块
models.Book.objects.all().update(price=F('price')+10) #支持四则运算
Q查询
from django.db.models import Q
# | -- or
# & -- and
# ~ -- not
# ret = models.Book.objects.filter(Q(comment__gt=30)|Q(dianzan__gt=50))
# ret = models.Book.objects.filter(Q(comment__gt=30)&Q(dianzan__gt=50))
# 等同于# ret = models.Book.objects.filter(comment__gt=30,dianzan__gt=50)
# ret = models.Book.objects.filter(Q(comment__gt=30) | Q(dianzan__gt=50),publishDate__year='2018')
# 注意没有Q包裹的条件,写在Q包裹的条件后面.
# 多层嵌套
# ret = models.Book.objects.filter(Q(Q(comment__gt=30) | Q(dianzan__gt=50))&Q(xx=11),publishDate__year='2018')
# 条件取反
# 取评论数小于等于30 的,或者点赞数大于50的
# ret = models.Book.objects.filter(~Q(comment__gt=30)|Q(dianzan__gt=50))
# print(ret)
锁和事务
from dango.db import transaction
# @transaction.atomic # 装饰器形式,所有的被装饰函数中的sql语句捆绑为事务,方式1
def xx(request):
# a= 10
# models ...
# a= a-1
#方式2 给逻辑中的部分sql加是事务
with transaction.atomic():
models.Book.objects.filter(price=100).select_for_update()# select * from app01-book where price =100 for update
#with 语句体里面的aql都捆绑为事务
#so sometings ....
ajax
特点:异步请求 局部刷新
$.ajax({
url:'请求路径',
type:'请求方法,
data:{'键':'值'.....},
success:function(res){
},
error:function(error){
}
})
中间件
请求生命周期
自定义中间件流程
1.在应用下从创建一个文件夹
2.在文件夹中创建一个py文件
3.在文件中配置内容
from django.utils.deprecation import MiddlewareMixin
class Auth(MiddlewareMixin): # 类名随意,继承MiddlewareMixin
# 如果想对请求做一些统一处理,那么就定义process_repuest方法
def process_repuest(self,request):
print('请求来了')
def process_reponse(self,request,reponse):
# reponse 视图响应回来的相应对象
# response.status_code = 201
print('请求走了')
return response
4.在settings.py文件中做如下配置
MIDDLEWARE = [
'app01.mymiddlewares.xx.Auth'
'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',
'app01.mymiddlewares.xx.Auth', #将中间件类的路径写到这里
]
三.cookie和session
http协议的两个特点(基于tcp协议
- 无连接(短链接) 请求:connection: close,keepalive
- 无状态 : 不会记录客户端和服务端的任何信息,导致服务端的和客户端不能维持会话
- 因为http协议上述特点所以出现cookie浏览器技术,浏览器访问服务端,带着一个空的cookie,然后有服务器产生内容,浏览器收到相应后保存在本地:在浏览器访问时,浏览器会自动带上cookie.这样服务器就能通过cookie的内容来判断这个是"谁"了
作用过程
cookie操作
# 获取cookie
repuest.COOLIES['key']
request.get-signed_cookie(key,default=RAISE_ERROR,salt='',max_age=None)#代签名的值
# 设置cookie
rep = HttpResponse(...)
rep =render(re1uest,...)
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
#c修改 cookie
rep.set_cookie('username','uname') # 相同的键设置不同的值就是修改
# 删除cookie
ret.delete_cookie('username')
'''
参数:
key, 键
value='', 值
max_age=None, 超时时间,单位为秒 5表示5秒后失效
expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) 值为时间日期类型数据
path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=True, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
'''
-
特点
- cookie是明文存储的
- 大小限制
#cookie 大小上限为4kb; 一个服务器最多在客户端浏览器上保存20个cookie;一个浏览器最多保存300个cookie,因为一个浏览器可以访问多个服务器.
seeeion技术
- 一个浏览器对应一个服务端,就是session
- 数据没有大小限制
- cookie中放的数据是密文的
session操作
# 获取session
requsest.session['k1']
request.session.get('k1',None)
#设置session
request.session['k1'] =123
request.seeion.setdefault('k1',123)
#删除 seeion
del request.session['k1']
#清空session
request.session.flush()
# 注销
def logout(request):
request.session.flush()
#1.删除cookie中的数据
#2.删除数据库中django-session表中的数据
return redirect('/login/')
django外部脚本调用django环境
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_cookie.settings")
import django
django.setup()
from app01 import models
models.Book.objects.create(
title='xxx',
price=200,
)
csrftoken
cssef:跨站请求伪造
文件上传
from表单上传文件
<form action="" method="post" enctype="multipart/form-data"><--在标签中添加enctype属性--->
{% csrf_token %}
用户名:<input type="text" name="username" >
头像:<input type="file" name="avatar" multiple>
<input type="submit">
</form>
ajax上传文件
用户名:<input type="text" name="username" id="username">
头像:<input type="file" name="avatar" id="avatar">
<button id="ajax_btn">上传</button>
$('#ajax_btn').click(function () {
var uname = $('#username').val();
var file_obj = $('#avatar')[0].files[0];
var formdata = new FormData();
formdata.append('username',uname);
formdata.append('csrfmiddlewaretoken','{{ csrf_token }}');
formdata.append('avatar',file_obj);
$.ajax({
url:'/login/',
type:'post',
data:formdata,
processData: false , // 不处理数据
contentType: false, // 不设置内容类型
success:function (res) {
console.log(res);
}
})
})
视图代码
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
# print(request.POST)
# print(request.FILES)
file_obj = request.FILES.get('avatar')
print(file_obj)
name = file_obj.name
print(name)
# with open(fr'C:\Users\oldboy\Desktop\Pointofix\{name}', 'wb') as f:
print(type(file_obj))
with open(name, 'wb') as f:
# 方式1
# for i in file_obj: # \r\n
# 方式2
# for i in file_obj.chunks(): # \r\n
for i in file_obj.chunks(): # \r\n 读取65536B
f.write(i)
return HttpResponse('ok')