1、django的简介
浏览器与服务器之间遵循的一个协议: HTTP协议
服务器与应用程序框架之间:wsgi
1.1 Django的简介
Django是一个重量型框架
主要目的:简便快捷开发
Django基于MVC模式,但是它是MVT模式的
核心:解耦(高内聚,低耦合)
MVC设计的框架
(1)重量级框架
(2)MVT模式
MVC :
定义:
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
2、创建项目的环境
虚拟环境:
项目所需要的第三方软件环境相互隔离
安装虚拟环境
sudo pip3 install virtualenv
sudo pip3 install virtualenvwrapper
创建虚拟环境
mkvirtualenv dj_3 -p python
切换虚拟环境
workon dj_3
删除虚拟环境
rmvirtualenv dj_3
退出环境
deactivate
3、安装Django,初步讲解
3.1安装Django:
pip install django==安装的版本号
一般安装在虚拟环境中
3.2创建项目:
django-admin startproject 项目名称
里面有一个和项目同名的文件夹,内部包含的是项目的配置文件
settings.py项目的整体配置文件
urls.py项目的URL配置文件
wsgi.py是项目与服务器的入口
manage.py是项目运行的入口
3.3 运行服务器
python manage.py runserver
注意点:如果不指定端口,默认是8000
python manage.py runserver 5000
3.4 成功的界面:
3.5 创建模块
python manage.py startapp 模块名
user子模块
admin.py 后台管理站点的配置
apps.py 当前子应用的信息
models.py 保存书写的数据库模型
text.py 用来保存开发测试用例的,进行单元测试
views.py 书写逻辑,用来保存视图。
3.6 创建模块之后,注册子模块,注册到同名文件夹的setting中
pai0805中的settings.py
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"user.apps.UserConfig"
]
3.7 简单体验数据库交互
3.7.1 定义表与字段
一张表对应了一个类
user中models.py
class UserInfo(models.Model):
name = models.CharField(max_length=10)
age = models.IntegerField()
phone = models.CharField(max_length=11)
def __str__(self):
return self.name
类名对应的是表名
属性名对应的是字段名
注意点:继承于Model
3.7.2 迁移数据库表
1、生成迁移文件
python manage.py makemigration
2、执行迁移文件
pyhton manage.py migrate
迁移成功并且执行迁移之后,才真正在数据库产生表
3.7.3 创建超级权限账号和修改界面语言
pai0805中的settings.py
LANGUAGE_CODE = "zh-Hans"
TIME_ZONE = "Asia/Shanghai"
python manage.py createsuperuser
3.7.4 数据库的配置
1、设置数据库
pai0805中的settings.py
DATABASES={
"default":{
"ENGINE":"django.db.backends.mysql",#这个参数是设置数据库
"NAME":"数据库名字",#使用的数据的名字
"USER":"root",#数据库的用户名
"PASSWORD":"mysql",
"HOST":"127.0.0.1",#数据库主机的地址
"PORT":3307,#数据库的端口
}
}
2、添加驱动
pai0805中的 __init__.py
import pymysql
pymysql.install_as_MySQLdb()
3、在admin站点管理中注册models
user 中 admin.py
from django.contrib import admin
from user.models import UserInfo #导入
admin.site.register(UserInfo)
3.7.5 查看
use pai0805;
show tables;
其中user_userinfo表是创建的
3.8 实现网站的用户信息展示
3.8.1 定义视图,处理业务
user中view.py
视图的作业:创建业务逻辑,完成路径--视图的匹配,返回给对应路径的数据或者网页
视图函数
def userinfo():
"""这个视图函数实现的是暂时用户的信息,匹配的路径/userinfo"""
pass
def index(request):
print("----------------------")
print(type(request))
print(request)
# return "hello world"
return HttpResponse("hello world")
Request是一个请求对象,可以是别的名字,但是一般是request,每一个视图函数都必须要有一个参数来承接
报错:
注意点:视图函数必须要有一个响应对象返回
视图函数,至少需要一个参数来接收Django传递过来的参数
3.8.2 配置路由URL
1、路由配置第一种方法:
from user import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r"^index/$",views.indesx),
]
2、第二种方法(分模块再集中)
(1)在项目所在的同名文件夹的urls里面配置include()将各个模块的URL加载
(2)在各个模块的URL里面进行配置URL-----VIEWS
(3)项目同名的urls值完成路由的分发匹配过程
真正匹配路径在各个模块的URL下面
视图与路径的匹配
3.9 配置html模板文件
3.9.1 创建一个名字叫templates的文件夹
3.9.2 配置文件夹的查找路径
在同名文件夹中settings.py中的
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
#设置模板的查找路径
'DIRS': [os.path.join(BASE_DIR,"templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
打印路径:
3.9.3 返回模板到前端
def show(request):
# #创建连接
# con = pymysql.connect(host="localhost", user="root", password="535897", database="pai0805_2", port=3307,charset="utf8")
# #获取光标对象,操作句柄
# cur=con.cursor()
# #写查询语句
# sql1="select * from user_userinfo;"
# #进行查询
# ret_num1 =cur.execute(sql1)
# ret = cur.fetchall()
# return HttpResponse(ret)
from user.models import UserInfo
data = UserInfo.objects.all()
for i in data:
print(i)
#可以在info.html中设置界面的字体,格式
return render(request,"info.html",{"data":data[0],"data1":data[1]})
3.10 查看数据库的日志
sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
#修改mysql中的配置文件让日志可以查看
将
general_log_file = /var/log/mysql/mysql.log
general_log = 1
前面的#除去
sudo service mysql restart
#重启mysql服务器
sudo tail -f /var/log/mysql/mysql.log
最后打开就能开启日志
3.11 表中html文件和静态文件
在settings.py中
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
#设置模板的查找路径在templates中
'DIRS': [os.path.join(BASE_DIR,"templates")],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static")
]
#静态文件包括js,图片,css文件
#设置静态文件模板的查找路径在static中
- 模型的增删改查(ORM模块)
4.1 数据的查询
4.1.1 查询所有的数据
from user.models import UserInfo
data = UserInfo.objects.all()
格式: 模型类名.objects.all()
查询多条数据
4.1.2 按照指定条件查询
def get(self,*args,**kwargs):
注意点:
1、get()获取的是指定条件符合数据,只有一条
4.1.3 计数语句
data = Userinfo.objects.count()
返回的是数据的条数
4.1.4 符合条件的多条数据查询
1、比较查询
符合条件的多条语句
1、比较查询 大于 gt greater then
data = UserInfo.objects.filter(id_gt = 1)
id大于等于3 gte
data = UserInfo.objects.filter(id_gte = 3)
id小于1的人
data = UserInfo.objects.filter(id_lt=1)
id小于等于1的人
data = UserInfo.objects.filter(id_lte=1)
注意点:
filter既可以查询一条语句,也可以查询多条语句
但是查询的结果放在一个列表里面、
如果查询不到任何满足条件的数据,则返回的是一个空列表
2、模糊查询
查找名字中带有王的人
data = UserInfo.objects.filter(name_contains = "王")
查找姓王的人
data = UserInfo.objects.filter(name_startswith = "王")
4.1.5 关于外键的查询
UserInfo.objects.all()
add.userinfo_set.all()
#未知
4.2 数据的增加
4.2.1 第一种方式
通过创建对象,进行属性设置,最后再保存的方式
book1 = BookInfo(booknane="红楼梦",time="1700-1-1")
book1.save()
4.2.2 第二种方式(重点)
PersonInfo.objects.create(name="猴子",weapon="棒子",book=book1[0])
4.3 当数据表存在数据的时候,对于修改数据表
第一种
book2 = BookInfo.objects.get(pk=3)
book2.time="1973-1-1"
book2.save()
第二种
BookInfo.objects.filter(name="三国演绎").update(time="1973-10-01")
4.4关于数据库表名的设计
如果没有指定数据库的表名,则MYSQL中会以应用名_模型类名的小写作为数据库的表名
设计数据库表名:
在模型类的内部进行表的设计
class PersonInfo(models.Model):
gender_choice = (
(0,"boy"),
(1,"girl"),
)
#名字
name = models.CharField(max_length=30,verbose_name="人物名字")
# 性别
gender = models.SmallIntegerField(choices=gender_choice,default=0,verbose_name="性别")
# 武器与技能
weapon = models.CharField(max_length=300, verbose_name="武器")
# 逻辑删除
is_delete = models.BooleanField(default=False, verbose_name="逻辑删除")
book = models.ForeignKey(BookInfo,on_delete=models.DO_NOTHING,verbose_name="外间图书")
def __str__(self):
return self.name
class Meta:
db_table = "personinfo"
#指定数据库的表名
verbose_name = "人物信息"
#修改后台管理中心中数据库表的名字
verbose_name_plural=verbose_name
#将复数形式去除
注意点:
修改数据库的表名需要迁移
4.5 关于模型与数据库数据的关系
from book.models import BookInfo,PersonInfo#测试的时候,需要将使用到的模型导入
add = BookInfo(name="书名",time="出版")#数据库的一条数据,其实就是模型类的一个实例
add.save()#保存,不保存不生效
当存在外键的时候:
person1 = PersonInfo()
person1.name="名字"
person1.book = add
person1.save()
Addinfo这个属性是一个外键,赋值的时候需要以对象进行赋值
4.6 关于orm模块设置数据类型
约束
primary_key = False 主键
null = False 允许为空,默认为false,数据库的角度来说的
blank = False 字段允许为空白,表单验证
db_column = None字段的名称,没有指定的话,使用属性的名字
db_index=Flase给该字段设置索引
unique =Flase 说明该字段在这个表里面具有唯一性
delfault = 设置默认值
例子:设置字段名字
4.7 模型的相关字段
name = models.CharField(db_column="username",max_length=20)
phone = models.CharField(max_length=11,unique=True)
pwd = models.CharField(max_length=8,default="123")
pwd= mdoels.IntegerField(default="123")
models.AutoField()如果不指定,则自动指定属性名为id的自增长属性,一般不指定
models.BooleanField()布尔值
models.NullBolleanField()布尔值加null
models.TextField()大文本字段,字符在4000向上
models.DecimalField(max_digits=总位数,decimal_places=小数位数)#十进制浮点型
models.DateField()表示的是日期
models.TimeField()表示的是时间
models.DateTimeField()表示的是日期加时间
models.FileField()上传文件的字段
models.ImageField()对上传的文件进行校验,保证是有效图片
4.8 重写父类的manager方法
4.8.1重写父类的manager方法
class BookManager(models.Manager):
#重写管理器方法
def all(self):
return super().filter(is_delete=False)
def mycreate(self,name,time):
'''
自定义创建一个新的模型对象
:return:
'''
print("这是进行定义的创建方法")
print(self.model)
obj=self.model()
obj.name=name
obj.time=time
obj.save()
return obj
# book=BookInfo()
# book.name="123"
# book.time="2019-9-28"
# book.save()
4.8.2 在对应的模型类里面进行注册(实例化)
new_objects = BookManager()
4.8.3 进行使用
BookInfo.new_objects.mycreate("123","2019-9-28")
4.9 重写all方法
class BookManager(models.Manager):
#重写管理器方法
def all(self):
return super().filter(is_delete=False)
def mycreate(self,name,time):
'''
自定义创建一个新的模型对象
:return:
'''
print("这是进行定义的创建方法")
print(self.model)
obj=self.model()
obj.name=name
obj.time=time
obj.save()
return obj
# book=BookInfo()
# book.name="123"
# book.time="2019-9-28"
# book.save()
class BookInfo(models.Model):
# 图书名
name = models.CharField(max_length=20,verbose_name="书籍名字")
# 出版时间
time = models.DateField(verbose_name="出版时间",null=True)
"""
auto_now=False,表示每次保存对象时,自动设置该字段为当前时间,用于作最后一次修改的时间
auto_now_add=False表示当前对象第一次被创建自动设置当前时间,用于创建的时间戳
当前两个参数是相互排斥的
"""
# 阅读量
count = models.IntegerField(verbose_name="阅读量",default=0)
# 销量
seltcount = models.IntegerField(verbose_name="销量",default=0)
# 逻辑删除
is_delete = models.BooleanField(default=False,verbose_name="逻辑删除")
class Meta:
db_table = "bookinfo"
#指定数据库的表名
verbose_name = "书籍"
#修改后台管理中心中数据库表的名字
verbose_name_plural=verbose_name
#将复数形式去除
def __str__(self):
return self.name
new_objects = BookManager()
#创建一个新的实例化对象
4.12 前后端不分离
html文件
<script src="/static/jquery-3.1.1.js"></script>
<script>
$(function() {
$(".box2").click(function () {
console.log("abc");
$(this).siblings(".box4").toggle();
});
$(".box1").click(function () {
$(".box6").toggle()
})
})
</script>
<style>
.box2{
}
.box4{
display: none;
}
.box6{
display: none;
}
</style>
<body>
<button class="box1">查询</button>
<div class = "box6">
{% for i in data %}
<ul>{{ i }} <button class="box2">详情</button><a href="http://127.0.0.1:5000/book/shanchu/id={{ i.id }}"><button>删除</button></a>
<li class="box4">{{ i.name }}</li>
<li class="box4">{{ i.time }}</li>
<li class="box4">{{ i.seltcount }}</li>
<li class="box4">{{ i.count }}</li>
</ul>
{% endfor %}
</div>
views文件
def bookinfo4(request):
books = BookInfo.new_objects.all()
# books = dict(i.name:i for i in books)
context = {"data":books}
#将列表转换成字典
print(context)
print(type(books))
return render(request,"chaxun.html",context)
def shanchu(request,id=None):
print(request)
print(id)
book = BookInfo.new_objects.get(id=id)
print(book)
print(book.is_delete)
# book.is_delete = True
# book.save()
book.delete()
return HttpResponse("ok")
5、视图与模型的结合使用
以简单的用户注册为例
5.1 设计展示用户操作页面
<form action="http://127.0.0.1:5000/huoqu" method="get">
请输入用户名:<input type="text" name="username">
请输入用户密码:<input type="password" name="password">
<input type="submit">
</form>
5.2 设置页面对应的路径,当用户请求的时候,展示
注意点:视图函数至少有一个参数
必有要有响应对象返回
return HttpResponse("ok")
5.3 HTTP请求对象
def index(request):
print(request)
return HttpResponse("123")
注意点:
1、这是一个HTTP请求对象
2、这个请求对象里面封装的是请求信息,包括请求的方式,数据等
5.3.1 request.body
当非表单数据,request.body 来获取到请求体数据,但是,body获取的数据是bytes类型
def index(request):
print(request.body)
return HttpResponse("123")
一般情况下,都是json
5.3.2 request.GET
获取到的都是类字典类型
<script src="/static/jquery-3.1.1.js"></script>
<script>
$(function () {
$(".btn").click(function () {
console.log("123");
var username = $("#username").val();
var password = $("#password").val();
console.log(username,password);
var data = {
"username":username,
"password":password
};
$.ajax({
url:"http://127.0.0.1:5000/username/huoqu1/",
type:"GET",
data:data,
dataType:"json",
success:function () {
console.log("666666")
}
})
})
})
</script>
<body>
请输入用户名:<input type="text" name="username" id="username">
请输入用户密码:<input type="password" name="password" id="password">
<button class="btn">提交</button>
注意点:会友crsf问题解决方法,注释掉
# 'django.middleware.csrf.CsrfViewMiddleware',
或者在html文件中加上{% crsf %}
5.3.3 request.POST
获取到的都是类字典类型
<script src="/static/jquery-3.1.1.js"></script>
<script>
$(function () {
$(".btn").click(function () {
console.log("123");
var username = $("#username").val();
var password = $("#password").val();
console.log(username,password);
var data = {
"username":username,
"password":password
};
{#data = json.stringify(data);#}
{#$.ajax({#}
{# 这是ajax的提交路径 #}
{# url:"http://127.0.0.1:5000/username/huoqu1/",#}
{# type:"POST",#}
{# 这是ajax的请求防护斯 #}
{# data:data,#}
{# 这是携带的数据 #}
{# dataType:"json",#}
{# #}
{# success:function () {#}
{# console.log("6666")#}
{# },#}
{# error:function () {#}
{# console.log("9999")#}
{# }#}
})
})
</script>
<body>
请输入用户名:<input type="text" name="username" id="username">
请输入用户密码:<input type="password" name="password" id="password">
<button class="btn">提交</button>
5.3.4 request.META获取请求头的数据
def index(request):
print(request.META)
requset.META这个属性,用来获取的是请求头header部分所有的数据
他是一个字典类型
5.4 httpresponse对象
5.4.1 render函数的具体使用
def render(request, template_name, context=None, content_type=None, status=None, using=None):
"""
Returns a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
注意点:
1、 Render函数适用的范围是:有网页,有填充网页的数据
2、 Render这个函数的第一个参数是当前路径的请求对象
3、 Context这个参数指向的是填充网页的数据,注意它的格式必须是字典
5.4.2 HTTPresponse
def __init__(self, content_type=None, status=None, reason=None, charset=None):
# _headers is a mapping of the lower-case name to the original case of
# the header (required for working with legacy systems) and the header
# value. Both the name of the header and its value are ASCII strings.
self._headers = {}
self._closable_objects = []
# This parameter is set by the handler. It's necessary to preserve the
# historical behavior of request_finished.
self._handler_class = None
self.cookies = SimpleCookie()
self.closed = False
if status is not None:
try:
self.status_code = int(status)
except (ValueError, TypeError):
raise TypeError('HTTP status code must be an integer.')
if not 100 <= self.status_code <= 599:
raise ValueError('HTTP status code must be an integer from 100 to 599.')
self._reason_phrase = reason
self._charset = charset
if content_type is None:
content_type = '%s; charset=%s' % (settings.DEFAULT_CONTENT_TYPE,
self.charset)
self['Content-Type'] = content_type
注意点:
1、适用范围,返回的是让浏览器或者客户端展示的字符串
5.5 csrf的问题
5.5.1 在表单中增加
<form action="http://127.0.0.1:5000/username/huoqu" method="get">
请输入用户名:<input type="text" name="username">
请输入用户密码:<input type="password" name="password">
<input type="submit">
</form>
5.6 关于路径匹配的补充知识
5.6.1 路径匹配
url(r"^userna(\w+)/",views.username),
url(r"^huoqu/",views.huoqu),
Exception Value:
username() takes 1 positional argument but 2 were given
URL函数正则匹配,如果需要匹配组,则匹配出来的组信息,会自动传递给对应的视图函数
url(r"^userna(\w+)/"[0],views.username),
5.6.2 关键字参数
urlpatterns = [
url(r"(?P<city>city)/(?P<cname>\w+)/",views.username),
url(r"^huoqu/",views.huoqu),
url(r"^search/$",views.search),
url(r"^data/$",views.data),
]
def username(request,city,cname):
# #创建连接
# con = pymysql.connect(host="localhost", user="root", password="535897", database="pai0805_2", port=3307,charset="utf8")
# #获取光标对象,操作句柄
# cur=con.cursor()
# #写查询语句
# sql1="select * from user_userinfo;"
# #进行查询
# ret_num1 =cur.execute(sql1)
# ret = cur.fetchall()
# return HttpResponse(ret)
print(city)
print(cname)
return render(request,"register.html")
注意点:?P
5.7 查询对象的获取
try:
book = BookInfo.objects.get(name="三国演绎")
except Exception as e:
print(e)
#通过书籍查找该书籍查找该书籍中的人物
print(book)
peo = book.personinfo_set.all()
print(peo)
obj = PersonInfo.objects.get(name="猴子")
print(obj.book.id)
print(type(obj.book))
#关联过滤查询
book_obj = BookInfo.objects.filter(personinfo__name__exact="猴子")
print(book_obj)
#关联模型类的小写--属性名--条件筛选运算符=值
per_obj = PersonInfo.objects.filter(book__seltcount__gt=0)
print(per_obj)
注意点:
1、request。GET\POST 获取的都是QUERYDICT对象
2、获取QueryDict的某一个值对应的对象,使用的是get ,但是如果一个键名对应多个值的时候,get只能获取一个,并且是最后一个
3、如果想要获取多个值,需要使用的是getlist
4、get \getlist 都可以设置默认值,当获取不到数据的时候(当键名不存在获取或者getlist获取到空列表的时候)
5.8 form表单的提取
注意点:
当name值相同的时候,可以设置value的值
5.9 关于浏览器与服务器之间的状态保持
浏览器与服务器之间是无状态的
Ssession:存在服务器一端
Cookie:存在浏览器一端
关系:一一对应的
5.9.1 cookie
特点:
1、键值结构
2、cookie是基于域名安全的,不同域名之间cookie是不可以进行相互的访问的
3、当浏览器请求某一个网站,会将与当前网站所有的cookie提交给服务器
4、cookie数据不允许存储敏感信息,密码,支付宝密码
5.9.2 cookie的设置
设置完成之后:
创建cookie
Response.set_cookie()
删除cookie
Resposne.delete_cookie()
读取cookie
Request.COOKIES.get(键名)
Cookie虽然存储在浏览器一端,但是,它是在服务器一端进行设置的,
5.9.2 session
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
1、如果需要使用数据库存储session,安装应用
'django.contrib.sessions',
2、在数据库中
注意点:在Django项目中,session的引擎没有设置,因为则是默认的存储方式
5.10 一些查询和判断
Bookinfo.objects.exclude(id__exact=1)
查询的是book表中id不等于1的对象返回的是一个queryset
判断查询集中是否有数据
Bookinfo.objects.exclude(id__exact=1).length
重用查询集
使用books=BookInfo.objects.all()
5.11 中间件的使用
5.11.1创建一个中间件
def my_middleware(get_response):
print("init被调用")
def middleware(request):
print("before request")
response = get_response(request)
print("after response")
return response
return middleware
5.11.2注册进settings中
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"book.middleware.my_middleware",#注册进settings中
]
5.11.3 中间件的顺序
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"book.middleware.my_middleware",
]
在处理request是先从上往下
处理完request之后返回response时从下往上
5.12 类视图
需要先导包
from django.views.generic import View
然后创建类
class Showusers(View):
def get(self,request):
return HttpResponse("这是get方法")
def post(self,request):
return HttpResponse("这是post方法")
在URL中的设置
url("^users/$",views.Showusers.as_view()),
之后再postman之中
使用不同的连接方式
会显示不同的内容
6 DRF 架构的使用
6.1 restful风格设计
域名:尽量的将API部署到专用的域名下面
http://api.example.com
版本:将版本号放入到url中
http://passport.baidu.com/v2/api/?login
路径:
资源作为网址一般情况下只能由名词
GET /goods 展示所有的商品(列表页)
GET /goods/id (详情页)
PUT /goods/9 (更新某一个商品) 6.2 安装第三方包
pip install djangorestframework
6.3 添加应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"user.apps.UserConfig",
"book.apps.BookConfig",
"username.apps.UsernameConfig",
"rest_framework",
]
6.4 序列化与反序列化
将模型数据转化成json数据 — 序列化
from rest_framework import serializers
from book.models import BookInfo
class UserSeralizer2(serializers.Serializer):
name = serializers.CharField(max_length=20)
time = serializers.DateField()
count = serializers.IntegerField()
seltcount = serializers.IntegerField()
is_delete = serializers.BooleanField()
def get(self,request):
user_obj = BookInfo.new_objects.get(id=1)
print(user_obj)
ser1_obj = UserSeralizer(user_obj)
return JsonResponse(ser1_obj.data)
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
将接收到的json数据转化成模型 — 反序列化
# user_obj = BookInfo.new_objects.get(id=1)
# print(user_obj)
# ser1_obj = UserSeralizer(user_obj)
# serializer = UserSeralizer(data=ser1_obj.data)
# print(serializer.is_valid())
# # True
# print(serializer.validated_data)
# return JsonResponse(serializer.validated_data, safe=False)
6.5 保存实例
如果我们希望能够基于经过验证的数据返回完整的对象实例,则需要实现.create()和.update()方法之一或两者。
如果您的对象实例与Django模型相对应,则还需要确保这些方法将对象保存到数据库中。例如,如果Comment是Django模型,则方法可能如下所示:
def create(self, validated_data):
book_obj = BookInfo.objects.create(**validated_data)
return book_obj
def update(self, instance, validated_data):
instance.name = validated_data.get("name")
instance.time = validated_data.get("time")
instance.count = validated_data.get("count")
instance.seltcount = validated_data.get("seltcount")
instance.is_delete = validated_data.get("is_delete")
instance.save()
return instance
现在,在对数据进行反序列化时,我们可以.save()基于已验证的数据调用以返回对象实例。
def put(self,request):
user_obj_1 = BookInfo.new_objects.get(id=8)
json_str = request.body.decode()
print(json_str)
# import json
# json_str1 = json.load(json_str)
# print(json_str1)
json_list = json_str.split("------")[1:6]
print(json_list)
param_list = []
for each in json_list:
# print(each.split("\r\n"))
param_list.append(each.split("\r\n")[3])
print(param_list)
query_dict = {"name":param_list[0],"time":param_list[1],"count":param_list[2],"seltcount":param_list[3],"is_delete":param_list[4]}
print(query_dict)
ser_2 = UserSeralizer(user_obj_1,data=query_dict)
ser_2.is_valid()
ser_2.save()
return HttpResponse("更新")
调用.save()将创建一个新实例,或更新一个现有实例,具体取决于在实例化序列化程序类时是否传递了一个现有实例:
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
无论是.create()和.update()方法是可选的。根据序列化程序类的用例,您既可以实现它们之一,也可以两者都不实现。
将其他属性传递给 .save()
有时,您希望视图代码能够在保存实例时注入其他数据。这些附加数据可能包括诸如当前用户,当前时间或不属于请求数据一部分的任何其他信息。
您可以通过在调用时包含其他关键字参数来实现.save()。例如:
serializer.save(owner=request.user)
6.6验证方式
反序列化数据时,您始终需要先调用,is_valid()然后再尝试访问经过验证的数据或保存对象实例。如果发生任何验证错误,则该.errors属性将包含一个字典,代表产生的错误消息。例如:
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
6.6.1 现场级验证
from rest_framework import serializers
from book.models import BookInfo
class UserSeralizer2(serializers.Serializer):
name = serializers.CharField(max_length=20)
time = serializers.DateField()
count = serializers.IntegerField()
seltcount = serializers.IntegerField()
is_delete = serializers.BooleanField()
def validate_count(self, value):
print("1")
if value == 0:
return value
else:
raise serializers.ValidationError("4564756")
6.6.2 对象级验证
from rest_framework import serializers
from book.models import BookInfo
class UserSeralizer2(serializers.Serializer):
name = serializers.CharField(max_length=20)
time = serializers.DateField()
count = serializers.IntegerField()
seltcount = serializers.IntegerField()
is_delete = serializers.BooleanField()
def validate(self, attrs):
print("***************************")
print("2")
print(attrs)
import re
if len(attrs["name"])>12 or attrs["count"]>=200 or re.search(r"^[^4]]",attrs["name"]):
raise serializers.ValidationError("4564756")
return attrs
6.6.3 验证者
序列化程序上的各个字段可以包含验证器,方法是在字段实例上声明它们,例如:
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
UniqueValidator
该验证器可用于unique=True对模型字段实施约束。它带有一个必需的参数和一个可选的messages参数:
- queryset 必需 -这是应针对其强制执行唯一性的查询集。
- message -验证失败时应使用的错误消息。
- lookup-用于查找具有已验证值的现有实例的查找。默认为’exact’。
该验证器应应用于序列化器字段,如下所示:
from rest_framework.validators import UniqueValidator
slug = SlugField(
max_length=100,
validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)
UniqueTogetherValidator
该验证器可用于unique_together对模型实例施加约束。它具有两个必需参数和一个可选messages参数:
- queryset 必需 -这是应针对其强制执行唯一性的查询集。
- fields 必填 -字段名称的列表或元组,应组成唯一的集合。这些必须作为字段存在于序列化程序类中。
- message -验证失败时应使用的错误消息。
验证器应应用于序列化器类,如下所示:
from rest_framework.validators import UniqueTogetherValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
# ToDo items belong to a parent list, and have an ordering defined
# by the 'position' field. No two items in a given list may share
# the same position.
validators = [
UniqueTogetherValidator(
queryset=ToDoItem.objects.all(),
fields=['list', 'position']
)
]
6.7 ModelSerializer
通常,您会需要与Django模型定义紧密映射的序列化程序类。
本ModelSerializer类提供了一个快捷方式,可以让你自动创建一个Serializer类对应于模型字段的字段。
该ModelSerializer班是一样的常规Serializer类,不同之处在于:
- 它将根据模型自动为您生成一组字段。
- 它将自动为序列化器生成验证器,例如unique_together验证器。
- 它包括简单的默认实现.create()和.update()。
声明ModelSerializer如下所示:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
默认情况下,该类上的所有模型字段都将映射到相应的序列化器字段。
任何关系(例如模型上的外键)都将映射到PrimaryKeyRelatedField。默认情况下不包括反向关系,除非按照序列化器关系文档中的指定明确包含反向关系。
检查一个 ModelSerializer
序列化程序类会生成有用的详细表示形式字符串,使您可以全面检查其字段的状态。这ModelSerializers在您要确定要为您自动创建哪些字段和验证器的地方工作时特别有用。
为此,请使用打开Django Shell,python manage.py shell然后导入序列化程序类,实例化它,然后打印对象表示形式…
>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
指定要包括的字段 如果只希望在模型序列化器中使用默认字段的子集,则可以使用fields或exclude选项,就像使用一样ModelForm。强烈建议您使用fields属性显式设置应序列化的所有字段。这将减少在模型更改时导致意外暴露数据的可能性。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
您也可以将fields属性设置为特殊值,'all’以指示应使用模型中的所有字段。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
您可以将exclude属性设置为要从序列化器中排除的字段列表。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
在上面的例子中,如果Account模型具有3个字段account_name,users和created,这将导致在字段account_name和created被序列化。
fields和exclude属性中的名称通常会映射到模型类上的模型字段。
另外,fields选项中的名称可以映射到属性或方法,这些属性或方法不包含模型类上存在的参数。
从3.3.0版开始,必须提供属性fields或之一exclude。 6.8 基于类的视图
REST框架提供了一个APIView类,该类继承了Django的View类。
APIView类与常规View类在以下方面有所不同:
- 传递给处理程序方法的请求将是REST框架的Request实例,而不是Django的HttpRequest实例。
- 处理程序方法可能返回REST框架的Response而不是Django的HttpResponse。该视图将管理内容协商并在响应上设置正确的渲染器。
- 任何APIException异常都将被捕获并调解为适当的响应。
- 在将请求分派到处理程序方法之前,将对传入的请求进行身份验证并进行适当的权限和/或限制检查。
使用APIView该类与使用常规View类几乎相同,通常,传入的请求将分派到适当的处理程序方法,例如.get()或.post()。另外,可以在控制API策略各个方面的类上设置许多属性。
6.9 通用视图
基于类的视图的主要优点之一是它们允许您组成一些可重用行为的方式。REST框架通过提供许多提供常用模式的预构建视图来利用此优势。
REST框架提供的通用视图使您可以快速构建与数据库模型紧密映射的API视图。
如果通用视图不符合您的API的需求,则可APIView以下拉到使用常规类,或者重用通用视图使用的mixins和基类来组成自己的可重用通用视图集。
6.9.1 API参考
GenericAPIView 该类扩展了REST框架的APIView类,为标准列表和详细信息视图添加了通常需要的行为。
提供的每个具体的通用视图都是通过将GenericAPIView,和一个或多个mixin类组合而构建的。
属性 基本设定:
以下属性控制基本视图行为。
queryset-应该用于从该视图返回对象的查询集。通常,您必须设置此属性或重写get_queryset()方法。如果要覆盖视图方法,则必须进行调用get_queryset()而不是直接访问此属性,这queryset将被评估一次,并且这些结果将被缓存用于所有后续请求,这一点很重要。 serializer_class-应该用于验证和反序列化输入以及序列化输出的序列化器类。通常,您必须设置此属性或重写get_serializer_class()方法。 lookup_field-应该用于执行单个模型实例的对象查找的模型字段。默认为’pk’。请注意,使用超链接的API时,您需要确保双方的API意见和串行类设置查找字段,如果你需要使用一个自定义值。 lookup_url_kwarg-用于对象查找的URL关键字参数。URL conf应包含与此值相对应的关键字参数。如果未设置,则默认使用与相同的值lookup_field。 分页:
与列表视图一起使用时,以下属性用于控制分页。
pagination_class-对列表进行分页时应使用的分页类。默认值为与DEFAULT_PAGINATION_CLASS设置相同的值’rest_framework.pagination.PageNumberPagination’。设置pagination_class=None将禁用此视图上的分页。 筛选:
filter_backends-应该用于过滤查询集的过滤器后端类的列表。默认值为与DEFAULT_FILTER_BACKENDS设置相同的值。 方法 基本方法:
get_queryset(self) 返回应用于列表视图的查询集,该查询集应用作详细视图中的查询的基础。默认情况下返回queryset属性指定的查询集。
应始终使用此方法,而不是self.queryset直接访问此方法,因为它self.queryset只会被评估一次,并且那些结果将为所有后续请求缓存。
可以重写以提供动态行为,例如返回特定于发出请求的用户的查询集。
class GericInfo1(generics.ListCreateAPIView):
queryset = BookInfo.new_objects.all()
serializer_class = UserSeralizer
6.10 视图集
Django REST框架允许您在称为的单个类中将一组相关视图的逻辑组合在一起ViewSet。在其他框架中,您可能还会发现概念上类似的实现,这些实现的名称类似于“资源”或“控制器”。
甲ViewSet类只是一种类型的基于类的视图,即不提供任何方法的处理程序,例如.get()或.post(),而是提供操作,如.list()和.create()。
使用方法,a的方法处理程序ViewSet仅在完成视图时绑定到相应的动作.as_view()。
通常,您将向路由器集注册视图集,而不是在urlconf中的视图集中显式注册视图,而是自动为您确定urlconf。
例 让我们定义一个简单的视图集,可用于列出或检索系统中的所有用户。
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving users.
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
如果需要,可以将此视图集绑定到两个单独的视图中,如下所示:(方法一)
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
通常,我们不这样做,而是向路由器注册视图集,并允许自动生成urlconf。(方法二)
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls
您通常不希望编写自己的视图集,而是要使用提供默认行为集的现有基类。例如:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
与使用ViewSet类相比,使用类有两个主要优点View。
- 重复的逻辑可以合并为一个类。在上面的示例中,我们只需要指定queryset一次,它将在多个视图中使用。
- 通过使用路由器,我们不再需要自己处理URL conf。
两者都需要权衡。使用常规视图和URL conf更加明确,并给您更多的控制权。如果您想快速启动并运行,或者当您使用大型API并希望在整个过程中实施一致的URL配置,则ViewSets很有帮助。
6.10.1ViewSet操作
REST框架随附的默认路由器将为一组标准的创建/检索/更新/销毁样式操作提供路由,如下所示:
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
内省ViewSet动作
在分发期间,可以使用以下属性ViewSet。
- basename -用于创建的URL名称的基础。
- action-当前动作的名称(例如list,create)。
- detail -布尔值,指示是否为列表视图或详细信息视图配置了当前操作。
- suffix-视图集类型的显示后缀-镜像detail属性。
- name-视图集的显示名称。此参数与互斥suffix。
- description -视图集单个视图的显示描述。
您可以检查这些属性以根据当前操作调整行为。例如,您可以将权限限制为除以下list类似操作外的所有权限: 6.11 路由
6.11.1 分类
SimpleRouter
DefaultRouter
6.12 认证方式
身份验证是将传入请求与一组标识凭据(例如,请求来自的用户或与其进行签名的令牌)相关联的机制。然后,权限和限制策略可以使用这些凭据来确定是否应允许该请求。
REST框架提供了许多现成的身份验证方案,并且还允许您实现自定义方案。
身份验证始终在视图的最开始,权限和限制检查发生之前以及允许任何其他代码继续执行之前运行。
该request.user属性通常将设置为contrib.auth包User类的实例。
该request.auth属性用于任何其他身份验证信息,例如,它可用于表示与请求进行签名的身份验证令牌。
如何确定身份验证
身份验证方案始终定义为类列表。REST框架将尝试对列表中的每个类进行身份验证,并将设置request.user并request.auth使用成功进行身份验证的第一个类的返回值。
如果没有任何类通过身份验证,request.user则将设置为的实例django.contrib.auth.models.AnonymousUser,并将request.auth设置为None。
未认证请求的request.user和值request.auth可以使用UNAUTHENTICATED_USER和UNAUTHENTICATED_TOKEN设置进行修改。
设置认证方案 可以使用该DEFAULT_AUTHENTICATION_CLASSES设置全局设置默认身份验证方案。例如。
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
您还可以使用APIView基于类的视图基于每个视图或每个视图集设置身份验证方案。
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
或者,如果您将@api_view装饰器与基于函数的视图一起使用。
@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
未经授权和禁止的回应 当未经身份验证的请求被拒绝权限时,可能有两个不同的错误代码可能适用。
HTTP 401未经授权 HTTP 403权限被拒绝 HTTP 401响应必须始终包含WWW-Authenticate标头,该标头指示客户端如何进行身份验证。HTTP 403响应不包含WWW-Authenticate标头。
将使用的响应类型取决于身份验证方案。尽管可以使用多种身份验证方案,但仅可以使用一种方案来确定响应的类型。 确定响应类型时,将使用在视图上设置的第一个身份验证类。
请注意,当请求可以成功进行身份验证但仍然被拒绝执行该请求的权限时403 Permission Denied,无论身份验证方案如何,都将始终使用响应。
6.13 权限
权限与身份验证和限制一起,决定了是否应授予请求访问权限。
权限检查始终在视图的最开始处运行,然后再允许执行其他任何代码。权限检查通常会使用request.user和request.auth属性中的身份验证信息来确定是否应允许传入请求。
权限用于授予或拒绝不同类别的用户对API不同部分的访问。
最简单的许可方式是允许访问任何经过身份验证的用户,并拒绝访问任何未经身份验证的用户。这对应IsAuthenticated于REST框架中的类。
稍微不太严格的权限样式将是允许对经过身份验证的用户进行完全访问,但允许对未经身份验证的用户进行只读访问。这对应IsAuthenticatedOrReadOnly于REST框架中的类。
如何确定权限 REST框架中的权限始终定义为权限类列表。
在运行视图主体之前,将检查列表中的每个权限。如果任何权限检查失败,则将引发exceptions.PermissionDenied或exceptions.NotAuthenticated异常,并且视图主体将不运行。
当权限检查失败时,将根据以下规则返回“ 403禁止访问”或“ 401未经授权”响应:
该请求已成功通过身份验证,但权限被拒绝。—将返回HTTP 403禁止响应。 该请求未成功通过身份验证,并且最高优先级的身份验证类不使用WWW-Authenticate标头。—将返回HTTP 403禁止响应。 该请求未成功通过身份验证,并且最高优先级的身份验证类确实使用WWW-Authenticate标头。— WWW-Authenticate将返回带有适当标头的HTTP 401未经授权的响应。
对象级别权限 REST框架权限还支持对象级权限。对象级别权限用于确定是否应允许用户对特定对象执行操作,该对象通常是模型实例。
.get_object()调用时,对象级别权限由REST框架的通用视图运行。与视图级别权限一样,exceptions.PermissionDenied如果不允许用户对给定对象执行操作,则会引发异常。
如果您要编写自己的视图并要强制执行对象级别权限,或者get_object在通用视图上覆盖该方法,则需要.check_object_permissions(request, obj)在检索到该位置的视图上显式调用该方法。宾语。
这将引发PermissionDenied或NotAuthenticated异常,或者如果视图具有适当的权限则仅返回。
对象级别权限的限制
出于性能原因,返回对象列表时,通用视图不会自动将对象级别权限应用于查询集中的每个实例。
通常,当您使用对象级别权限时,您还希望适当地过滤查询集,以确保用户仅能查看允许其查看的实例。
设置权限策略 可以使用该DEFAULT_PERMISSION_CLASSES设置在全局范围内设置默认权限策略。例如。
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
如果未指定,则此设置默认为允许无限制访问:
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
您还可以使用APIView基于类的视图基于每个视图或每个视图集设置身份验证策略。
class ExampleView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
或者,如果您将@api_view装饰器与基于函数的视图一起使用。
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
AllowAny
该AllowAny许可类将允许不受限制的访问,不管请求被认证或未认证的。
此权限不是严格要求的,因为您可以通过将空列表或元组用于权限设置来达到相同的结果,但是您可以发现指定该类很有用,因为它使意图明确。
IsAuthenticated
该IsAuthenticated许可类将拒绝允许任何未认证用户,并允许许可,否则。
如果您希望仅注册用户可以访问您的API,则此权限很合适。
IsAdminUser 所述IsAdminUser许可类将拒绝许可给任何用户,除非user.is_staff是True在这种情况下的许可将被允许。
如果只希望一部分受信任的管理员可以访问您的API,则此权限很合适。
IsAuthenticatedOrReadOnly 在IsAuthenticatedOrReadOnly将允许被授权的用户进行任何请求。仅当请求方法是“安全”方法之一时,才允许对未授权用户的请求;GET,HEAD或OPTIONS。
如果您希望您的API允许匿名用户具有读取权限,而只允许经过身份验证的用户具有写入权限,则此权限非常适合。
自定义权限 要实现自定义权限,请重写BasePermission并实现以下方法之一或两者:
.has_permission(self, request, view) .has_object_permission(self, request, view, obj) True如果应授予请求访问权,则方法应返回,False否则返回。
如果您需要测试如果请求是读操作或写操作,你应该检查对常量的请求方法SAFE_METHODS,这是一个包含一个元组’GET’,‘OPTIONS’和’HEAD’。例如:
例子 以下是权限类的示例,该权限类针对黑名单检查传入请求的IP地址,如果IP已被列入黑名单,则拒绝该请求。\
from rest_framework import permissions
class BlacklistPermission(permissions.BasePermission):
"""
Global permission check for blacklisted IPs.
"""
def has_permission(self, request, view):
ip_addr = request.META['REMOTE_ADDR']
blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
return not blacklisted
除了针对所有传入请求运行的全局权限外,您还可以创建仅针对影响特定对象实例的操作运行的对象级别权限。例如:
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Instance must have an attribute named `owner`.
return obj.owner == request.user
6.14 节流
节流类似于权限,它确定是否应授权请求。节流指示临时状态,并用于控制客户端可以向API发出的请求的速率。
与权限一样,可以使用多个调节器。您的API可能会对未经身份验证的请求进行限制,而对于经过身份验证的请求则进行限制较少。
您可能要使用多个调节器的另一种情况是,由于某些服务特别耗费资源,因此您需要在API的不同部分施加不同的约束。
如果要同时施加突发节流率和持续节流率,也可以使用多个节气门。例如,您可能希望将用户限制为每分钟最多60个请求,每天最多1000个请求。
节流阀不一定仅指速率限制请求。例如,存储服务可能还需要限制带宽,而付费数据服务可能需要限制一定数量的正在访问的记录。
节流如何确定 与权限和身份验证一样,REST框架中的限制始终定义为类列表。
在运行视图主体之前,请检查列表中的每个油门。如果任何油门检查失败,exceptions.Throttled将引发异常,并且视图主体将不运行。
设定节流政策 可以使用DEFAULT_THROTTLE_CLASSES和DEFAULT_THROTTLE_RATES设置全局设置默认的限制策略。例如。
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
用在速率的描述DEFAULT_THROTTLE_RATES可以包括second,minute,hour或day作为节流段。
您还可以使用APIView基于类的视图基于每个视图或每个视图集设置节流策略。
class ExampleView(APIView):
throttle_classes = [UserRateThrottle]
def get(self, request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
或者,如果您将@api_view装饰器与基于函数的视图一起使用。
@api_view(['GET'])
@throttle_classes([UserRateThrottle])
def example_view(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
7、富文本编辑器
7.1 安装
pip install django-ckeditor
7.2 配置
7.2.1 在settings里面添加模块
INSTALLED_APPS = [
"ckeditor",#富文本编辑器
"ckeditor_uploader",#富文本编辑器的图片上传模块
]
7.2.2 添加配置
CKEDITOR_CONFIGS = {
'default': {
'language': 'zh-cn',
'toolbar_YourCustomToolbarConfig': [
{'name': 'clipboard', 'items': ['Undo', 'Redo', '-', 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord']},
{'name': 'paragraph', 'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote']},
{'name': 'insert', 'items': ['Image', 'Table', 'HorizontalRule', 'Smiley']},
{'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
{'name': 'editing', 'items': ['Find', 'Replace', '-']},
{'name': 'tools', 'items': ['Maximize']},
'/',
{'name': 'styles', 'items': ['Format', 'Font', 'FontSize']},
{'name': 'basicstyles',
'items': ['Bold', 'Italic', 'Underline', 'Strike', '-', 'RemoveFormat']},
{'name': 'colors', 'items': ['TextColor', 'BGColor']},
{'name': 'paragraph',
'items': ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock']},
{'name': 'document', 'items': ['Source']},
],
'toolbar': 'YourCustomToolbarConfig', # put selected toolbar config here
'width': '100%',
'tabSpaces': 4,
'extraPlugins': ','.join([
'uploadimage', # the upload image feature
# your extra plugins here
'div',
'autolink',
'autoembed',
'embedsemantic',
'autogrow',
'widget',
'lineutils',
'clipboard',
'dialog',
'dialogui',
'elementspath'
]),
}
}
#使用fdfs,不用设置
CKEDITOR_UPLOAD_PATH = ''
7.2.3 路由的设置
url(r"^ckeditor/",include("ckeditor_uploader.url"))
7.3 字段对比
from ckeditor.fields import RichTextField#不支持文件上传
from ckeditor.fields import RichTextFormField#支持文件上传
8、关于电商网站的基本概念
8.1 关于电商网站的基本概念
SKU即库存进出计量的单位, 可以是以件、盒、托盘等为单位。在服装、鞋类商品中使用最多最普遍。 例如纺织品中一个SKU通常表示:规格、颜色、款式。
也有人说SKU就是库存的最小单位,在服装行业,正常情况是“单款单色单码”,国内品牌有把“单款单色”当做一个SKU、也有把“单款”的几个色当一个SKU、也有把一块面料的几个个款式当一个SKU,这些都是误读。
同时,引申出另外一个概念:SKC:单款、单色。如果一定要打比方的话:SKC是一个桔子,SKU是一瓣桔子,但不管怎么说,一个桔子是桔子,一瓣桔子也是桔子。
8.2 spu
SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
例如,iphone4就是一个SPU,N97也是一个SPU,这个与商家无关,与颜色、款式、套餐也无关。以化妆品为例,下图是SPU信息:
9、文件上传
9.1 七牛云
9.2 fastdfs
https://baijiahao.baidu.com/s?id=1588881983988039766&wfr=spider&for=pc
C语言写的开源
它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题
9.2.1 追踪服务器 tracker server
负载均衡、调度的,根据客户端发出的请求(文件的上传与下载),通过tracker的调度,由storage服务器来完成上传与下载
9.2.2 存储服务器 storage server
文件的存储
Storage 可以分成多个组,每个组之间保存的数据是不同的,
每个组内部可以有多个成员,组成员内部保存的内容是一样的,没有主从的概念
9.2.3 文件上传的流程
9.2.4 文件的下载
9.3 fdfs的使用
from django.conf import settings
from django.core.files.storage import Storage,FileSystemStorage
from fdfs_client.client import *
from django.utils.deconstruct import deconstructible
from qiniu import Auth, put_file, etag
@deconstructible
class MyStorage2(Storage):
def __init__(self,location=None, base_url=None,client_conf=None):
"""
:param base_url: 图片存储的完整路径
:param client_conf: 客户端的配置文件的路径
"""
if base_url is None:
base_url=settings.FAST_URL
if client_conf is None:
client_conf=settings.FAST_CLIENT_CONF
self.base_url = base_url
self.client_conf = client_conf
def _open(self,name,mode="rb"):
print(name)
print(mode)
print(self.path(name))
pass
def _save(self,name,content):
# print("这是自定义的文件上传")
# print(content)
# print(name)
#
# print(self.client_conf)
# #创建连接对象
# client = Fdfs_client(self.client_conf)
# print(client)
# #通过bytes文件上传
# ret = client.upload_by_buffer(content.read())
# print(ret)
# if ret.get("Status") != "Upload successed.":
# raise Exception("文件上传失败")
# file_id = ret.get("Remote file_id")
# access_key=settings.QINIU_ACCESS_KEY
# secret_key=settings.QINIU_SECRET_KEY
# q = Auth(access_key, secret_key)
# bucket_name=settings.QINIU_BUCKET_NAME
# key=name
# token = q.upload_token(bucket_name, key, 3600)
# localfile = self.path(name)
# print(localfile)
# ret, info = put_file(token, key, localfile)
# return info
def url(self, name):
print("进入url方法")
print(name)
return self.base_url + name
def exists(self, name):
"""
判断文件是否存在
:param name:
:return:
"""
return False
10、docker的运用
10.1 docker的基本概念
sudo usermod -a -G docker $USER
1、镜像
2、容器
3、仓库
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。
一个完整的Docker有以下几个部分组成:
- DockerClient客户端
- Docker Daemon守护进程
- Docker Image镜像
- DockerContainer容器
10.2 镜像相关的指令
列出当前的镜像
Docker image ls
拉取远程仓库的镜像
Docker image pull 分组/镜像名
删除镜像
Docker image rm 镜像名或者镜像id
10.3 容器相关的指令
10.3.1 指令格式
Sudo docker run [相关参数] 镜像 [向容器中执行的命令]
10.3.2 查询容器
docker container ls
10.3.3 开启容器
docker container start 容器名或容器id
11、Fdfs在Django的运用
11.1 关于返回文件id的讲解
client.upload_by_filename(’/home/python/Desktop/a.png’)
getting connection
<fdfs_client.connection.Connection object at 0x7fe2c11011d0>
<fdfs_client.fdfs_protol.Tracker_header object at 0x7fe2c1101198>
{‘Uploaded size’: ‘28.00KB’, ‘Local file name’: ‘/home/python/Desktop/a.png’, ‘Storage IP’: ‘192.168.42.146’, ‘Status’: ‘Upload successed.’, ‘Group name’: ‘group1’, ‘Remote file_id’: ‘group1/M00/00/00/wKgqklz-n-SAWbz7AAByfLu1SY4811.png’}
'group1/M00/00/00/wKgqklz-n-SAWbz7AAByfLu1SY4811.png
Group1 :文件上传之后storage组的名称
M00 :storage配置的虚拟路径
/00/00/ 数据的两级目录,用来存放数据
wKgqklz-n-SAWbz7AAByfLu1SY4811.png:文件上传之后的名字,注意点它和上传的时候已经不一样了。它是由服务器根据特定的信息生成的,文件名包括:源存储服务器的IP地址、文件创建的时间戳、文件的大小、随机数和文件的拓展名等信息
11.2django默认的文件上传系统
django.core.files.storage.FileSystemStorage
11.2 Django默认的存储类
@deconstructible
class FileSystemStorage(Storage):
"""
Standard filesystem storage
"""
def __init__(self, location=None, base_url=None, file_permissions_mode=None,
directory_permissions_mode=None):
self._location = location
self._base_url = base_url
self._file_permissions_mode = file_permissions_mode
self._directory_permissions_mode = directory_permissions_mode
setting_changed.connect(self._clear_cached_properties)
11.3 文件存储类的基类
class Storage(object):
"""
A base storage class, providing some default behaviors that all other
storage systems can inherit or override, as necessary.
"""
# The following methods represent a public interface to private methods.
# These shouldn't be overridden by subclasses unless absolutely necessary.
def open(self, name, mode='rb'):
"""
Retrieves the specified file from storage.
"""
return self._open(name, mode)
def save(self, name, content, max_length=None):
"""
Saves new content to the file specified by name. The content should be
a proper File object or any python file-like object, ready to be read
from the beginning.
"""
# Get the proper name for the file, as it will actually be saved.
if name is None:
name = content.name
if not hasattr(content, 'chunks'):
content = File(content, name)
name = self.get_available_name(name, max_length=max_length)
return self._save(name, content)
# These methods are part of the public API, with default implementations.
一定要写_open和_save方法
11.5 定义fdfs文件存储系统
DEFAULT_FILE_STORAGE = "shanghuishop.utils.fastdfs.fdfs_storage.MyStorage"
FAST_URL = "http://127.0.0.1:8888/"
FAST_CLIENT_CONF = os.path.join(BASE_DIR,"utils/fastdfs/client.conf")
12、页面静态化技术
静态页面:
最早的时候,网站内容是通过在主机空间中放置大量的静态网页实现的。为了方便对这些分散在不同目录的静态网页的管理,(一般是通过FTP),象frontpage/dreamweaver这样软件甚至直接提供了向主页空间以FTP方式直接访问文件的功能。以静态网页为主的网站最大的困难在于对网页的管理,在这种框架里,网页框架和网页中的内容混杂在一起,很大程度地加大了内容管理的难度。为了减轻这种管理的成本,发展出了一系列的技术,甚至连css本身,原本也是针对这种乱七八糟的网页维护而设计的,目的就是把网页表达的框架和内容本身抽象分离出来。
A.静态网页的内容稳定,页面加载速度快。
B.静态网页的没有数据库支持,在网站制作和维护方面的工作量较大。
C.静态网页的交互性差,有很大的局限性。
动态页面:
通过执行asp、php、jsp和.net等程序生成客户端网页代码的网页。通常可以通过网站后台管理系统对网站的内容进行更新管理。发布新闻,发布公司产品,交流互动,博客,网上调查等,这都是动态网站的一些功能。也是我们常见的。 常见的扩展名有:.asp、php、jsp、cgi和aspx 等。 注意:动态页面的“动态”是网站与客户端用户互动的意思,而非网页上有动画的就是动态页面。
A.交互性好。
B.动态网页的信息都需要从数据库中读取,每打开一个一面就需要去获取一次数据库,如果访问人数很多,也就会对服务器增加很大的荷载,从而影响这个网站的运行速度。
其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。
为什么需要动态页面静态化:
- 搜索引擎的优化
尽管搜索机器人有点讨厌,各个网站不但不会再象从前一样把它封起来,反而热情无比地搞SEO,所谓的面向搜索引擎的优化,其中就包括访问地址的改写,令动态网页看上去是静态网页,以便更多更大量地被搜索引擎收录,从而最大限度地提高自已的内容被目标接收的机会。但是,在完全以动态技术开发的网站,转眼中要求变换成静态网页提供,同时,无论如何,动态网页的内容管理功能也是必须保留的;就如同一辆飞驶的奔驰忽然要求180度转弯,要付出的成本代价是非常大的,是否真的值得,也确实让人怀疑。
- 提高程序性能
很多大型网站,进去的时候看它很复杂的页面,但是加载也没有耗费多长时间,除了其它必要原因以外,静态化也是其中必需考虑的技术之一。
先于用户获取资源或数据库数据进而通过静态化处理,生成静态页面,所有人都访问这一个静态页面,而静态化处理的页面本身的访问速度要较动态页面快很多倍,因此程序性能会有大大的提升。
静态化在页面上的体现为:访问速度加快,用户体验性明显提升;在后台体现为:访问脱离数据库,减轻了数据库访问压力。
13、定时任务
13.1 安装支持包
安装方式:pip install django-crontab
13.2 注册定时应用
INSTALLED_APPS = [
'django_crontab', # 定时任务
]
13.3 定时任务crontab的时间设置格式
定时时间基本格式 :
5个'*'符号:* * * * * 命令
依次对应:分 时 日 月 周
M: 分钟(0-59)。每分钟用 * 或者 */1 表示
H:小时(0-23)。(0表示0点)
D:天(1-31)。
m: 月(1-12)。
d: 一星期内的天(0~6,0为星期天)。
13.4 设置定时任务
在settings中添加CRONJOBS,设置定时任务。
- 以下示例定时任务为:apps下–contents应用内–generate_static_index_html方法;
- 定时时间为:每隔一分钟运行一次;
- ">>"符:拼接定时任务运行结果保存路径。
#配置定时人物
CRONJOBS = [
('*/1 * * * *','advertisement.advercorntab.save_static_html','>> /home/felton/logs/contab.log'),
]
CRONTAB_COMMAND_PREFIX="LANG——ALL=zh_cn.UTF-8"
#防止出现中文错误
13.5 管理定时任务
添加定时任务到系统中,启动定时任务必须先执行改语句。
$ python manage.py crontab add
显示已激活的定时任务
$ python manage.py crontab show
移除定时任务
$ python manage.py crontab remove