课程特点:
django-docs-1.11-en.zip
django-docs-1.11-en/index.html
查看已安装的版本
>>> import django
>>> print(django.VERSION)
(1, 11, 8, 'final', 0)
安装
$ sudo pip3 install django
安装django的最新版本$ sudo pip3 install django[==版本]
安装django的指定版本$ sudo pip3 install django==1.11.8
$ tar -xvf Django-1.11.8.tar.gz
$ cd Django-1.11.8
$ sudo python3 setup.py install
pip3 download -d /home/tarena/django_packs django==1.11.8
Django的卸载
$ pip3 uninstall django
Django 的开发环境
- $ django-admin startproject 项目名称
- 如:
- $ django-admin startproject mysite1
- 运行
```shell
$ cd mysite1
$ python3 manage.py runserver
# 或
$ python3 manage.py runserver 5000 # 指定只能本机使用127.0.0.1的5000端口访问本机
```
$ django-admin startproject mysite1
$ tree mysite1/
mysite1/
├── manage.py
└── mysite1
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
1 directory, 5 files
manage.py
manage.py
包含项目管理的子命令, 如:
python3 manage.py runserver
启动服务python3 manage.py startapp
创建应用python3 manage.py migrate
数据库迁移python3 mange.py []
可以显示帮助文档,查看所有选项__init__.py
settings.py
BASE_DIR
用于绑定当前项目的绝对路径(动态计算出来的), 所有文件都可以依懒此路径
DEBUG
用于配置Django项目的启用模式, 取值:
True 表示开发环境中使用 调试模式
(用于开发中)
False 表示当前项目运行在生产环境中
(不启用调试)
ALLOWED_HOSTS
设置允许访问到本项目的网络地址列表,取值:
127.0.0.1
, localhost
能访问本项目python3 manage.py runserver 0.0.0.0:5000
# 指定网络设备所有主机都可以通过5000端口访问(需加ALLOWED_HOSTS = ['*']
)
INSTALLED_APPS
指定当前项目中安装的应用列表
MIDDLEWARE
用于注册中间件
TEMPLATES
用于指定模板的配置信息
DATABASES
用于指定数据库的配置信息
LANGUAGE_CODE
用于指定语言配置
取值:
英文 : "en-us"
中文 : "zh-Hans"
TIME_ZONE
用于指定当前服务器端时区
取值:
世界标准时间: "UTC"
中国时区 : "Asia/Shanghai"
ROOT_URLCONF
用于配置根级 url 配置 ‘mysite1.urls’
如:
ROOT_URLCONF = 'mysite1.urls'
注: 此模块可以通过
from django.conf import settings
导入和使用
urls.py
views.py
url 即统一资源定位符 Uniform Resource Locator
作用:
说明:
URL的一般语法格式为:
https://www.djangoproject.com/download/
protocol :// hostname[:port] / path [?query][#fragment]
如:
http://tts.tmooc.cn/video/showVideo?meuId=657421&version=AID201908#s
说明:
HTTP://
HTTPS://
file:///
用函数创建的视图
视图函数是用于接收一个浏览器请求并通过HttpResponse对象返回数据的函数。此函数可以接收浏览器请求并根据业务逻辑返回相应的内容给浏览器
视图处理的函数的语法格式:
def xxx_view(request[, 其它参数...]):
return HttpResponse对象
参数:
示例:
视图处理函数 views.py
# file : <项目名>/views.py
from django.http import HttpResponse
def page1_view(request):
html = "这是第1个页面
"
return HttpResponse(html)
优点:可以直接匹配get,post,head等请求方式
如果平常的用FBV的话就需要匹配用if—elif的方式来匹配访问方式,这种方式往往显得很笨拙
views.py
# FBV模式
def home(request):
if request.method == 'GET':
xxxx
elif request.method == 'POST':
XXX
urls.py
from django.urls import path,include
from index import views
urlpatterns = [
# FBV模式
path('',views.index_home)
]
views.py
# CBV模式
class Index_home(View):
def get(self,request):
return HttpResponse('这是get主页')
def post(self,request):
return HttpResponse('这是post主页')
urls.py
from django.urls import path,include
from index import views
urlpatterns = [
# FBV模式
# path('',views.index_home)
# CBV模式
path('',views.Index_home.as_view())
]
from django.urls import path
from . import views
# path函数的第一个参数为路由地址名,第二个参数为views的函数名
urlpatterns = [
path('',views.main_view), # 匹配127.0.0.1
path('page/1',views.page1_view), # 127.0.0.1/page/1
path('page/2',views.page1_view), # 127.0.0.1/page/2
]
path函数的第一个参数为路由地址名,第二个参数为views的函数名
函数 path()
具有四个参数,两个必须参数:route
和 view
,两个可选参数:kwargs
和 name
。现在,是时候来研究这些参数的`含义了。
route
是一个匹配 URL 的准则(类似正则表达式)。当 Django 响应一个请求时,它会从 urlpatterns
的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项。
这些准则不会匹配 GET 和 POST 参数或域名。例如,URLconf 在处理请求 https://www.example.com/myapp/
时,它会尝试匹配 myapp/
。处理请求 https://www.example.com/myapp/?page=3
时,也只会尝试匹配 myapp/
view
当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个 HttpRequest
对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入
kwargs
任意个关键字参数可以作为一个字典传递给目标视图函数
name
为你的 URL 取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个 URL 模式。
定义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pxiZ6lVJ-1683969713205)(…/assets/image-20220308134517864_1646837814760_0.png)]
# 例子 匹配127.0.0.1/int/str/int 第一个参数为任意整型,第二个参数为任意字符型,第三个参数为任意整型
path('//' ,views.cal_view),
类型分类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYmyKwne-1683969713206)(…/assets/image-20220308134802884_1646837839971_0.png)]
根据HTTP标准,HTTP请求可以使用多种请求方法。
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法(最常用)
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
HTTP1.1 请求详述
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息,并返回实体主体。 |
2 | HEAD | 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 |
4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
5 | DELETE | 请求服务器删除指定的页面。 |
6 | CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 |
7 | OPTIONS | 允许客户端查看服务器的性能。 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
django的view.py的每个视图函数的形参里面都有一个request,这就是httpRequest对象,httpRequest有很多属性和方法供我们服务端使用,我们可以从中简单的得到客户端发来的请求信息
常用的方法和属性如下
视图函数的第一个参数是HttpRequest对象
服务器接收到http协议的请求后,会根据请求数据报文创建HttpRequest对象
HttpRequest属性
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头用以响应浏览器的请求。
HTTP状态码的英文为HTTP Status Code。
下面是常见的HTTP状态码:
HTTP状态码分类
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
视图函数的返回的一个函数
HttpResponse详解
构造函数格式:
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
作用:
collapsed:: true
参数:
collapsed:: true
content:表示返回的内容。
status_code:返回的HTTP响应状态码(默认为200)。
content_type:指定返回数据的的MIME类型(默认为"text/html")。浏览器会根据这个属性,来显示数据。如果是text/html,那么就会解析这个字符串,如果text/plain,那么就会显示一个纯文本。
常用的Content-Type如下:
'text/html'
(默认的,html文件)'text/plain'
(纯文本)'text/css'
(css文件)'text/javascript'
(js文件)'multipart/form-data'
(文件提交)'application/json'
(json传输)'application/xml'
(xml文件)
> 注: 关键字MIME(Multipurpose Internet Mail Extensions)是指多用途互联网邮件扩展类型。
HttpResponse 子类
类型 | 作用 | 状态码 |
---|---|---|
HttpResponseRedirect | 重定响 | 301 |
HttpResponseNotModified | 未修改 | 304 |
HttpResponseBadRequest | 错误请求 | 400 |
HttpResponseNotFound | 没有对应的资源 | 404 |
HttpResponseForbidden | 请求被禁止 | 403 |
HttpResponseServerError | 服务器错误 | 500 |
GET请求方式中可以通过查询字符串(Query String)将数据传递给服务器
URL 格式: xxx?参数名1=值1&参数名2=值2...
http://127.0.0.1:8000/page1?a=100&b=200
服务器端接收参数
判断 request.method 的值判断请求方式是否是get请求
if request.method == 'GET':
处理GET请求时的业务逻辑
else:
处理其它请求的业务逻辑
获取客户端请求GET请求提交的数据
语法
request.GET['参数名'] # QueryDict
request.GET.get('参数名','默认值') # 类似于字典的
request.GET.getlist('参数名')
# mypage?a=100&b=200&c=300&b=400
# request.GET=QueryDict({'a':['100'], 'b':['200','400'], 'c':['300']})
# a = request.GET['a']
# b = request.GET['b'] # Error
能够产生get请求方式的场合
一般查询字符串的大小会受到浏览器的的限制(不建议超过2048字节)
练习:
练习:
客户端通过表单等POST请求将数据传递给服务器端,如:
<form method='post' action="/login">
姓名:<input type="text" name="username">
<input type='submit' value='登陆'>
form>
服务器端接收参数
通过 request.method 来判断是否为POST请求,如:
if request.method == 'POST':
处理POST请求的数据并响应
else:
处理非POST 请求的响应
使用post方式接收客户端数据
request.POST['参数名'] # request.POST 绑定QueryDict
request.POST.get('参数名','')
request.POST.getlist('参数名')
取消csrf验证,否则Django将会拒绝客户端发来的POST请求
取消 csrf 验证
删除 settings.py 中 MIDDLEWARE 中的 CsrfViewsMiddleWare 的中间件
```python
```python
MIDDLEWARE = [ ..# 'django.middleware.csrf.CsrfViewMiddleware',...]
```
MTV 代表 Model-Template-View(模型-模板-视图) 模式。这种模式用于应用程序的分层开发
模板传参是指把数据形成字典,传参给模板,为模板渲染提供数据
t = loader.get_template('xxx.html')
html = t.render(字典数据)
return HttpResponse(html)
return render(request,'xxx.html',字典数据)
在模板中使用变量语法
{{ 变量名 }}
{{ 变量名.index }}
list.0
{{ 变量名.key}}
dict[‘key’] dict.key
{{ 对象.方法 }}
{{ 函数名 }}
def xxx_view(request)
dic = {
"变量1":"值1",
"变量2":"值2",
}
return render(request, 'xxx.html', dic)
练习
写一个简单的计算器页面,能够在服务端进行简单加减乘除计算
form中的action属性 -> 此次提交的地址
ex: action=‘/mycal’ -> 提交地址为当前浏览器地址栏的ip+端口+ action, 即 http://127.0.0.1:8000/mycal
参考代码
<form action='/mycal' method='POST'>
<input type='text' name="x" value="1">
<select name='op'>
<option value="add" > +加 option>
<option value="sub" > -减 option>
<option value="mul"> *乘 option>
<option value="div"> /除 option>
select>
<input type='text' name="y" value="2"> = <span>3span>
<div>
<input type="submit" value='开始计算'>
<div>
form>
作用
将一些服务器端的功能嵌入到模板中
标签语法
{% 标签 %}
...
{% 结束标签 %}
if 标签
{% if 条件表达式1 %}
...
{% elif 条件表达式2 %}
...
{% elif 条件表达式3 %}
...
{% else %}
...
{% endif %}
if 标签里的布尔运算符
if 条件表达式里可以用的运算符 ==, !=, <, >, <=, >=, in, not in, is, is not, not、and、or
在if标签中使用实际括号是无效的语法。 如果您需要它们指示优先级,则应使用嵌套的if标记。
locals函数的使用
locals() 返回当前函数作用域内全部局部变量形成的字典。
for 标签
{% for 变量 in 可迭代对象 %}
... 循环语句
{% empty %}
... 可迭代对象无数据时填充的语句
{% endfor %}
2. 内置变量 - forloop
变量 | 描述 |
---|---|
forloop.counter | 循环的当前迭代(从1开始索引) |
forloop.counter0 | 循环的当前迭代(从0开始索引) |
forloop.revcounter | 循环结束的迭代次数(从1开始索引) |
forloop.revcounter0 | 循环结束的迭代次数(从0开始索引) |
forloop.first | 如果这是第一次通过循环,则为真 |
forloop.last | 如果这是最后一次循环,则为真 |
forloop.parentloop | 当嵌套循环,parentloop 表示外层循环 |
作用
在变量输出时对变量的值进行处理
您可以通过使用 过滤器来改变变量的输出显示。
{{ 变量 | 过滤器1:参数值1 | 过滤器2:参数值2 … }}
常用的过滤器
过滤器 | 说明 |
---|---|
lower | 将字符串转换为全部小写。 |
upper | 将字符串转换为大写形式 |
safe | 默认不对变量内的字符串进行html转义 |
add: “n” | 将value的值增加 n |
truncatechars:‘n’ | 如果字符串字符多于指定的字符数量,那么会被截断。 截断的字符串将以可翻译的省略号序列(“…”)结尾。 |
… |
文档参见:
https://docs.djangoproject.com/en/1.11/ref/templates/builtins/
模板继承可以使父模板的内容重用,子模板直接继承父模板的全部内容并可以覆盖父模板中相应的块
定义父模板中的块 block
标签
标识出哪些在子模块中是允许被修改的
block标签:在父模板中定义,可以在子模板中覆盖
{% block block_name %}
定义模板块,此模板块可以被子模板重新定义的同名块覆盖
{% endblock block_name %}
继承模板 extends
标签(写在模板文件的第一行)
子模板继承语法标签
{% extends '父模板名称' %}
{% extends 'base.html' %}
子模板 重写父模板中的内容块
{% block block_name %}
子模板块用来覆盖父模板中 block_name 块的内容
{% endblock block_name %}
重写的覆盖规则
注意
参考文档
https://docs.djangoproject.com/en/1.11/ref/templates/
模板的继承示例:
url 反向解析是指在视图或模板中,用为url定义的名称来查找或计算出相应的路由
url 函数的语法
url() 的name
关键字参数
作用:
name=
关键字传参给 url确定了个唯一确定的名字,在模板中,可以通过这个名字反向推断出此url信息在模板中通过别名实现地址的反向解析
{% url '别名' %}
{% url '别名' '参数值1' '参数值2' %}
练习:
写一个有四个自定义页面的网站,对应路由:
/ 主页
/page1 页面1
/page2 页面2
/page3 页面3
功能: 主页加 三个页面的连接分别跳转到一个页面,三个页面每个页面加入一个链接用于返回主页
url反向解析在django的视图函数的应用
实际用法
先在路由函数中取别名,然后在视图函数中利用reverse函数进行反向解析
什么是静态文件
静态文件配置
配置静态文件的访问路径
https://oimagea1.ydstatic.com/image?id=-5337101211608292607&product=adpublish&w=640&h=480&sc=0&rm=2&gsb=0&gsbd=60
配置静态文件的存储路径 STATICFILES_DIRS
示例:
# file: setting.py
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
访问静态文件
使用静态文件的访问路径进行访问
访问路径: STATIC_URL = ‘/static/’
示例:
<img src="/static/images/lena.jpg">
<img src="http://127.0.0.1:8000/static/images/lena.jpg">
通过 {% static %}标签访问静态文件
{% static %}
表示的就是静态文件访问路径{% load static %}
{% static '静态资源路径' %}
创建步骤
创建应用的子命令
Django应用的结构组成
migrations
文件夹
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
配置安装应用
在 settings.py 中配置应用, 让此应用能和整个项目融为一体
# file : settings.py
INSTALLED_APPS = [
... ...,
'自定义应用名称'
]
如:
INSTALLED_APPS = [
# ....
'user', # 用户信息模块
'music', # 收藏模块
]
作用:
函数格式
模块
app命字/url模块名.py
文件件里必须有urlpatterns 列表
使用前需要使用from django.conf.urls import include
导入此函数
练习:
1.创建四个应用
1.创建 index 应用,并注册
2.创建 sport 应用,并注册
3.创建 news 应用,并注册
4.创建 music 应用,并注册
2.创建分布式路由系统
主路由配置只做分发
每个应用中处理具体访问路径和视图
1. 127.0.0.1:8000/music/index
交给 music 应用中的 index_view() 函数处理
2. 127.0.0.1:8000/sport/index
交给 sport 应用中的 index_view() 函数处理
3. 127.0.0.1:8000/news/index
交给 news 应用中的 index_view() 处理处理
安装 pymysql包
$ sudo pip3 install pymysql
$ sudo pip3 install mysqlclient
创建 和 配置数据库
创建数据库
create database 数据库名 default charset utf8 collate utf8_general_ci;
create database mywebdb default charset utf8 collate utf8_general_ci;
数据库的配置
sqlite 数据库配置
# file: settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
mysql 数据库配置
DATABASES = {
'default' : {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mywebdb', # 数据库名称,需要自己定义
'USER': 'root',
'PASSWORD': '123456', # 管理员密码
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
关于数据为的SETTING设置
ENGINE
'django.db.backends.mysql'
'django.db.backends.sqlite3'
'django.db.backends.oracle'
'django.db.backends.postgresql'
NAME
'NAME': 'mywebdb'
USER
'USER':'root'
PASSWORD
'PASSWORD':'123456'
HOST
'HOST':'127.0.0.1'
PORT
'PORT':'3306'
添加 mysql 支持
安装pymysql 模块
$ sudo pip install pymysql
修改项目中__init__.py 加入如下内容来提供pymysql引擎的支持
import pymysql
pymysql.install_as_MySQLdb()
此示例为添加一个 bookstore_book 数据表来存放图书馆中书目信息
添加一个 bookstore 的 app
$ python3 manage.py startapp bookstore
添加模型类并注册app
# file : bookstore/models.py
from django.db import models
class Book(models.Model):
title = models.CharField("书名", max_length=50, default='')
price = models.DecimalField('定价', max_digits=7, decimal_places=2, default=0.0)
注册app
# file : setting.py
INSTALLED_APPS = [
...
'bookstore',
]
数据库的迁移
数据库的表名为应用名下划线+类ming
python3 manage.py makemigrations
python3 manage.py migrate
每次修改完模型类再对服务程序运行之前都需要做以上两步迁移操作。
生成迁移脚本文件bookstore/migrations/0001_initial.py
并进行迁移
$ python3 manage.py makemigrations
$ python3 manage.py migrate
编写模型类Models
模型类需继承自django.db.models.Model
Models的语法规范
from django.db import models
class 模型类名(models.Model):
字段名 = models.字段类型(字段选项)
模型类名是数据表名的一部分,建议类名首字母大写
字段名又是当前类的类属性名,此名称将作为数据表的字段名
字段类型用来映射到数据表中的字段的类型
字段选项为这些字段提供附加的参数信息
app_label
如果模型是在 INSTALLED_APPS 中的应用程序之外定义的,它必须声明它属于哪个应用程序base_manager_name
用于模型的 _base_manager 的管理器的属性名称,例如“对象”db_table
用于模型的数据库表的名称例如:
from django.db import models
class 模型类名(models.Model):
字段名 = models.字段类型(字段选项)
class Meta:
db_table = 'xxxx'
BooleanField()
CharField()
必须要指定max_length参数值,否则会报错
DateField()
DateTimeField()
DecimalField()
数据库类型:decimal(x,y)
和钱相关
编程语言中:使用小数表示该列的值
在数据库中:使用小数
参数(必须):
示例:
money=models.DecimalField(
max_digits=7,
decimal_places=2,
default=0.0
)
FloatField()
EmailField()
IntegerField()
URLField()
ImageField()
数据库类型:varchar(100)
作用:在数据库中为了保存图片的路径
编程语言和数据库中使用字符串
示例:
image=models.ImageField(
upload_to="static/images"
)
upload_to:指定图片的上传路径
在后台上传时会自动的将文件保存在指定的目录下
TextField()
参考文档 https://docs.djangoproject.com/en/1.11/ref/models/fields/#field-types
字段选项FIELD_OPTIONS
尽量的不使用
示例:
# 创建一个属性,表示用户名称,长度30个字符,必须是唯一的,不能为空,添加索引
name = models.CharField(max_length=30, unique=True, null=False, db_index=True)
文档参见:
当执行 $ python3 manage.py makemigrations
出现如下迁移错误时的处理方法
错误信息
$ python3 manage.py makemigrations
You are trying to change the nullable field 'title' on book to non-nullable without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)
3) Quit, and let me add a default in models.py
Select an option:
翻译为中文如下:
$ python3 manage.py makemigrations
您试图将图书上的可空字段“title”更改为非空字段(没有默认值);我们不能这样做(数据库需要填充现有行)。
请选择修复:
1)现在提供一次性默认值(将对所有现有行设置此列的空值)
2)暂时忽略,让我自己处理空值的现有行(例如,因为您在以前的数据迁移中添加了RunPython或RunSQL操作来处理空值)
3)退出,让我在models.py中添加一个默认值
选择一个选项:
错误原因
class Book(models.Model):
title = models.CharField("书名", max_length=50, null=True)
class Book(models.Model):
title = models.CharField("书名", max_length=50)
处理方法:
default=XXX
的缺省值(推荐使用)数据库的迁移文件混乱的解决办法
__init__.py
除外)每个继承自 models.Model 的模型类,都会有一个 objects 对象被同样继承下来。这个对象叫管理器对象
数据库的增删改查可以通过模型的管理器实现
class MyModel(models.Model):
...
MyModel.objects.create(...) # objects 是管理器对象
Django 使用一种直观的方式把数据库表中的数据表示成Python 对象
创建数据中每一条记录就是创建一个数据对象
MyModel.objects.create(属性1=值1, 属性2=值1,…)
创建 MyModel 实例对象,并调用 save() 进行保存
obj = MyModel(属性=值,属性=值)
obj.属性=值
obj.save()
无返回值,保存成功后,obj会被重新赋值
在Django提供了一个交互式的操作项目叫 Django Shell
它能够在交互模式用项目工程的代码执行相应的操作
利用 Django Shell 可以代替编写View的代码来进行直接操作
在Django Shell 下只能进行简单的操作,不能运行远程调式
启动方式:
$ python3 manage.py shell
练习:
在 bookstore/models.py 应用中添加两个model类
1. Book - 图书
1. title - CharField 书名,非空,唯一
2. pub - CharField 出版社,字符串,非空
3. price - 图书定价,,
4. market_price - 图书零售价
2. Author - 作者
1. name - CharField 姓名,非空
2. age - IntegerField, 年龄,非空,缺省值为1
3. email - EmailField, 邮箱,允许为空
然后用 Django Shell 添加如下数据
图书信息
书名 | 定价 | 零售价 | 出版社 |
---|---|---|---|
Python | 20.00 | 25.00 | 清华大学出版社 |
Python3 | 60.00 | 65.00 | 清华大学出版社 |
Django | 70.00 | 75.00 | 清华大学出版社 |
JQuery | 90.00 | 85.00 | 机械工业出版社 |
Linux | 80.00 | 65.00 | 机械工业出版社 |
Windows | 50.00 | 35.00 | 机械工业出版社 |
HTML5 | 90.00 | 105.00 | 清华大学出版社 |
作者信息:
姓名 | 年龄 | 邮箱 |
---|---|---|
王老师 | 28 | [email protected] |
吕老师 | 31 | [email protected] |
祁老师 | 30 | [email protected] |
拓展:
settings.py里 APPEND_SLASH
APPEND_SLASH -> 自动补全 /
案例:
url(r’^page1/$', xx) ,访问浏览器时 地址栏输入 127.0.0.1:8000/page1 ,此时 django接到请求后
返回301【永久重定向】,并在响应头中指定重定向地址为 /page1/ ,从而出现自动补全 / 效果
若要关闭此功能,可将 APPEND_SLASH = False
出现8000端口已占用 解决方案
1,查看是否有django进程启动
ps aux|grep ‘runserver’
2,若grep中出现 相关进程,直接干!
kill -9 pid pid
案例如下:
执行 查!
tarena@tedu:~/aid1906/django/day03/mysite3$ ps aux|grep 'runserver'
tarena 13984 0.0 0.4 125980 39604 pts/0 S+ 15:39 0:00 python3 manage.py runserver
tarena 14914 1.2 0.5 202864 41312 pts/0 Sl+ 16:10 0:05 /usr/bin/python3 manage.py runserver
tarena 15056 0.0 0.0 21532 1156 pts/4 S+ 16:17 0:00 grep --color=auto runserv
#执行 干!
kill -9 13984 14914
插入一条数据
MyModel.objects.create(obj1)
批量创建数据
MyModel.objects.bulk_create([obj1, obj2, obj3])
数据库的查询需要使用管理器对象进行
通过 MyModel.objects 管理器方法调用查询接口
方法 | 说明 |
---|---|
all() | 查询全部记录,返回QuerySet查询对象 |
get() | 查询符合条件的单一记录 |
filter() | 查询符合条件的多条记录 |
exclude() | 查询符合条件之外的全部记录 |
… |
方法: all()
用法: MyModel.objects.all()
作用: 查询MyModel实体中所有的数据
返回值: QuerySet容器对象,内部存放 MyModel 实例
示例:
from bookstore import models
books = models.Book.objects.all()
for book in books:
print("书名", book.title, '出版社:', book.pub)
在模型类中定义 def __str__(self):
方法可以将自定义默认的字符串
class Book(models.Model):
title = ...
def __str__(self):
return "书名: %s, 出版社: %s, 定价: %s" % (self.title, self.pub, self.price)
方法: values(‘列1’, ‘列2’)
用法: MyModel.objects.values(…)
作用: 查询部分列的数据并返回
返回值: QuerySet
示例:
from bookstore import models
books = models.Book.objects.values("title", "pub")
for book in books:
print("书名", book["title"], '出版社:', book['pub'])
print("book=", book)
方法:values_list(‘列1’,‘列2’)
用法:MyModel.objects.values_list(…)
作用:
返回值: QuerySet容器对象,内部存放 元组
示例:
from bookstore import models
books = models.Book.objects.values_list("title", "pub")
for book in books:
print("book=", book) # ('Python', '清华大学出版社')...
方法:order_by
用法:MyModel.objects.order_by(‘-列’,‘列’)
作用:
说明:
默认是按照升序排序,降序排序则需要在列前增加’-'表示
示例:
from bookstore import models
books = models.Book.objects.order_by("-price")
for book in books:
print("书名:", book.title, '定价:', book.price)
根据条件查询多条记录
方法: filter(条件)
语法:
MyModel.objects.filter(属性1=值1, 属性2=值2)
返回值:
说明:
Books.objects.filter(price=20, pub="清华大学出版社")
返回定价为20 且
出版社为"清华大学出版社"的全部图书示例:
# 查询书中出版社为"清华大学出版社"的图书
from bookstore import models
books = models.Book.objects.filter(pub="清华大学出版社")
for book in books:
print("书名:", book.title)
2. 查询Author实体中id为1并且isActive为True的
- authors=Author.objects.filter(id=1,isActive=True)
字段查询是指如何指定SQL语句中 WHERE 子句的内容。
字段查询需要通过QuerySet的filter(), exclude() and get()的关键字参数指定。
非等值条件的构建,需要使用字段查询
示例:
# 查询作者中年龄大于30
Author.objects.filter(age__gt=30)
# 对应
# SELECT .... WHERE AGE > 30;
__exact
: 等值匹配
Author.objects.filter(id__exact=1)
# 等同于select * from author where id = 1
__contains
: 包含指定值
Author.objects.filter(name__contains='w')
# 等同于 select * from author where name like '%w%'
__startswith
: 以 XXX 开始
__endswith
: 以 XXX 结束
__gt
: 大于指定值
Author.objects.filer(age__gt=50)
# 等同于 select * from author where age > 50
__gte
: 大于等于
__lt
: 小于
__lte
: 小于等于
__in
: 查找数据是否在指定范围内
Author.objects.filter(country__in=['中国','日本','韩国'])
# 等同于 select * from author where country in ('中国','日本','韩国')
__range
: 查找数据是否在指定的区间范围内
# 查找年龄在某一区间内的所有作者
Author.objects.filter(age__range=(35,50))
# 等同于 SELECT ... WHERE Author BETWEEN 35 and 50;
详细内容参见: https://docs.djangoproject.com/en/1.11/ref/models/querysets/#field-lookups
示例
MyModel.objects.filter(id__gt=4)
# 等同于 SELECT ... WHERE id > 4;
练习:
查询Book表中price大于等于50的信息
Book.objects.filter(price__gte=50)
查询Author表中姓王的人的信息
Author.objects.filter(name__startswith=‘王’)
查询Author表中Email中包含"wc"的人的信息
Author.objects.filter(email__contains=‘wc’)
不等的条件筛选
语法:
MyModel.objects.exclude(条件)
作用:
条件
的 全部的数据集示例:
清华大学出版社,定价大于50
以外的全部图书books = models.Book.objects.exclude(pub="清华大学出版社", price__gt=50)
for book in books:
print(book)
语法:
MyModel.objects.get(条件)
作用:
返回值:
说明:
示例:
from bookstore import models
book = models.Book.objects.get(id=1)
print(book.title)
如:
from bookstore import models
abook = models.Book.objects.get(id=10)
abook.market_price = "10.5"
abook.save()
直接调用QuerySet的update(属性=值) 实现批量修改
如:
# 将 id大于3的所有图书价格定为0元
books = Book.objects.filter(id__gt=3)
books.update(price=0)
# 将所有书的零售价定为100元
books = Book.objects.all()
books.update(market_price=100)
练习:修改图书得零售价
路由: /bookstore/mod/5
删除单个对象
步骤
示例:
try:
auth = Author.objects.get(id=1)
auth.delete()
except:
print(删除失败)
删除查询结果集
步骤
示例:
# 删除全部作者中,年龄大于65的全部信息
auths = Author.objects.filter(age__gt=65)
auths.delete()
不带分组聚合
不带分组的聚合查询是指导将全部数据进行集中统计查询
聚合函数:
django.db.models
from django.db.models import *
语法:
返回结果:
示例:
# 得到所有书的平均价格
from bookstore import models
from django.db.models import Count
result = models.Book.objects.aggregate(myAvg=Avg('price'))
print("平均价格是:", result['myAvg'])
print("result=", result) # {"myAvg": 58.2}
# 得到数据表里有多少本书
from django.db.models import Count
result = models.Book.objects.aggregate(mycnt=Count('title'))
print("数据记录总个数是:", result['mycnt'])
print("result=", result) # {"mycnt": 10}
分组聚合
分组聚合是指通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
语法:
用法步骤:
通过先用查询结果MyModel.objects.value. 查找查询要分组聚合的列
MyModel.objects.value(‘列1’, ‘列2’)
如:
pub_set = models.Book.objects.values('pub')
print(books) #
通过返回结果的 QuerySet.annotate 方法分组聚合得到分组结果
QuerySet.annotate(名=聚合函数(‘列’))
返回 QuerySet 结果集,内部存储结果的字典
如:
pub_count_set = pub_set.annotate(myCount=Count('pub'))
print(pub_count_set) #
示例:
def test_annotate(request):
- from django.db.models import Count
from . import models
# 得到所有出版社的查询集合QuerySet
pub_set = models.Book.objects.values('pub')
# 根据出版社查询分组,出版社和Count的分组聚合查询集合
pub_count_set = pub_set.annotate(myCount=Count('pub')) # 返回查询集合
for item in pub_count_set:
print("出版社:", item['pub'], "图书有:", item['myCount'])
return HttpResponse('请查看服务器端控制台获取结果')
作用:
用法
from django.db.models import F
语法:
from django.db.models import F
F('列名')
说明:
可以避免资源竞争
示例1
models.Book.objects.all().update(market_price=F('market_price')+10)
# 以s做法好于如下代码
books = models.Book.objects.all()
for book in books:
book.update(market_price=book.marget_price+10)
book.save()
示例2
from django.db.models import F
from bookstore import models
books = models.Book.objects.filter(market_price__gt=F('price'))
for book in books:
print(book.title, '定价:', book.price, '现价:', book.market_price)
当在获取查询结果集 使用复杂的逻辑或 |
、 逻辑非 ~
等操作时可以借助于 Q对象进行操作
如: 想找出定价低于20元 或 清华大学出版社的全部书,可以写成
models.Book.objects.filter(Q(price__lt=20)|Q(pub="清华大学出版社"))
Q对象在 数据包 django.db.models 中。需要先导入再使用
from django.db.models import Q
作用
运算符:
语法
from django.db.models import Q
Q(条件1)|Q(条件2) # 条件1成立或条件2成立
Q(条件1)&Q(条件2) # 条件1和条件2同时成立
Q(条件1)&~Q(条件2) # 条件1成立且条件2不成立
...
示例
from django.db.models import Q
# 查找清华大学出版社的书或价格低于50的书
models.Book.objects.filter(Q(market_price__lt=50) | Q(pub_house='清华大学出版社'))
# 查找不是机械工业出版社的书且价格低于50的书
models.Book.objects.filter(Q(market_price__lt=50) & ~Q(pub_house='机械工业出版社'))
使用MyModel.objects.raw()进行 数据库查询操作查询
语法:
MyModel.objects.raw(sql语句)
用法
MyModel.objects.raw('sql语句')
返回值:
示例
books = models.Book.objects.raw('select * from bookstore_book')
for book in books:
print(book)
```
使用django中的游标cursor对数据库进行 增删改操作
在Django中可以使用 如UPDATE,DELETE等SQL语句对数据库进行操作。
在Django中使用上述非查询语句必须使用游标进行操作
使用步骤:
导入cursor所在的包
from django.db import connection
用创建cursor类的构造函数创建cursor对象,再使用cursor对象,为保证在出现异常时能释放cursor资源,通常使用with语句进行创建操作
如:
from django.db import connection
with connection.cursor() as cur:
cur.execute('执行SQL语句')
示例
# 用SQL语句将id 为 10的 书的出版社改为 "XXX出版社"
from django.db import connection
with connection.cursor() as cur:
cur.execute('update bookstore_book set pub_house="XXX出版社" where id=10;')
with connection.cursor() as cur:
# 删除 id为1的一条记录
cur.execute('delete from bookstore_book where id=10;')
django 提供了比较完善的后台管理数据库的接口,可供开发过程中调用和测试使用
django 会搜集所有已注册的模型类,为这些模型类提拱数据管理界面,供开发者使用
使用步骤:
创建后台管理帐号:
后台管理–创建管理员帐号
guoxiaonao guoxiaonao123
$ python3 manage.py createsuperuser
$ python3 manage.py createsuperuser
Username (leave blank to use 'tarena'): tarena # 此处输入用户名
Email address: [email protected] # 此处输入邮箱
Password: # 此处输入密码(密码要复杂些,否则会提示密码太简单)
Password (again): # 再次输入重复密码
Superuser created successfully.
$
用注册的帐号登陆后台管理界面
若要自己定义的模型类也能在 /admin
后台管理界中显示和管理,需要将自己的类注册到后台管理界面
添加自己定义模型类的后台管理数据表的,需要用admin.site.register(自定义模型类)
方法进行注册
配置步骤如下:
在应用app中的admin.py中导入注册要管理的模型models类, 如:
from . import models
调用 admin.site.register 方法进行注册,如:
from django.contrib import admin
admin.site.register(自定义模型类)
如: 在 bookstore/admin.py 添加如下代码对Book类进行管理
示例:
# file: bookstore/admin.py
from django.contrib import admin
# Register your models here.
from . import models
...
admin.site.register(models.Book) # 将Book类注册为可管理页面
在admin后台管理数据库中对自定义的数据记录都展示为 XXXX object
类型的记录,不便于阅读和判断
在用户自定义的模型类中可以重写 def __str__(self):
方法解决显示问题,如:
class Book(models.Model):
...
def __str__(self):
return "书名" + self.title
作用:
说明:
django.contrib.admin
里的 ModelAdmin
类模型管理器的使用方法:
在 <应用app>/admin.py
里定义模型管理器类
class XXXX_Manager(admin.ModelAdmin):
......
注册管理器与模型类关联
from django.contrib import admin
from . import models
admin.site.register(models.YYYY, XXXX_Manager) # 注册models.YYYY 模型类与 管理器类 XXXX_Manager 关联
示例:
# file : bookstore/admin.py
from django.contrib import admin
from . import models
class BookAdmin(admin.ModelAdmin):
list_display = ['id', 'title', 'price', 'market_price']
admin.site.register(models.Book, BookAdmin)
模型管理器类ModelAdmin中实现的高级管理功能
修改模型类字段的显示名字
模型类各字段的第一个参数为 verbose_name,此字段显示的名字会在后台数据库管理页面显示
通过 verbose_name 字段选项,修改显示名称示例如下:
title = models.CharField(
max_length = 30,
verbose_name='显示名称'
)
通过Meta内嵌类 定义模型类的属性及展现形式
模型类可以通过定义内部类class Meta 来重新定义当前模型类和数据表的一些属性信息
用法格式如下:
class Book(models.Model):
title = CharField(....)
class Meta:
1. db_table = '数据表名'
- 该模型所用的数据表的名称。(设置完成后需要立马更新同步数据库)
2. verbose_name = '单数名'
- 给模型对象的一个易于理解的名称(单数),用于显示在/admin管理界面中
3. verbose_name_plural = '复数名'
- 该对象复数形式的名称(复数),用于显示在/admin管理界面中
语法
class A(model.Model):
...
class B(model.Model):
属性 = models.OneToOneField(A)
用法示例
创建作家和作家妻子类
# file : xxxxxxxx/models.py
from django.db import models
class Author(models.Model):
'''作家模型类'''
name = models.CharField('作家', max_length=50)
#wife 隐式定义
class Wife(models.Model):
'''作家妻子模型类'''
name = models.CharField("妻子", max_length=50)
author = models.OneToOneField(Author) # 增加一对一属性
查询
创始一对一的数据记录
from . import models
author1 = models.Author.objects.create(name='王老师')
wife1 = models.Wife.objects.create(name='王夫人', author=author1) # 关联王老师
#wife1=models.wife.objects.create(name='王夫人', author_id=author1.id)
author2 = models.Author.objects.create(name='小泽老师') # 一对一可以没有数据对应的数据
一对一数据的相互获取
正向查询
# 通过 wife 找 author
from . import models
wife = models.Wife.objects.get(name='王夫人')
print(wife.name, '的老公是', wife.author.name)
反向查询
实例对象.引用类名(小写)
,如作家的反向引用为作家对象.wife
# 通过 author.wife 关联属性 找 wife,如果没有对应的wife则触发异常
author1 = models.Author.objects.get(name='王老师')
print(author1.name, '的妻子是', author1.wife.name)
author2 = models.Author.objects.get(name='小泽老师')
try:
print(author2.name, '的妻子是', author2.wife.name)
except:
print(author2.name, '还没有妻子')
作用:
练习:
用法语法
class A(model.Model):
...
class B(model.Model):
属性 = models.ForeignKey(多对一中"一"的模型类, ...)
外键类ForeignKey
构造函数:
ForeignKey(to, on_delete, **options)
常用参数:
**options
可以是常用的字段选项如:
示例
清华大学出版社
有书
北京大学出版社
有书
定义一对多类
# file: one2many/models.py
from django.db import models
class Publisher(models.Model):
'''出版社'''
name = models.CharField('名称', max_length=50, unique=True)
class Book(models.Model):
title = models.CharField('书名', max_length=50)
publisher = models.ForeignKey(Publisher, null=True)
创建一对多的对象
# file: xxxxx/views.py
from . import models
pub1 = models.Publisher.objects.create(name='清华大学出版社')
models.Book.objects.create(title='C++', publisher=pub1)
models.Book.objects.create(title='Java', publisher=pub1)
models.Book.objects.create(title='Python', publisher=pub1)
pub2 = models.Publisher.objects.create(name='北京大学出版社')
models.Book.objects.create(title='西游记', publisher=pub2)
models.Book.objects.create(title='水浒', publisher=pub2)
查询:
# 通过一本书找到对应的出版社
abook = models.Book.objects.get(id=1)
print(abook.title, '的出版社是:', abook.publisher.name)
# 通过出版社查询对应的书
pub1 = models.Publisher.objects.get(name='清华大学出版社')
books = pub1.book_set.all() # 通过book_set 获取pub1对应的多个Book数据对象
# books = models.Book.objects.filter(publisher=pub1) # 也可以采用此方式获取
print("清华大学出版社的书有:")
for book in books:
print(book.title)
数据查询
通过 Book 查询 Publisher
通过 publisher 属性查询即可
练习:
查询 西游记 对应的出版社信息,打印在终端上
通过 Publisher 查询 对应的所有的 Books
Django会在Publisher中增加一个属性来表示对对应的Book们的查询引用
属性:book_set(MyModel.objects)
语法
属性 = models.ManyToManyField(MyModel)
示例
class Author(models.Model):
...
class Book(models.Model):
...
authors = models.ManyToManyField(Author)
数据查询
通过 Book 查询对应的所有的 Authors
book.authors.all() -> 获取 book 对应的所有的author的信息
book.authors.filter(age__gt=80) -> 获取book对应的作者中年龄大于80岁的作者的信息
通过 Author 查询对应的所有的Books
author.book_set.all()
author.book_set.filter()
author.book_set.create(...) # 创建新书并联作用author
author.book_set.add(book) # 添加已有的书为当前作者author
author.book_set.clear() # 删除author所有并联的书
示例:
class Author(models.Model):
'''作家模型类'''
name = models.CharField('作家', max_length=50)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField('书名', max_length=50)
author = models.ManyToManyField(Author)
def __str__(self):
return self.title
from django.http import HttpResponse
from . import models
def many2many_init(request):
# 创建两人个作者
author1 = models.Author.objects.create(name='吕泽')
author2 = models.Author.objects.create(name='王老师')
# 吕择和王老师同时写了一本Python
book11 = author1.book_set.create(title="Python")
author2.book_set.add(book11) #
# 王老师还写了两本书
book21 = author2.book_set.create(title="C") # 创建一本新书"C"
book22 = author2.book_set.create(title="C++") # 创建一本新书"C++"
return HttpResponse("初始化成功")
def show_many2many(request):
authors = models.Author.objects.all()
for auth in authors:
print("作者:", auth.name, '发出版了', auth.book_set.count(), '本书: ')
for book in books:
print(' ', book.title)
print("----显示书和作者的关系----")
books = models.Book.objects.all()
for book in books:
auths = book.author.all()
print(book.title, '的作者是:', '、'.join([str(x.name) for x in auths]))
return HttpResponse("显示成功,请查看服务器端控制台终端")
mysql> select * from many2many_author;
+----+-----------+
| id | name |
+----+-----------+
| 11 | 吕泽 |
| 12 | 王老师 |
+----+-----------+
2 rows in set (0.00 sec)
mysql> select * from many2many_book;
+----+--------+
| id | title |
+----+--------+
| 13 | Python |
| 14 | C |
| 15 | C++ |
+----+--------+
3 rows in set (0.00 sec)
mysql> select * from many2many_book_author;
+----+---------+-----------+
| id | book_id | author_id |
+----+---------+-----------+
| 17 | 13 | 11 |
| 20 | 13 | 12 |
| 18 | 14 | 12 |
| 19 | 15 | 12 |
+----+---------+-----------+
4 rows in set (0.00 sec)
cookies是保存在客户端浏览器上的存储空间,通常用来记录浏览器端自己的信息和当前连接的确认信息
cookies 在浏览器上是以键-值对的形式进行存储的,键和值都是以ASCII字符串的形存储(不能是中文字符串)
cookies 的内部的数据会在每次访问此网址时都会携带到服务器端,如果cookies过大会降低响应速度
在Django 服务器端来设置 设置浏览器的COOKIE 必须通过 HttpResponse 对象来完成
HttpResponse 关于COOKIE的方法
添加、修改COOKIE
删除COOKIE
Django中的cookies
使用 响应对象HttpResponse 等 将cookie保存进客户端
方法1
from django.http import HttpResponse
resp = HttpResponse()
resp.set_cookie('cookies名', cookies值, 超期时间)
resp = HttpResponse()
resp.set_cookie('myvar', "weimz", 超期时间)
方法二, 使用render对象
from django.shortcuts import render
resp = render(request,'xxx.html',locals())
resp.set_cookie('cookies名', cookies值, 超期时间)
获取cookie
通过 request.COOKIES 绑定的字典(dict) 获取客户端的 COOKIES数据
value = request.COOKIES.get('cookies名', '没有值!')
print("cookies名 = ", value)
注:
Application
>> Storage
>> Cookies
查看和操作浏览器端所有的 Cookies 值cookies 示例
以下示例均在视图函数中调用
添加cookie
# 为浏览器添加键为 my_var1,值为123,过期时间为1个小时的cookie
responds = HttpResponse("已添加 my_var1,值为123")
responds.set_cookie('my_var1', 123, 3600)
return responds
修改cookie
# 为浏览器添加键为 my_var1,修改值为456,过期时间为2个小时的cookie
responds = HttpResponse("已修改 my_var1,值为456")
responds.set_cookie('my_var1', 456, 3600*2)
return responds
删除cookie
# 删除浏览器键为 my_var1的cookie
responds = HttpResponse("已删除 my_var1")
responds.delete_cookie('my_var1')
return responds
获取cookie
# 获取浏览器中 my_var变量对应的值
value = request.COOKIES.get('my_var1', '没有值!')
print("cookie my_var1 = ", value)
return HttpResponse("my_var1:" + value)
综合练习:
实现用户注册功能,界面如下:
要求 :
python3 manage.py startapp user
模型类
用户模型类
class User(models.Model):
username = models.CharField("用户名", max_length=30, unique=True)
password = models.CharField("密码", max_length=30)
def __str__(self):
return "用户" + self.username
登陆设计规范(在user应用中写代码)
路由正则 | 视图函数 | 模板位置 | 说明 |
---|---|---|---|
/user/reg | def reg_view(request): | templates/user/register.html | 用户注册 |
什么是session
session又名会话控制,是在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据
session的起源
实现方式
Django启用Session
在 settings.py 文件中
向 INSTALLED_APPS 列表中添加:
INSTALLED_APPS = [
# 启用 sessions 应用
'django.contrib.sessions',
]
向 MIDDLEWARE_CLASSES 列表中添加:
MIDDLEWARE = [
# 启用 Session 中间件
'django.contrib.sessions.middleware.SessionMiddleware',
]
session的基本操作:
request.session['KEY'] = VALUE
VALUE = request.session['KEY']
VALUE = request.session.get('KEY', 缺省值)
del request.session['KEY']
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
import django.conf.global_settings
注: 当使用session时需要迁移数据库,否则会出现错误
模型类
用户模型类
class User(models.Model):
username = models.CharField("用户名", max_length=30, unique=True)
password = models.CharField("密码", max_length=30)
create_time = models.DateTimeField('创建时间', auto_now_add=True)
def __str__(self):
return "用户" + self.username
笔记模型类
from django.db import models
from user.models import User
class Note(models.Model):
title = models.CharField('标题', max_length=100)
content = models.TextField('内容')
create_time = models.DateTimeField('创建时间', auto_now_add=True)
mod_time = models.DateTimeField('修改时间', auto_now=True)
user = models.ForeignKey(User)
```
登陆设计规范(在user应用中写代码)
路由正则 | 视图函数 | 模板位置 | 说明 |
---|---|---|---|
/user/login | def login_view(request): | templates/user/login.html | 用户登陆 |
/user/reg | def reg_view(request): | templates/user/register.html | 用户注册 |
/user/logout | def logout_view(request): | 无 | 退出用户登陆 |
主页设计规范(在index应用中写代码)
路由正则 | 视图函数 | 模板位置 | 说明 |
---|---|---|---|
/ | def index_view(request): | templates/index/index.html | 主页 |
云笔记设计规范
路由正则 | 视图函数 | 模板位置 | 说明 |
---|---|---|---|
/note/ | def list_view(request): | templates/note/list_note.html | 显示笔记列表功能 |
/note/add | def add_view(request): | templates/note/add_note.html | 添加云笔记 |
/note/mod/(\d+) | def mod_view(request, id): | templates/note/mod_note.html | 修改之前云笔记 |
/note/del/(\d+) | def del_view(request, id): | 无(返回列表页) | 删除云笔记 |
/note/(\d+) | def show_view(request, id): | templates/note/note.html | 查看单个云笔记 |
缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存
视图渲染有一定成本,对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数
案例分析
from django.shortcuts import render
def index(request):
# 时间复杂度极高的渲染
book_list = Book.objects.filter() #-> 此处假设耗时2s
return render(request, 'index.html', locals())
优化思想
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
1,博客列表页
2,电商商品详情页
3,缓存导航及页脚
https://docs.djangoproject.com/en/1.11/topics/cache/
Django中提供多种缓存方式,如需使用需要在settings.py中进行配置
1,数据库缓存
Django可以将其缓存的数据存储在您的数据库中
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
'OPTIONS':{
'MAX_ENTRIES': 300, #当前最大缓存数
'CULL_FREQUENCY': 3 #删除频率 1/cull_frequency
}
}
}
创建缓存表
python3 manage.py createcachetable
2,文件系统缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',#这个是文件夹的路径
#'LOCATION': 'c:\test\cache',#windows下示例
}
}
3, 本地内存缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake'
}
}
在视图View中使用cache
from django.views.decorators.cache import cache_page
@cache_page(30) -> 单位s
def my_view(request):
...
在路由中使用
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/', cache_page(60)(my_view)),
]
在模板中使用
{% load cache %}
{% cache 50 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
cache_key = sidebar + username
guoxiaonao访问 时 cache_key = sidebar + guoxiaonao
浏览器缓存分类:
不会向服务器发送请求,直接从缓存中读取资源
1,Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
Expires=max-age + 请求时间 UTC绝对时间
2019 10 27 xxx
Expires 是 HTTP/1 的产物,受限于本地时间,如果修改了本地时间,可能会造成缓存失效
2, Cache-Control
在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当Cache-Control:max-age=120
代表请求创建时间后的120秒,缓存失效
横向对比 Expires VS Cache-Control
**协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
1,Last-Modified和If-Modified-Since
第一次访问时,服务器会返回
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
浏览器下次请求时 携带If-Modified-Since这个header , 该值为 Last-Modified
服务器接收请求后,对比结果,若资源未发生改变,则返回304, 否则返回200并将新资源返回给浏览器
缺点:只能精确到秒,容易发生单秒内多次修改,检测不到
2,ETag和If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成
流程同上
横向对比 Last-Modified VS ETag
中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。
每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。
他的文档解释了中间件是如何工作的,如何激活中间件,以及如何编写自己的中间件。Django 具有一些内置的中间件,你可以直接使用。它们被记录在 built-in middleware reference 中。
中间件类:
django.utils.deprecation.MiddlewareMixin
类def process_request(self, request):
执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
def process_view(self, request, callback, callback_args, callback_kwargs):
调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象def process_response(self, request, response):
所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象def process_exception(self, request, exception):
当处理过程中抛出异常时调用,返回一个HttpResponse对象def process_template_response(self, request, response):
在视图刚好执行完毕之后被调用,在每个请求上调用,返回实现了render方法的响应对象编写中间件类:
# file : middleware/mymiddleware.py
from django.http import HttpResponse, Http404
from django.utils.deprecation import MiddlewareMixin
class MyMiddleWare(MiddlewareMixin):
def process_request(self, request):
print("中间件方法 process_request 被调用")
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件方法 process_view 被调用")
def process_response(self, request, response):
print("中间件方法 process_response 被调用")
return response
def process_exception(self, request, exception):
print("中间件方法 process_exception 被调用")
def process_template_response(self, request, response):
print("中间件方法 process_template_response 被调用")
return response
# file : settings.py
MIDDLEWARE = [
...
'middleware.mymiddleware.MyMiddleWare',
]
#单个中间件输出
MyMW process_request do---
MyMW process_views do ---
----this is test cache views ----
MyMW process_response do ---
#多个中间件时 输出
MyMW process_request do---
MyMW2 process_request do---
MyMW process_views do ---
MyMW2 process_views do ---
----this is test cache views ----
MyMW2 process_response do ---
MyMW process_response do ---
练习
用中间件实现强制某个IP地址只能向/test 发送 5 次GET请求
提示:
答案:
from django.http import HttpResponse, Http404
from django.utils.deprecation import MiddlewareMixin
import re
class VisitLimit(MiddlewareMixin):
'''此中间件限制一个IP地址对应的访问/user/login 的次数不能改过10次,超过后禁止使用'''
visit_times = {} # 此字典用于记录客户端IP地址有访问次数
def process_request(self, request):
ip_address = request.META['REMOTE_ADDR'] # 得到IP地址
if not re.match('^/test', request.path_info):
return
times = self.visit_times.get(ip_address, 0)
print("IP:", ip_address, '已经访问过', times, '次!:', request.path_info)
self.visit_times[ip_address] = times + 1
if times < 5:
return
return HttpResponse('你已经访问过' + str(times) + '次,您被禁止了')
跨站请求伪造攻击
说明:
CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。
作用:
解决方案:
取消 csrf 验证(不推荐)
django.middleware.csrf.CsrfViewMiddleware
的中间件通过验证 csrf_token 验证
需要在表单中增加一个标签
{% csrf_token %}
django.core.paginator
模块中。对象的构造方法
Paginator属性
Paginator方法
Paginator异常exception
创建对象
Paginator对象的page()方法返回Page对象,不需要手动构造
Page对象属性
Page对象方法
说明:
参考文档https://docs.djangoproject.com/en/1.11/topics/pagination/
分页示例:
from django.core.paginator import Paginator
def book(request):
bks = models.Book.objects.all()
paginator = Paginator(bks, 10)
print('当前对象的总个数是:', paginator.count)
print('当前对象的面码范围是:', paginator.page_range)
print('总页数是:', paginator.num_pages)
print('每页最大个数:', paginator.per_page)
# /index?page=1
cur_page = request.GET.get('page', 1) # 得到默认的当前页
page = paginator.page(cur_page)
return render(request, 'bookstore/book.html', locals())
<html>
<head>
<title>分页显示title>
head>
<body>
{% for b in page %}
<div>{{ b.title }}div>
{% endfor %}
{# 分页功能 #}
{# 上一页功能 #}
{% if page.has_previous %}
<a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页a>
{% else %}
上一页
{% endif %}
{% for p in paginator.page_range %}
{% if p == page.number %}
{{ p }}
{% else %}
<a href="{% url 'book' %}?page={{ p }}">{{ p }}a>
{% endif %}
{% endfor %}
{#下一页功能#}
{% if page.has_next %}
<a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页a>
{% else %}
下一页
{% endif %}
总页数: {{ page.len }}
body>
html>
文件上传必须为POST提交方式
表单中文件上传时必须有带有
enctype="multipart/form-data"
时才会包含文件内容数据。
表单中用标签上传文件
xxx
对应request.FILES['xxx']
对应的内存缓冲文件流对象。可通能过request.FILES['xxx']
返回的对象获取上传文件数据file=request.FILES['xxx']
file 绑定文件流对象,可以通过文件流对象的如下信息获取文件数据上传文件的表单书写方式
<html>
<head>
<meta charset="utf-8">
<title>文件上传title>
head>
<body>
<h3>上传文件h3>
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="myfile"/><br>
<input type="submit" value="上传">
form>
body>
html>
在setting.py 中设置一个变量MEDIA_ROOT 用来记录上传文件的位置
# file : settings.py
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/files')
在当前项目文件夹下创建 static/files
文件夹
$ mkdir -p static/files
添加路由及对应的处理函数
# file urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^upload', views.upload_view)
]
上传文件的视图处理函数
# file views.py
from django.http import HttpResponse, Http404
from django.conf import settings
import os
def upload_view(request):
if request.method == 'GET':
return render(request, 'index/upload.html')
elif request.method == "POST":
a_file = request.FILES['myfile']
print("上传文件名是:", a_file.name)
filename =os.path.join(settings.MEDIA_ROOT, a_file.name)
with open(filename, 'wb') as f:
data = a_file.file.read()
f.write(data)
return HttpResponse("接收文件:" + a_file.name + "成功")
raise Http404
访问地址: http://127.0.0.1:8000/static/upload.html
后端缓存:
1, 将视图函数最终结果 转存到其他介质里
【mysql表里,文件里, 内存里】
2,解决了 views重复计算问题 【有效降低视图层时间复杂度】
3,http 1.1 cache头 触发了 浏览器强缓存
浏览器缓存:
1,带有强缓存的响应头 的响应数据,存储自己的硬盘中或内存里
2,当强缓存有数据时,可以完全不给服务器发送请求,直接读取缓存内容 【减少 浏览器与服务器之间的请求次数】
Django带有一个用户认证系统。 它处理用户账号、组、权限以及基于cookie的用户会话。
作用:
文档参见
https://docs.djangoproject.com/en/1.11/topics/auth/
User模型类
位置: from django.contrib.auth.models import User
默认user的基本属性有:
属性名 | 类型 | 是否必选 |
---|---|---|
username | 用户名 | 是 |
password | 密码 | 是 |
邮箱 | 可选 | |
first_name | 名 | |
last_name | 姓 | |
is_superuser | 是否是管理员帐号(/admin) | |
is_staff | 是否可以访问admin管理界面 | |
is_active | 是否是活跃用户,默认True。一般不删除用户,而是将用户的is_active设为False。 | |
last_login | 上一次的登录时间 | |
date_joined | 用户创建的时间 |
数据库表现形式
mysql> use myauth;
mysql> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| password | varchar(128) | NO | | NULL | |
| last_login | datetime(6) | YES | | NULL | |
| is_superuser | tinyint(1) | NO | | NULL | |
| username | varchar(150) | NO | UNI | NULL | |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| email | varchar(254) | NO | | NULL | |
| is_staff | tinyint(1) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| date_joined | datetime(6) | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)
创建用户
创建普通用户create_user
from django.contrib.auth import models
user = models.User.objects.create_user(username='用户名', password='密码', email='邮箱',...)
...
user.save()
创建超级用户create_superuser
from django.contrib.auth import models
user = models.User.objects.create_superuser(username='用户名', password='密码', email='邮箱',...)
...
user.save()
删除用户
from django.contrib.auth import models
try:
user = models.User.objects.get(username='用户名')
user.is_active = False # 记当前用户无效
user.save()
print("删除普通用户成功!")
except:
print("删除普通用户失败")
return HttpResponseRedirect('/user/info')
修改密码set_password
from django.contrib.auth import models
try:
user = models.User.objects.get(username='xiaonao')
user.set_password('654321')
user.save()
return HttpResponse("修改密码成功!")
except:
return HttpResponse("修改密码失败!")
检查密码是否正确check_password
from django.contrib.auth import models
try:
user = models.User.objects.get(username='xiaonao')
if user.check_password('654321'): # 成功返回True,失败返回False
return HttpResponse("密码正确")
else:
return HttpResponse("密码错误")
except:
return HttpResponse("没有此用户!")
Django可直接在视图函数中生成csv文件 并响应给浏览器
import csv
from django.http import HttpResponse
from .models import Book
def make_csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="mybook.csv"'
all_book = Book.objects.all()
writer = csv.writer(response)
writer.writerow(['id', 'title'])
for b in all_book:
writer.writerow([b.id, b.title])
return response
Content-Disposition
标头,其中包含CSV文件的名称。它将被浏览器用于“另存为…”对话框writer.writerow
,传递一个可迭代对象,如列表或元组。QQ邮箱->设置->帐户->“POP3/IMAP......服务”
# 发送邮件设置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 固定写法
EMAIL_HOST = 'smtp.qq.com' # 腾讯QQ邮箱 SMTP 服务器地址
EMAIL_PORT = 25 # SMTP服务的端口号
EMAIL_HOST_USER = '[email protected]' # 发送邮件的QQ邮箱
EMAIL_HOST_PASSWORD = '******' # 邮箱的授权码(即QQ密码)
EMAIL_USE_TLS = True # 与SMTP服务器通信时,是否启动TLS链接(安全链接)默认false
mail.send_mail(subject='test1111', message='123',from_email='[email protected]',recipient_list=['[email protected]'], auth_password='gjy19881227')
视图函数中
from django.core import mail
mail.send_mail(
subject, #题目
message, # 消息内容
from_email, # 发送者[当前配置邮箱]
recipient_list=['[email protected]',], # 接收者邮件列表
auth_password='xxxxxxx' # 在QQ邮箱->设置->帐户->“POP3/IMAP......服务” 里得到的在第三方登录QQ邮箱授权码
)
python3 manage.py runserver
方法启动服务器安装同版本的数据库
django 项目迁移
安装python
$ sudo apt install python3
安装相同版本的包
$ pip3 freeze > package_list.txt
$ pip3 install -r package_list.txt
将当前项目源代码复制到远程主机上(scp 命令)
$ sudo scp -a 当前项目源代码 远程主机地址和文件夹
sudo scp -a /home/tarena/django/mysite1.zip [email protected]:/home/root/xxx
请输入root密码:
使用 python manage.py runserver
通常只在开发和测试环境中使用。
当开发结束后,完善的项目代码需要在一个高效稳定的环境中运行,这时可以使用uWSGI
uWSGI是WSGI的一种,它可以让Django、Flask等开发的web站点运行其中.
安装uWSGI
在线安装 uwsgi
$ sudo pip3 install uwsgi
离线安装
在线下载安装包:
$ pip3 download uwsgi
uwsgi-2.0.18.tar.gz
离线安装
$ tar -xzvf uwsgi-2.0.18.tar.gz
$ cd uwsgi-2.0.18
$ sudo python3 setup.py install
配置uWSGI
添加配置文件 项目文件夹/uwsgi.ini
[uwsgi]
# 套接字方式的 IP地址:端口号
# socket=127.0.0.1:8000
# Http通信方式的 IP地址:端口号
http=127.0.0.1:8000
# 项目当前工作目录
chdir=/home/tarena/.../my_project 这里需要换为项目文件夹的绝对路径
# 项目中wsgi.py文件的目录,相对于当前工作目录
wsgi-file=my_project/wsgi.py
# 进程个数
process=4
# 每个进程的线程个数
threads=2
# 服务的pid记录文件
pidfile=uwsgi.pid
# 服务的目志文件位置
daemonize=uwsgi.log
修改settings.py将 DEBUG=True 改为DEBUG=False
修改settings.py 将 ALLOWED_HOSTS = [] 改为 ALLOWED_HOSTS = [‘*’]
uWSGI的运行管理
启动 uwsgi
$ cd 项目文件夹
$ sudo uwsgi --ini 项目文件夹/uwsgi.ini
停止 uwsgi
$ cd 项目文件夹
$ sudo uwsgi --stop uwsgi.pid
说明:
测试:
Nginx是轻量级的高性能Web服务器,提供了诸如HTTP代理和反向代理、负载均衡、缓存等一系列重要特性,在实践之中使用广泛。
C语言编写,执行效率高
nginx 作用
原理:
客户端请求nginx,再由nginx 请求 uwsgi, 运行django下的python代码
ubuntu 下 nginx 安装
$ sudo apt install nginx
nginx 配置
# 在server节点下添加新的location项,指向uwsgi的ip与端口。
server {
...
location / {
uwsgi_pass 127.0.0.1:8000; # 重定向到127.0.0.1的8000端口
include /etc/nginx/uwsgi_params; # 将所有的参数转到uwsgi下
}
...
}
nginx服务控制
$ sudo /etc/init.d/nginx start|stop|restart|status
# 或
$ sudo service nginx start|stop|restart|status
通过 start,stop,restart,status 可能实现nginx服务的启动、停止、重启、查扑克状态等操作
修改uWSGI配置
项目文件夹/uwsgi.ini
下的Http通信方式改为socket通信方式,如:[uwsgi]
# 去掉如下
# http=127.0.0.1:8000
# 改为
socket=127.0.0.1:8000
$ sudo uwsgi --stop uwsgi.pid
$ sudo uwsgi --ini 项目文件夹/uwsgi.ini
测试:
解决静态路径问题
# file : /etc/nginx/sites-available/default
# 新添加location /static 路由配置,重定向到指定的绝对路径
server {
...
location /static {
# root static文件夹所在的绝对路径,如:
root /home/tarena/my_django_project; # 重定向/static请求的路径,这里改为你项目的文件夹
}
...
}
修改配置文件后需要重新启动 nginx 服务
在模板文件夹内添加 404.html 模版,当视图触发Http404 异常时将会被显示
404.html 仅在发布版中(即setting.py 中的 DEBUG=False时) 才起作用
当向应处理函数触发Http404异常时就会跳转到404界面
from django.http import Http404
def xxx_view(request):
raise Http404 # 直接返回404