装饰器复习

函数装饰器有4种,类装饰器有2种

函数装饰器

1.函数装饰函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from time import perf_counter

def time_counter(func):
def wrapper(*args,**kwargs):
start = perf_counter()
func(*args,**kwargs)
print(perf_counter() - start)

return wrapper


@time_counter
def foo():
[num for num in range(55555)]

foo()
2.函数装饰类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from time import perf_counter

def time_counter(func):
def wrapper(*args,**kwargs):
start = perf_counter()
func(*args,**kwargs)
print(perf_counter() - start)

return wrapper

class A:
@time_counter
def foo(self):
[num for num in range(55555)]

a = A()
a.foo()

上面两个装饰器一模一样 , 也就是说,函数装饰器能无差别的运用于函数和类方法

3.类装饰函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from time import perf_counter


class TimeCounter:
def __init__(self,func):
self.func = func

def __call__(self,*args,**kwargs):
start = perf_counter()
self.func(*args,**kwargs)
print(perf_counter() - start)

@TimeCounter
def foo():
[num for num in range(55555)]

foo()
4.类装饰类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from time import perf_counter


class TimeCounter:
def __init__(self,func):
self.func = func
self.cls = self.__class__

def __call__(self,*args,**kwargs):
start = perf_counter()
# 需要添加一个self.cls参数
self.func(self.cls,*args,**kwargs)
print(perf_counter() - start)


class A:
@TimeCounter
def foo(self):
[num for num in range(55555)]

a = A()
a.foo()

类装饰器

类装饰器要返回另外一个类

1.函数装饰类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def deco(cls):
class Wrapper:
def __init__(self,*args,**kwargs):
self.wrapped = cls(*args,**kwargs)
def __getattr__(self,name):
return getattr(self.wrapped,name)

return Wrapper

@deco
class C:
def __init__(self,x,y):
self.attr = 'spam'

c = C(6,7)
print(c.attr) # spam
2.类装饰类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Deco:
def __init__(self,cls):
self.cls = cls
def __call__(self,*args,**kwargs):
self.wrapped = self.cls(*args,**kwargs)
return self.wrapped
def __getattr__(self,name):
return getattr(self.wrapped,name)


@Deco
class C:
def __init__(self,name):
self.name = name

c = C('heyingliang')
print(c.name) # heyingliang

装饰器的嵌套

装饰器的进入顺序 : 由上到下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def deco1(func):
def wrapper(*args,**kwargs):
print('deco1')
func(*args,**kwargs)
return wrapper

def deco2(func):
def wrapper(*args,**kwargs):
print('deco2')
func(*args,**kwargs)
return wrapper

@deco1
@deco2
def func():
pass

func()
# deco1
# deco2

带参数的装饰器

1.函数装饰函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def deco(func_name):
def wrapper(func):
def wrapped(*args,**kwargs):
print(f'--- in deco : {func_name}---')
func(*args,**kwargs)
print(f'--- end deco: {func_name} ---')
return wrapped
return wrapper


@deco('foo')
def func():
pass

func()
2.类装饰函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def deco(func_name):
class wrapped:
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print(f'--- in deco : {func_name}---')
self.func(*args,**kwargs)
print(f'--- end deco: {func_name} ---')

return wrapped

@deco('foo')
def func():
pass

func()

正则表达式复习

?前面字符出现的次数为1或0

? : 前面字符出现的次数为1或者为0

注意,是前面字符出现的次数,不是后面字符出现的次数

1
2
3
4
5
6
7
8
import re

x = r'\(?0\d{2}[) -]?\d{8}'

print(re.search(x,'(010)88886666'))
print(re.search(x,'022-22334455'))
# <re.Match object; span=(0, 13), match='(010)88886666'>
# <re.Match object; span=(0, 12), match='022-22334455'>

|是贪婪的

x|y : 匹配x或y。

z|food能匹配 z 或 food。(z|f)ood则匹配 zood 或 food 。

也就是说 : |是贪婪的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import re

x = r'0\d{4}-\d{5}|\d{5}'
y = r'\d{4}-\d{5}|\d{5}'

print(re.search(y,'4444-12345'))
print(re.search(y,'12345'))
# <re.Match object; span=(0, 10), match='4444-12345'>
# <re.Match object; span=(0, 5), match='12345'>

print(re.search(x,'4444-12345'))
print(re.search(x,'12345'))
# <re.Match object; span=(5, 10), match='12345'>
# <re.Match object; span=(0, 5), match='12345'>

各个分组

注意 :
这些分组的括号都是加在最前面

(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。
(?:pattern) 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“`(
(?=pattern) 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,“`Windows(?=95
(?!pattern) 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如“`Windows(?!95
(?<=pattern) 反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,“`(?<=95
(?<!pattern) 反向否定预查,与正向否定预查类拟,只是方向相反。例如“`(?<!95

Python里的正则

方法 功能
match() 判断一个正则表达式是否从开始处匹配一个字符串
search() 遍历字符串,找到正则表达式匹配的第一个位置
findall() 遍历字符串,找到正则表达式匹配的所有位置,并以列表的形式返回
finditer() 遍历字符串,找到正则表达式匹配的所有位置,并以迭代器的形式返回

匹配对象的属性方法

方法 功能
group() 返回匹配的字符串
start() 返回匹配的开始位置
end() 返回匹配的结束位置
span() 返回一个元组表示匹配位置(开始,结束)
方法 用途
split() 在正则表达式匹配的地方进行分割,并返回一个列表
sub() 找到所有匹配的子字符串,并替换为新的内容
subn() 跟 sub() 干一样的勾当,但返回新的字符串以及替换的数目

标志符

标志 含义
ASCII,A 使得转义符号如\w,\b,\s和\d只能匹配ASCII字符
DOTALL,S 使得.匹配任何符号,包括换行符
IGNORECASE,I 匹配的时候不区分大小写
LOCALE,L 支持当前的语言(区域)设置
MULTILINE,M 多行匹配,影响^和$
VERBOSE,X(for’extended’) 启用详细的正则表达式

捕获分组(去重处理)

1
2
3
4
5
6
7
8
9
10
11
import re

s = "我..我我.我.....要..要要要要要.....学..学学学........编..编.编........程.程.程程";

def regex(s):
s = re.sub(r'\.','',s)
# 注意'\1'前面必须添加一个r
s = re.sub(r'(.)\1+',r'\1',s)
print(s)

regex(s) # 我要学编程
  • (.)匹配一个字符
  • (.)\1匹配两个相同的字符
  • (.)\1+匹配多个相同的字符

Xpath复习

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

简单来说:**/是直接子节点,//是非直接子节点**。注意,这两个都是子节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
from lxml import etree

html = '''

<div class='content'>
<ul id='haha'>
<li class="item-1"><a href="link2.html">first item</a></li>
</ul></div>
'''

x = etree.HTML(html)
result = x.xpath('//div[@class="content"]//li/a/text()')
print(result) # ['first item']

使用and函数来获取多属性的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
from lxml import etree

html = '''

<div class='content'>
<ul id='haha'>
<li class="item-1" name="hyl"><a href="link2.html">first item</a></li>
</ul></div>
'''

x = etree.HTML(html)
result = x.xpath('//div//li[@class="item-1" and @name="hyl"]/a/text()')
print(result) # ['first item']

多值属性获取

1
2
3
4
5
6
7
8
9
10
11
12
13
from lxml import etree

html = '''

<div class='content'>
<ul id='haha'>
<li class="item hyl" ><a href="link2.html">first item</a></li>
</ul></div>
'''

x = etree.HTML(html)
result = x.xpath('//div//li[contains(@class,"item hyl")]/a/text()')
print(result) # ['first item']

伪类选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from lxml import etree

html = '''

<div class='content'>
<ul id='haha'>
<li class="item-0" ><a href="link2.html">1</a></li>
<li class="item-1" ><a href="link2.html">2</a></li>
<li class="item-1" ><a href="link2.html">3</a></li>
<li class="item-0" ><a href="link2.html">4</a></li>
</ul></div>
'''

x = etree.HTML(html)
result1 = x.xpath('//div//li') # 返回列表
result2 = x.xpath('//div//li[1]') # 索引从1开始
result3 = x.xpath('//div//li[last()]')
result4 = x.xpath('//div//li[position()<3]')
result5 = x.xpath('//div//li[last()-2]')

关系节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from lxml import etree

html = '''

<div class='content'>
<ul id='haha'>
<li class="item-0" ><a href="link2.html">1</a></li>
<li class="item-1" ><a href="link2.html">2</a></li>
<li class="item-1" ><a href="link2.html">3</a></li>
<li class="item-0" ><a href="link2.html">4</a></li>
</ul></div>
'''

x = etree.HTML(html)
result1 = x.xpath('//li/ancestor::*') # 所有的祖先节点
result2 = x.xpath('//li[1]/ancestor::ul') # 所有标签为ul的祖先节点
result3 = x.xpath('//li[1]/attribute::*') # li结点的所有属性
result4 = x.xpath('//li[2]/child::a') # 标签为a的子节点
result5 = x.xpath('//li[2]/descendant::a') # 标签为a的后裔节点
result6 = x.xpath('//li[2]/following::*[2]') # li节点的后续所有节点,然后再索引第二个节点
result7 = x.xpath('//li[2]/following-sibling::*') # li节点的后续同级节点

Celery复习

概念

  1. 任务生产者 :产生任务并交给任务队列

  2. 任务调度 Beat:周期性的产生任务发送给任务队列

  3. 中间人(Broker):消息通信

  4. 执行单元 worker:worker监控任务队列,当队列中有新地任务时,它便取出来执行

  5. 任务结果存储backend:持久存储 Worker 执行任务的结果

v2-b59a38b663c3f51d682d5a7528cc680d_r

使用流程

  1. 创建APP
  2. APP加载配置
  3. 使用APP去装饰一个函数
  4. 使用命令行启动Celery
  5. 外部使用delay调用这个函数
  • 使用定时任务只需要修改celery_config.py即可
  • 启动 Celery Beat 进程

示例

1
2
3
4
5
6
celery_demo                    # 项目根目录
├── celery_app # 存放 celery 相关文件
│ ├── __init__.py # 创建app,初始化配置
│ ├── celeryconfig.py # 配置文件
│ └── task.py # 任务文件
└── client.py # 应用程序
1
2
3
4
5
# __init__.py
from celery import Celery

app = Celery('demo') # 创建 Celery 实例
app.config_from_object('celery_app.celeryconfig') # 通过 Celery 实例加载配置模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# celeryconfig.py
from datetime import timedelta

BROKER_URL = 'redis://127.0.0.1:6379' # 指定 Broker
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/0' # 指定 Backend

CELERY_TIMEZONE='Asia/Shanghai' # 指定时区,默认是 UTC
# CELERY_TIMEZONE='UTC'

CELERY_IMPORTS = ( # 指定导入的任务模块
'celery_app.task',
)

CELERYBEAT_SCHEDULE = {
'add-every-3-seconds': {
'task': 'celery_app.task.printer',
'schedule': timedelta(seconds=3), # 每 3 秒执行一次
'args': (5,) # 任务函数参数
},
}
1
2
3
4
5
6
7
8
9
10
# task.py
import time

from celery_app import app


@app.task
def printer(num):
time.sleep(2)
print('--------------',num)
1
2
3
4
5
6
7
8
9
10
# client.py
import time

from celery_app.task import printer


if __name__ == '__main__':
now = time.time()
printer.delay(5)
print('----',time.time()-now)

启动worker

1
celery -A celery_app worker -l info -P eventlet

启动beat

1
celery beat -A celery_app

设计工作流

签名包装了一个任务的调用参数执行参数

signature

当成偏函数使用:

1
2
3
4
5
# def add(num1,num2)为被app.task装饰的函数
s1 = add.s(2, 2)
res = s1.delay()
res.get()
# 4
Group

group并行地执行一个任务列表,按顺序返回一个特殊的结果实例(GroupResult对象)(所以可以使用.get()获取值)

1
2
3
4
5
from celery import group
from proj.tasks import add

group(add.s(i, i) for i in range(10))().get()
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
  • group(add.s(i, i) for i in range(10) : 返回<class 'celery.canvas.group'>
  • group(add.s(i, i) for i in range(10)() : 返回<class 'celery.result.GroupResult'>
  • group(add.s(i, i) for i in range(10))().get() : 返回 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Chains

任务可以像使用管道一样连接在一起,前一个执行完成后会执行下一个:

1
2
3
4
5
6
7
from celery import chain
from proj.tasks import add, mul

# (4 + 4) * 8
# add.s(4, 4)的值当成是 mul.s()的第二个参数
chain(add.s(4, 4) | mul.s(8))().get()
# 64
Chords

chord是一个带回调函数的group

1
2
3
4
5
from celery import chord
from proj.tasks import add, xsum

chord((add.s(i, i) for i in range(10)), sum.s())().get()
#90

链式任务

尽量使用异步回调的方式进行链式任务的调用

错误示范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@app.task
def fetch_page(url):
return myhttplib.get(url)

@app.task
def parse_page(url, page):
return myparser.parse_document(page)

@app.task
def store_page_info(url, info):
return PageInfo.objects.create(url, info)

@app.task
def main(url):
# fetch_page -> parse_page -> store_page
page = fetch_page.delay(url).get()
info = parse_page.delay(url, page).get()
store_page_info.delay(url, info)

正确示范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@app.task()
def fetch_page(url):
return myhttplib.get(url)

@app.task()
def parse_page(page):
return myparser.parse_document(page)

@app.task(ignore_result=True)
def store_page_info(info, url):
PageInfo.objects.create(url=url, info=info)

def main(url):
# fetch_page -> parse_page -> store_page
# fetch_page的返回值当成parse_page的参数,parse_page的返回值当成store_page_info的参数
chain = fetch_page.s(url) | parse_page.s() | store_page_info.s(url)
chain()
  • 链式任务中前一个任务的返回值默认是下一个任务的输入值之一
  • 不想让返回值做默认参数可以用 si() 或者 s(immutable=True) 的方式调用 。

Django复习

1+N问题

  • 1+N问题是针对于外键的
  • 1+N问题是针对多条数据查找的
1
2
3
4
5
6
7
8
9
# User和Post是一对多的关系,也就是User是Post的外键。

posts = Post.objects.all() # 获取所有的文章数据,注意此时不会执行sql语句 by the5fire
result = []
for post in posts: # 此时会执行select * from post的查询
result.append({
'title': post.title,
'owner': post.owner.name, # 此时会执行 select * from user where user_id = <post.user_id>
})

上面代码中,我们是查找所有文章的作者,这样肯定会带来1+N问题,这时候就可以使用select_related

1
Post.objects.select_related('user').all()

但是针对于单个查找,比如说查找文章名为”a”的文章的作者,就可以直接使用.外键字段

1
2
# 使用 .外键字段
Post.objects.filter(title__exact='a').owner.name

Post.objects.select_related('user').all()Post.objects.filter(title__exact='a').owner.name的关系有点像

1
select * from post join user on post.owner = user.user_id
1
2
3
select name from user where user_id in (
select owner from post where post.title='a'
)

一对多查找(防止1+N问题)

连接两个表 : User.objects.all().select_related('外键字段')

连接三个表 : User.objects.all().select_related('外键字段__外键字段')

1
2
3
4
5
6
7
8
9
10
########### 连接两个表 ########### 
# category是Article模型的一个字段
articles = Article.objects.all().select_related('category')
# category__name__exact='Life':category表的name字典=Life
Life_article = articles.filter(category__name__exact='Life').order_by('-create_time')[:6]

########### 连接三个表 ###########
# ArticleComment表根据自己的from_user字段连接User表,
# 再根据User表的icon字段连接Icon表
comments = ArticleComment.objects.select_related('from_user__icon').filter(article=art).all()

多对多查找(防止1+N问题)

对于一张表来说,定义了foreignKey和ManyToManyField其实是一件好事,因为这样就可以使用filter(外键字段__字段__操作符='XXX')很轻易的获取它的值

1
2
# Article定义了foreignkey(category)
Life_article = Article.objects.filter(category__name__exact='Life').order_by('-create_time')[:6]

不过如果是大量查找的话会造成1+N问题,

解决方案:

1
2
3
4
# Article表根据tags字段连接Tag表
# 然后使用filter查找tags字段的engname

articles = (Article.objects.prefetch_related('tags').filter(tags__engname__exact='hyl'))

单个查找所有(已知某个作者,查找该作者的全部出版书籍)

1
2
3
4
5
6
7
8
9
10
from django.db import models

# 一本书有多个作者,一个作者有多本书
class Author(models.Model):
name = models.CharField()
email = models.EmailField()

class Book(models.Model):
title = models.CharField()
authors = models.ManyToManyField(Author)
1
2
3
4
5
6
7
 # 1.一本书的所有作者
b = Book.objects.get(id=1)
b.author.all()

# 2.一个作者的所有书籍
a = Author.objects.get(id=1)
a.book_set.all()

没有的一方调用有的一方,就要使用_set
(没有定义ManyToMany的一方调用有ManyToMany的的一方时,需要使用_set)

1+N问题总结

前面的细节部分不用看了,直接看这个

  1. 适用范围

    • 1+N问题是针对于外键的
    • 1+N问题是针对多条数据查找的

    也就是说:
    如果是单条数据的查找,可以不使用select_relatedprefetch_related

  2. 一对多单条数据查询:

    • Article和category是一对多关系. 查找category为a的文章

    • 不使用select_related

      1
      articles = Article.objects.filter(category__name__exact='a').all()
    • 使用select_related

      1
      articles = Article.objects.select_related('category').filter(category__name__exact='a')
  3. 多对多单条数据查询:

    • Article和tag是多对多关系 . 查找标签为a的所有文章

    • 不使用prefetch_related

      1
      articles = Article.objects.filter(tags__engname__exact='a').all()
    • 使用prefetch_related

      1
      articles = Article.objects.prefetch_related('tags').filter(tags__engname__exact='a')
  4. 获取

    • 一对多 : 获取某个分类的所有文章:

      1
      Article.objects.filter(category__name__exact='a').all()
    • 一对多 : 获取某个文章的分类:

      1
      2
      3
      Article.objects.filter(id=1).category
      # 或者
      Category.objects.article_set.first()
    • 多对多 : 获取某个文章的所有标签:

      1
      Article.objects.filter(id=1).tags.all()
    • 多对多 : 获取某个标签的所有文章:

      1
      2
      3
      Article.objects.filter(tags__name__exact='a').all()
      # 或者
      Tag.objects.get(id=1).article_set.all()

聚合函数

使用aggregate()函数返回聚合函数的值
Avg,Count,Max,Min,Sum

1
2
from django.db.models import Max
maxAge = Students.objects.aggregate(Max('sage'))

groupby要使用annotate

annotate用于添加一列:

  • Article.objects.values("category")查取category列
  • 使用Count("category")计算数量
  • 然后添加Count列
1
articles_num = Article.objects.values("category").annotate(Count=Count("category"))

示例 :

Count 接受的参数为需要计数的模型的名称

1
2
# Count 接受的参数为需要计数的模型的名称
Category.objects.annotate(num_posts=Count('post'))
  • 统计返回的 Category 记录的集合中每条记录下的文章数

  • 代码中的 Count 方法为我们做了这个事,它接收一个和 Categoty 相关联的模型参数名(这里是 Post,通过 ForeignKey 关联的),然后它便会统计 Category 记录的集合中每条记录下的与之关联的 Post 记录的行数,也就是文章数,最后把这个值保存到 num_posts 属性中。

  • 现在在 Category 列表中每一项都新增了一个 num_posts 属性记录该 Category 下的文章数量,我们就可以在模板中引用这个属性来显示分类下的文章数量了。

==只要是两个 model 类通过 ForeignKey 或者 ManyToMany 关联起来,那么就可以使用 annotate 方法来统计数量。==

1
2
3
4
5
6
7
8
9
10
class Post(models.Model):
title = models.CharField(max_length=70)
body = models.TextField()
Tags = models.ManyToManyField('Tag')

def __str__(self):
return self.title

class Tag(models.Model):
name = models.CharField(max_length=100)

统计标签下的文章数:

1
2
3
4
5
from django.db.models.aggregates import Count
from blog.models import Tag

# Count 计算分类下的文章数,其接受的参数为需要计数的模型的名称
tag_list = Tag.objects.annotate(num_posts=Count('post'))

自定义模型管理器

1
2
3
4
5
6
7
8
9
class IsDeleteManager(models.Manager):
'去除删除Manager'
def get_queryset(self):
return super().get_queryset().filter(isDelete=False)


class Article(models.Model):
'文章'
objects = IsDeleteManager()

自定义标签和过滤器

  1. 在app中创建templatetags模块(模块名只能是templatetags)
  2. 在templatetags里面创建任意 .py 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from django import template
from django.utils.safestring import mark_safe

register = template.Library() #register的名字是固定的,不可改变

# 过滤器
@register.filter
def multi(x,y):
return x*y

# 标签
@register.simple_tag
def multitag(x,y,z):
return x*y*z

# 标签
@register.simple_tag
def my_input(id,arg):
   result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
   return mark_safe(result)

在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py

1
{% load my_tags %} 
1
2
3
4
5
{# 过滤器 #}
{{ var|filter_name:参数 }}

{# 标签 #}
{% simple_tag 参数1 参数2 ... %}
定义标签示例
1
2
3
4
5
6
7
8
9
# get_data.py
from django import template

register = template.Library()

@register.simple_tag
def get_data_age():
age = 10
return age
1
2
3
4
5
6
7
8
9
<body>
<!--导入自定义模板标签-->
{% load get_data %}
<!--将函数获取的值取名-->
{% get_data_age as age %}
<h1>我的第一个HTML页面</h1>
<!--使用-->
<p>我的年纪{{ age }}</p>
</body>

{% get_data_age as age %} : 给函数获取的值取名

定义过滤器示例
1
2
3
4
5
6
7
8
9
10
11
# get_data.py
from django import template

register = template.Library()

@register.filter(name='truncate_filter')
def truncate_chars(value):
if value.__len__() > 5:
return '%s......'% value[0:5]
else:
return value

如果没有使用name参数,django默认会将函数名作为name参数的值,所以下面的代码和上面的代码作用相同。

1
2
3
4
5
6
7
<body>
<!--导入自定义模板标签-->
{% load get_data %}
<h1>我的第一个HTML页面</h1>
<!--使用-->
<p>我的年纪{{ "123456789"|truncate_chars }}</p>
</body>

如果tempaltetags无法调用,很有可能是load放错位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
{% extends 'base.html' %}
{% load staticfiles %}

{% block title %}
工具 | 凉薄
{% endblock title %}

{% block content %}
{# 注意要放在content里面 #}
{% load saveposts %}
{% install_posts_from_net as msg %}

{% endblock content %}

session

设置session过期时间

1
2
3
4
def showmain(request):
username = reqeust.POST.get('username')
# 设置10秒寿命
request.sessionn.set_expiry(10)

清除session

1
2
3
4
5
# 退出登录的视图
from django.contrib.auth import logout
def quit(request):
logout(request)
...

模板

在模板中使用点语法

  • 字典查询
  • 属性或者方法
  • 数字索引

模板里使用session:
{{ request.session.username }} : 取出session里的username

1
2
3
4
5
6
7
{% if request.session.username %}
欢迎 <a href="#">{{ request.session.username }}</a>
<a href="#">注销</a>
{% else %}
<a href="{% url 'user:login' %}">登录</a>
<a href="{% url 'user:register' %}">注册</a>
{% endif %}

del request.session[key]:删除指定的key

静态文件

导入静态文件:

1
{% load staticfiles %}

按月归档

1
Post.objects.dates('created_time', 'month', order='DESC')
  • 这里 dates 方法会返回一个列表,列表中的元素为每一篇文章(Post)的创建时间,且是 Python 的 date 对象,精确到月份,降序排列。
  • 接受的三个参数值表明了这些含义,一个是 created_time ,即 Post 的创建时间,month 是精度,order='DESC' 表明降序排列(即离当前越近的时间越排在前面)。
  • 例如我们写了 3 篇文章,分别发布于 2017 年 2 月 21 日、2017 年 3 月 25 日、2017 年 3 月 28 日,那么 dates 函数将返回 2017 年 3 月 和 2017 年 2 月这样一个时间列表,且降序排列,从而帮助我们实现按月归档的目的。

AbstractUser

系统默认用户的继承使用:

  • 继承AbstractUser
  • 修改setting.py里的AUTH_USER_MODEL = ‘user.UserProfile’
  • 重新执行迁移同步.

只有继承了abstractuser类的用户模型才能使用logout,login和authenticate.

authenticate和login是一起使用的先验证后登录:

  • 先使用authenticate进行用户的数据库查询判断,如果有则返回用户对象
  • login(request,user):类似于request.session,但是不是使用session属性了,而是使用request.user

form

Django会处理涉及表单的三个不同部分:

  • 准备并重组数据,以便下一步的渲染
  • 为数据创建HTML表单
  • 接收并处理客户端提交的表单及数据

form.is_valid()返回false的原因一般是

  • form中的每个field默认都是required的,如果没有填,form.is_valid()就会返回false
  • html中的form中的各个field的name一定要和对应的form类的各个field的name保持一致,这也是一个易错点。
  • 慎用自定义form的互相继承,很容易造成form.is_valid()返回false

使用 lform.errors 获取 forms.ValidationError

1
2
lform = UserLoginForm(request.POST)
content = {'msg':lform.errors}

验证码全流程

验证码全流程

用户全流程

用户相关全流程

分页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

def listing(request):
contact_list = Contacts.objects.all()
paginator = Paginator(contact_list, 25) # 每页显示 25 个联系人

page = request.GET.get('page')
try:
contacts = paginator.page(page)
except PageNotAnInteger:
# 如果用户请求的页码号不是整数,显示第一页
contacts = paginator.page(1)
except EmptyPage:
# 如果用户请求的页码号超过了最大页码号,显示最后一页
contacts = paginator.page(paginator.num_pages)

return render(request, 'list.html', {'contacts': contacts})

ListView

  • ListView : 用来获取某个 model 中的所有数据
  • DetailView : 从数据库获取模型的一条记录数据
  • ListView 主要用在获取某个 model 列表中
  • 通过 template_name 属性来指定需要渲染的模板,
    通过 context_object_name 属性来指定获取的 model 列表的名字,否则只能通过默认的 object_list 获取
  • 复写 get_queryset 方法以增加获取 model 列表的其他逻辑
  • 复写 get_context_data 方法来为上下文对象添加额外的变量以便在模板中访问
1
2
3
4
5
6
7
8
def index(request):
"""
获取 Article 列表以渲染首页模板
"""
article_list = Article.objects.all() # 获取文章列表
category_list = Category.objects.all() # 获取分类列表
context = { 'article_list' : article_list , 'category_list' : category_list }
return render_to_response('blog/index.html',context)

一个应用中可能会重复书写下面代码:

1
2
3
article_list = Article.objects.all()
context = { 'article_list' : article_list }
return render_to_response('blog/index.html',context)

这些逻辑抽象出来放到一个类里,于是 Django 帮我们写好了一个类,专门用于处理上面的情况,这就是 ListView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class IndexView(ListView):
# 将 model 指定为 Post,告诉 Django 我要获取的模型是 Post。
model = Post
# 指定需要渲染的模板
template_name = "blog/index.html"
# 指定获取的模型列表数据保存的变量名。这个变量会被传递给模板。
context_object_name = "article_list"

# 复写get_queryset方法:增加获取 model 列表的其他逻辑
def get_queryset(self):
article_list = Article.objects.all()
# 修改article的body属性
for article in article_list:
article.body = markdown2.markdown(article.body, extras=['fenced-code-blocks'], )
return article_list

# 复写get_context_data方法:给 context 增加额外的变量
def get_context_data(self, **kwargs):
kwargs['category_list'] = Category.objects.all().order_by('name')
return super(IndexView, self).get_context_data(**kwargs)
1
2
3
4
5
6
7
8
9
10
class CategoryView(ListView):
model = Post
template_name = 'blog/index.html'
context_object_name = 'post_list'

# ListView默认的get_queryset是取得全部的数据
# 覆写了get_queryset 方法进行筛选
def get_queryset(self):
cate = get_object_or_404(Category, pk=self.kwargs.get('pk'))
return super().get_queryset().filter(category=cate)

ListView分页技术

类视图 ListView 已经帮我们写好了上述的分页逻辑,我们只需通过指定 paginate_by 属性来开启分页功能即可

ListView 传递了以下和分页有关的模板变量供我们在模板中使用:

  • paginator ,即 Paginator 的实例。
  • page_obj ,当前请求页面分页对象。
  • is_paginated,是否已分页。只有当分页后页面超过两页时才算已分页。
  • object_list,请求页面的对象列表,和 post_list 等价。所以在模板中循环文章列表时可以选 post_list ,也可以选 object_list
1
2
3
4
5
6
7
8
from django.views.generic import ListView
from msg_board.models import Msg

class MsgList(ListView):
model = Msg#数据模型
context_object_name = 'msg_list'#模板中变量
template_name = 'index.html'#模板文件
paginate_by = 3 #一个页面显示的条目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% if msg_list %}
<table id = "msgs">
<tr>
<th>Title</th>
<th>Content</th>
<th>Author</th>
<th>Ip</th>
<th>Time</th>
<th>Click</th>
</tr>
{%for msg in msg_list %}
<tr>
<td>{{msg.title}}</td>
<td>{{msg.content}}</td>
<td>{{msg.user}}</td>
<td>{{msg.ip}}</td>
<td>{{msg.datetime}}</td>
<td>{{msg.click_count}}</td>
</tr>
{% endfor%}
</table>
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
<a href="/mysite?page={{ page_obj.previous_page_number }}">上一页</a>
{% endif %}
{% if page_obj.has_next %}
<a href="/mysite?page={{ page_obj.next_page_number }}">下一页</a>
{% endif %}
<span class="page-current">
{{ page_obj.number }}页 ,共{{ page_obj.paginator.num_pages }}页。
</span>
</span>
</div>
{%endif%}
{% else %}

DetailView

  • DetailView主要用在获取某个 model 的单个对象中
  • 通过 template_name 属性来指定需要渲染的模板,通过 context_object_name 属性来指定获取的 model 对象的名字,否则只能通过默认的 object 获取
  • 复写 get_object 方法以增加获取单个 model 对象的其他逻辑
  • 复写 get_context_data 方法来为上下文对象添加额外的变量以便在模板中访问

获取单个 mdoel 对象是很常见的,比如 Blog 里点击某篇文章后进入文章的详情页,这里获取的就是点击这篇文章。

1
2
3
4
def detail(request,article_id):
article = get_object_or_404(Article,pk=article_id)
context = { 'article' : article }
return render_to_response('blog/detail.html',context)

会重复写:

1
article = get_object_or_404(Article,pk=article_id) 

Django 通过 DetailView 来把这种逻辑抽象出来,把上面的视图函数转成类视图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ArticleDetailView(DetailView):
# 将 model 指定为 Article,告诉 Django 我要获取的模型是 Article。
model = Article
# 指定需要渲染的模板
template_name = "blog/detail.html"
# 指定获取的模型列表数据保存的变量名。这个变量会被传递给模板。
context_object_name = "article"
# pk_url_kwarg用于接收一个来自url中的主键,然后会根据这个主键进行查询
# 这里的article_id就是url(r'^blog/article/(?P<article_id>\d+)$')中的article_id
pk_url_kwarg = 'article_id'

# 返回该视图要显示的对象
def get_object(self, queryset=None):
obj = super().get_object()
obj.body = markdown2.markdown(obj.body, extras=['fenced-code-blocks'], )
return obj


# urls.py
url(r'^blog/article/(?P<article_id>\d+)$', views.ArticleDetailView.as_view(), name='detail')
  1. 假设用户点击了第三篇文章,那么该 url 会被解析成:/blog/article/3,其中 3 被传递给了详情页面视图函数。
  2. 现在视图函数被调用,它首先根据传给它的参数获自动调用 get_object 方法取到文章的 model,然后根据 context_object_name = “article” 把 article 加入到上下文中
  3. 之后渲染 template_name = “blog/detail.html” 指定的模板文件,至此用户就跳转到了文章详情页

Django rest framework

认证

django_rest_framework认证源码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from rest_framework.views import APIView,exceptions
from rest_framework import exceptions


class Authtication(BaseAuthentication):
def authenticate(self,request):
token = request._request.GET.get('token')
token_obj = UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
# 在rest framework内部会将这两个字段赋值给request,以供后续操作使用
# 这两个参数分别为request.user和request.auth
return (token_obj.user,token_obj)


class OrderView(APIView):
authentication_classes = [Authtication,]

def get(self,request,*args,**kwargs):
# 先定义好返回值ret
ret = {'code':1000,'msg':None,'date':None}
try:
# 然后再对ret进行补充修改
ret['date'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)

流程 :

  1. 创建类,继承BaseAithentication,实现authenticate()方法
  2. 返回值:
    • None : 不做处理,执行下一个认证
    • raise exception.AuthenticationFailed(‘用户认证失败’)
    • (元素1,元素2) : 元素1赋值给request.user , 元素2赋值给request.auth

权限

django_rest_framework权限源码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from rest_framework.permissions import BasePermission

class SVIPPermission(BasePermission):
# 使用messag控制拒绝访问返回的message
message = '必须是SVIP才能访问'
def has_permission(self,request,view):
if request.user.user_type != 3:
return False
return True


class OrderView(APIView):
# 添加一个permission_classes: 控制权限的类 组成的列表
permission_classes = [SVIPPermission,]

def get(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None,'date':None}
try:
ret['date'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)

实现流程 :

  1. 创建类,继承BasePermission,实现has_permission()方法
  2. 返回值:
    • True
    • False

节流

django_rest_framework阈值源码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import time
from rest_framework.throttling import BaseThrottle

VISIT_RECORD = {}

class VisitThrottle(BaseThrottle):
def __init__(self):
self.history = None

def allow_request(self,request,view):
# 获取用户IP
remote_addr = request.META.get('REMOTE_ADDR')
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime,]
return True
self.history = VISIT_RECORD.get(remote_addr)
while self.history and self.history[-1] < ctime - 10:
self.history.pop()
if len(self.history) < 3:
self.history.insert(0,ctime)
return True

def wait(self):
# 还需要等待多少秒后,才能访问
return 10 - (time.time() - self.history[-1])


class OrderView(APIView):
throttle_classes = [VisitThrottle,]

def get(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None,'date':None}
try:
ret['date'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)

实现流程

  1. 创建类
    • 继承BaseThrottle,实现allow_reques()方法和wait()方法.
    • 继承SimpleRateThrottle,实现get_cache_key()方法和scope类属性(settings.py中哦key)
  2. 返回值:
    • True
    • False

版本

django_rest_framework版本源码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# urls.py
url(r'api/(?P<version>[v1|v2]+)/user/$',views.UserView.as_view(),name='user'),

# views.py
from rest_framework.versioning import BaseVersioning

class ParamVersion(BaseVersioning):
def determine_version(self,request,*args,**kwargs):
# 捕捉url中的version值
version = request.query_params.get('version')
return version

class UserView(APIView):
# 注意:因为板块只要控制一个就行了,所以不是versioning_classes,也不用使用列表
versioning_class = ParamVersion

def get(self,request,*args,**kwargs):
# 使用ParamVersion后,直接将version信息添加到request的version属性中
x = request.version
# 处理版本的对象
y = request.versioning_scheme
return HttpResponse(str(x))

一般使用内置类,全局使用:

1
2
3
4
5
6
7
from rest_framework.versioning import QueryParameterVersioning

class UserView(APIView):

def get(self,request,*args,**kwargs):
x = request.version
return HttpResponse(str(x))
1
2
3
4
5
6
7
# settings.py
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS' : 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION' : 'v1',
'ALLOWED_VERSIONS' : ['v1','v2'],
'VERSION_PARAM' : 'version',
}

实现流程:

  • 使用配置文件 :

    1
    2
    3
    4
    5
    6
    REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS' : 'rest_framework.versioning.URLPathVersioning',
    'DEFAULT_VERSION' : 'v1',
    'ALLOWED_VERSIONS' : ['v1','v2'],
    'VERSION_PARAM' : 'version',
    }
  • urls.py :

    1
    url(r'api/(?P<version>[v1|v2]+)/user/$',views.UserView.as_view(),name='user'),

解析器

  • 本质: 根据请求头中的Content_Type去找到合适的解析器类

  • 对于request.body有值而request.POST取不到值的情况,我们可以使用rest_framework的解析器了

  • 解析器的源码流程和权限类似

django_rest_framework解析器源码流程

简单来说就是

  1. 在dispatch将各种解析器封装到request中
  2. 当我们想要使用数据的时候request.data触发self._load_data_and_files(),
  3. 然后使用反射找到合适的解析器类,
  4. 最后触发解析器类的parse()方法去解析数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# urls.py
url(r'^api/(?P<version>[v1|v2]+)/user/$',views.UserView.as_view(),name='user'),


# views.py
from rest_framework.parsers import JSONParser,FormParser

class UserView(APIView):
# 只能解析请求头为 application/json和application/x-www-form-urlencoded的数据
parser_classes = [JSONParser,FormParser,]

def post(self,request,*args,**kwargs):
# 获取解析后的结果
print(request.data)
return HttpResponse('hyl')

一般使用内置类,全局使用:

1
2
3
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASS' : ['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser']
}

序列化

django_rest_framework序列化其源码流程

对请求数据进行校验

使用方法和django form一模一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class UserInfoSerializer(serializers.Serializer):
name = serializers.CharField(error_messages={'required':'标题不能为空'})
age = serializers.IntegerField()

# 验证name的钩子函数
def validate_name(self,value): # 验证的字段值
if value.startswith("w"):
raise serializers.ValidationError('name字段不能以w开头')
else:
return value #注意通过验证,必须返回其值

class UserView(APIView):
def post(self,request,*args,**kwargs):
ser = UserInfoSerializer(data=request.data)
if ser.is_valid():
print('------------',ser.validated_data)
else:
print('------------',ser.errors)
return HttpResponse('******')
对QuerySet进行序列化

使用方法很像Scrapy中的Item:

先定义字段,然后在实例化的时候传入数据就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from rest_framework import serializers

class RolesSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField()

class RoleView(APIView):
def get(self,request,*args,**kwargs):
roles = Role.objects.all().values('id','title')
# 有多条数据,所以使用many=True
ser = RolesSerializer(instance=roles,many=True)
# 使用ser.data获取序列化后的值
res = json.dumps(ser.data,ensure_ascii=False)
return HttpResponse(res)
# # [{"id": 1, "title": "医生"}, {"id": 2, "title": "护士"}, {"id": 3, "title": "老师"}]

source参数

  1. 对于field1 = serializers.IntegerField(source='id'),其实是扫描每一行row,然后执行row.source(这里就是row.id)
  2. 如果row.source是一个可执行对象,那么就会执行该对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class UserInfoSerializer(serializers.Serializer):
# 使用get_XXX_display获取展示名
filed2 = serializers.CharField(source='get_user_type_display')
# 使用点号索引获取外键
group_title = serializers.CharField(source='group.title')
# 使用点号获取ManyToMany
roles = serializers.CharField(source='roles.all')
# 使用SerializerMethodField自定义返回
roles2 = serializers.SerializerMethodField()

# 这里返回什么,roles就是什么
def get_roles(self,row):
role_obj_list = row.roles.all()
ret = [{'id':item.id,'title':item.title} for item in role_obj_list]
return ret

ModelSerializer

ModelSerializerSerializer的关系,类似于formModelForm的关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# utils.serialize.py
class UserInfoSerialier(serializers.ModelSerializer):
field1 = serializers.CharField(source='get_user_type_display')
rls = serializers.SerializerMethodField()

class Meta:
model = UserInfo
# model = '__all__'
fields = ['id','username','field1','rls','group']
# 获取的深度(默认为0)
depth = 1

# 这里返回什么,roles就是什么
def get_roles(self,row):
role_obj_list = row.roles.all()
ret = [{'id':item.id,'title':item.title} for item in role_obj_list]
return ret

depth参数 :

当你获取的字段是外键获取多对多键的时候,depth就是获取外键或多对多键的数据.

分页

  1. 看第n页,每页显示n条数据,
  2. 在某个位置,向后查看n条数据;
  3. 加密分页,只能看上一页和下一页
看第n页,每页显示n条数据,

如果我们想要自定义分页,只需继承PageNumberPagination即可:

  • page_size : 每页的数据数量(默认)
  • page_size_query_param : 制定每页的数量(局部)
  • max_page_size : 每页最多的数据数量
  • page_query_param : url中表示页数的参数(默认是page)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from rest_framework.pagination import PageNumberPagination

class MyPageNumberPagination(PageNumberPagination):
page_size = 2
page_size_query_param = 'size'
max_page_size = 10

page_query_param = 'page'


class UserView(APIView):
def get(self,request,*args,**kwargs):
# 获取数据
users = UserInfo.objects.all()
# 创建分页对象
pg = MyPageNumberPagination()
# 在数据库中获取分页的数据
pager = pg.paginate_queryset(queryset=users,request=request,view=self)
# 对数据进行序列化
ser = UserInfoSerializer(instance=pager,many=True)
return Response(ser.data)

接下来就可以使用pagesize控制数据了

在某个位置,向后查看n条数据

使用LimitoffsetPagination

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from rest_framework.pagination import LimitOffsetPagination

class MyPageNumberPagination2(LimitOffsetPagination):
default_limit = 2
limit_query_param = 'limit'
offset_query_param = 'offset'

max_limit = 5


class UserView(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
users = UserInfo.objects.all()
# 创建分页对象
pg = MyPageNumberPagination2()
# 在数据库中获取分页的数据
pager = pg.paginate_queryset(queryset=users,request=request,view=self)
# 对数据进行序列化
ser = UserInfoSerializer(instance=pager,many=True)

return Response(ser.data)

接下来就可以使用limitoffset控制数据了

加密分页,只能看上一页和下一页

使用cursorPagination

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from rest_framework.pagination import cursorPagination

class MyPageNumberPagination3(CursorPagination):
cursor_query_param = 'cursor'
page_size = 2
# 这个分页器是需要排序的,所以要提供排序的键
ordering = 'id'
page_size_query_param = None
max_page_size = None


class UserView(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
users = UserInfo.objects.all()
# 创建分页对象
pg = MyPageNumberPagination3()
# 在数据库中获取分页的数据
pager = pg.paginate_queryset(queryset=users,request=request,view=self)
# 对数据进行序列化
ser = UserInfoSerializer(instance=pager,many=True)
# 返回get_paginated_response
return pg.get_paginated_response(ser.data)

视图

视图类继承关系-1565665206934

GenericAPIView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Views.py
from rest_framework.generics import GenericAPIView

class UserView(GenericAPIView):
queryset = UserInfo.objects.all()
serializer_class = UserInfoSerializer
pagination_class = PageNumberPagination

def get(self,request,*args,**kwargs):
# 获取所有数据
users = self.get_queryset()
# 在数据库中获取分页的数据
pager = self.paginate_queryset(users)
# 对数据进行序列化
ser = self.get_serializer(instance=pager,many=True)
# 返回Response
return Response(ser.data)

只是将定义变量提前了而已

GenericViewSet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# urls.py
url(r'^api/(?P<version>[v1|v2]+)/users/$',views.UserView.as_view({'get':'list','post':'xxx'}),name='users')

# views.py
from rest_framework.viewsets import GenericViewSet
class UserView(GenericViewSet):
serializer_class = UserInfoSerializer
queryset = UserInfo.objects.all()
pagination_class = PageNumberPagination

def list(self,request,*args,**kwargs):
# 获取所有数据
users = self.get_queryset()
# 在数据库中获取分页的数据
pager = self.paginate_queryset(users)
# 对数据进行序列化
ser = self.get_serializer(instance=pager,many=True)
# 返回get_paginated_response
return Response(ser.data)

def xxx(self,request,*args,**kwargs):
return Response('post')

GenericViewSet的唯一功能 :as_view({'get':'list','post':'xxx'})

将get请求映射到list方法,让list方法处理.让post请求交给xxx方法处理

ModelViewSet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# urls.py
urlpatterns = [
url(r'^api/(?P<version>[v1|v2]+)/users/$',views.UserView.as_view({'get':'list','post':'create'}),name='users'),

url(r'^api/(?P<version>[v1|v2]+)/users/(?P<pk>\d+)/$',views.UserView.as_view({'get':'retrieve','delete':'destory','put':'update','patch':'partical_update'}),name='users'),
]

# Views.py
from rest_framework.viewsets import ModelViewSet

class UserView(ModelViewSet):
serializer_class = UserInfoSerializer
queryset = UserInfo.objects.all()
pagination_class = PageNumberPagination
  • get : list(获取全部数据)
  • get : retrieve (获取单条数据)
  • post : create
  • delete : destory
  • put : update (更新单条数据)
  • patch : partical_update (更新全部数据)

路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from rest_framework import routers
from django.conf.urls import url,include
from . import views

router = routers.DefaultRouter()
# 第一个参数是前缀,第二个参数是调用的视图
# 这样就可以匹配api/(?P<version>[v1|v2]+)/users/...
router.register(r'users',views.UserView)


urlpatterns = [
# 一个代替4个
url(r'api/(?P<version>[v1|v2]+)/',include(router.urls))
]

渲染器

  • 渲染器定义了框架按照content_type来返回不同的响应。
  • 解析器的classes一样,renderer_classes有什么,这个视图就支持怎么样的渲染
1
2
# urls.py
url(r'^api/(?P<version>[v1|v2]+)/role/$',views.RoleView.as_view(),name='role')
1
2
3
4
5
6
7
8
9
10
from rest_framework.renderers import JSONRenderer

class RoleView(APIView):
renderer_classes = [JSONRenderer,]

def get(self,request,*args,**kwargs):
roles = Role.objects.all().values('id','title')
ser = RolesSerializer(instance=roles,many=True)
res = json.dumps(ser.data,ensure_ascii=False)
return Response(res)