白日依山尽,黄河入海流。欲穷千里目,更上一层楼。 -- 唐·王之涣

Django restframework实现批量操作

这篇文章主要介绍两种方式实现`批量操作`, 一种是使用 Django restframework提供的装饰器action,可以更具实际情况扩展默认的增删改查操作,扩展性很好;另外一种是使用第三方模块 `djangorestframework-bulk`,这个模块简化了我们对于 `对象本身增删改查的批量化操作`,各有优缺点。实际工作中选择合适的就好。
阅读更多

django 实现分页

1、函数视图分页

1
2
3
4
5
6
7
8
9
10
11
12
from django.core.paginator import Paginator
from django.shortcuts import render
from .models import Post

def index(request):
posts = Post.objects.all()
paginator = Paginator(posts, 15)
# 默认一般都是使用page参数
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'blog/index.html', {'page_obj': page_obj})

2、类视图分页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from django.views.generic import ListView
from .models import Post

class IndexListView(ListView):
# 关联模型
model = Post
# 模板名称,默认推导为 index_list.html
template_name = 'blogv3/index.html'
# 模板中的对象名称,默认为 object_list
context_object_name = 'posts'
# 限制请求方法
http_method_names = ['get']
# 设置分页
paginate_by = 15

# 可以通过get_context_data获取到分页信息,当然前提是设置了 paginate_by
# def get_context_data(self, *args, **kwargs):
# context = super().get_context_data(*args, **kwargs)
# print(context)
# return context

上述调试中获取的到context内容为

1
2
3
4
5
6
7
8
{
'paginator': <django.core.paginator.Paginator object at 0x104ff4160>,
'page_obj': <Page 1 of 4>,
'is_paginated': True,
'object_list': <QuerySet [... ...]>,
'view': <blogv3.views.IndexListView object at 0x104ff43a0>
}

说明:

  • paginator Django的分页对象
  • page_obj 当前页参数, 实际在template中判断的时候使用的就是该对象
  • is_paginated 是否分页
  • object_list 对象名称,context_object_name 定义了posts ,两者效果等效
  • view 视图名称

template中配置实际分页信息

1
2
3
4
5
6
7
8
9
10
11
12
<div class="container navigation">
{% if page_obj.has_previous %}
<a class="navigate pull-left" href="?page={{ page_obj.previous_page_number }}"><i class="fa fa-caret-left"></i> <<< 更 新</a>
{% endif %}

<!-- 当然有时候这里也可以显示当前页码 -->
<span>Current Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }} </span>

{% if page_obj.has_next %}
<a class="navigate pull-right" href="?page={{ page_obj.next_page_number }}">更 多 >>> <i class="fa fa-caret-right"></i></a>
{% endif %}
</div>

附加:

Paginator常用的属性和方法

  • count 总共有多少条记录
  • num_pages 总共有多少页
  • page_range 页面的区间

Page 常用属性和方法

  • has_next
  • has_previous
  • next_page_number
  • previous_page_number
  • number 当前页
  • start_index 当前页的第一条记录的索引值
  • end_index 当前页的最后一条记录的索引值

Paginator 练习

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
(django_exercise_book) [ 22-04-02 11:08 ] [ colinspace ] python manage.py shell
Python 3.9.6 (default, Jul 16 2021, 13:41:17)
[Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.core.paginator import Paginator
>>> obj_list = ['apple', 'orange', 'banana', 'tomato', 'pear' ]
>>> paginator = Paginator(obj_list, 2)
>>> paginator
<django.core.paginator.Paginator object at 0x106cf8130>
>>> paginator.
paginator.ELLIPSIS paginator.get_page( paginator.page(
paginator.allow_empty_first_page paginator.num_pages paginator.page_range
paginator.count paginator.object_list paginator.per_page
paginator.get_elided_page_range( paginator.orphans paginator.validate_number(
>>> paginator.count
5
>>> paginator.num_pages
3
>>> paginator.per_page
2
>>> paginator.page_range
range(1, 4)
>>> paginator.object_list
['apple', 'orange', 'banana', 'tomato', 'pear']
>>>
>>>
>>> page1 = paginator.page(2)
>>> page2 = paginator.page(2)
>>>
>>> page2
<Page 2 of 3>
>>> page2.
page2.count( page2.has_other_pages( page2.next_page_number( page2.paginator
page2.end_index( page2.has_previous( page2.number page2.previous_page_number(
page2.has_next( page2.index( page2.object_list page2.start_index(
>>> page2.has_previous()
True
>>> page2.has_next()
True
>>> page2.number
2
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
3
>>> page2.previous_page_number()
1
>>> page2.start_index()
3
>>> page2.end_index()
4
>>>

参考:
https://docs.djangoproject.com/en/3.2/topics/pagination/

Nginx的alias/root/try_files实战

基于项目实战,在配置try_files时遇到问题,提示资源404找不到,或者500 internal error等问题,进过排查分析定位到原因,同时加深了对Nginx的 alias、root和try_files彼此组合的了解,这里分享给大家
阅读更多

linux curl请求中的单引号、双引号及变量

4.1、单引号、双引号结合使用

参数是在单引号中,比如 'Content-type:application/json'
json中的 k-v 是要在双引号中,所以如果遇到要在curl中使用变量,就使用 字符串拼接

1
2
3
curl -i -X POST -H 'Content-type:application/json' \
-d '{"msgtype": "text", "text": {"content": "'$warnmsg'"}}' \
'https://oapi.dingtalk.com/robot/send?access_token=xxx'

4.2、全部使用双引号

不方便的地方在于json中的k-v都需要使用双引号,那就需要进行转移",如果json中的k-v很多,那就书写有点麻烦

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
curl -X POST -H "Content-Type:application/json"  \
-d "{\"userid_list\": \"${userid}\" ,\"msg\": {\"msgtype\": \"text\", \"text\": {\"content\": \"${build_msg}\"}}}" "https://oapi.dingtalk.com/robot/send?access_token=xxx"
​```

## 补充

1、curl下载文件

+ -o filename

`-o` 参数需要后面紧跟一个自定义的文件名

所以除了URL是具体的文件地址之外,URL也可以是非具体地址,比如 https://www.baidu.com 会报错整个网页的内容

+ -O

该参数后面跟的URL地址只能是具体的文件地址

另外

`-C` 可以实现断点续传
`-#` 显示下载进度
`-s` 静默输出
`-A` 模拟浏览器,比如`Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.0)`
`-e` 伪造referer(盗链)

2、监控网页请求性能

核心是利用`-w`参数(--write-out)使用curl内置的标识来输出请求过程的一些性能数据,比如状态码、总响应时长、DNS解析时长等

比如

```bash
curl -s -o /dev/null -w "%{http_code} %{remote_ip} %{time_total}" https://www.baidu.com

输出了状态码、用户IP和响应时长

具体的标识有:

content_type
filename_effective
ftp_entry_path
http_code
http_connect
local_ip
local_port
num_connects
num_redirects
redirect_url
remote_ip
remote_port
size_download
size_header
size_request
size_upload
speed_download
speed_upload
ssl_verify_result
time_appconnect
time_connect
time_namelookup
time_pretransfer
time_redirect
time_starttransfer
time_total
url_effective

Django接入Celery实现任务管理

Django接入Celery实现任务管理,并且启用flower进行任务的可视化查看。另外任务的结果除了保存在Redis中之外,我们也可以选在保存在MySQL等持久化数据库,方便后续做统计分析,结果查询等等操作
阅读更多

使用场景

  • 热数据缓存

  • 数据共享分布式 分布式session

  • 定义分布式锁 string类型 setnx 在key不存在时才能添加成功,返回True

  • 全局ID int 类型的 incrby 利用原子性

  • 计数器
    int 类型的 incr
    允许一定的延迟,先写Redis,在定时同步后端数据库

  • 限流
    int 类型的 incr
    以访问者的ip和其他信息作为key,访问一次增加一次计数,超过次数则返回false

  • 位统计
    String类型的bitcount(1.6.6的bitmap数据结构介绍)

    1
    2
    3
    4
    setbit k1 6 1
    setbit k1 7 0

    BITOP ADD / OR / XOR / NOT
  • 购物车

string或者hash, string 可以实现的, hash都可以实现
key/field/value
hincr/hdecr/hdel/hgetall/hlen

  • 用户消息时间线 timeline

list,双向链表,直接作为timeline就好了。插入有序

  • 消息队列

List提供了两个阻塞的弹出操作:blpop/brpop,可以设置超时时间

blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
上面的操作。其实就是java的阻塞队列。学习的东西越多。学习成本越低

队列:先进先除:rpush blpop,左头右尾,右边进入队列,左边出队列
栈:先进后出:rpush brpop

  • 抽奖

随机值获取

spop myset

  • 点赞

sadd / srem / sismember / smembers / scard

  • 商品标签

  • 商品筛选

1
2
3
4
5
6
# 差集
sdiff set1 set2
# 交集 intersection
sinter set1 set2
# 并集
sunion set1 set2
  • 用户关注 推荐模型

用户1可能认识的人(差集):sdiff 2:follow 1:follow
用户2可能认识的人:sdiff 1:follow 2:follow

  • 排行榜

id 为6001 的新闻点击数加1:zincrby hotNews:20190926 1 n6001

获取今天点击最多的15条:zrevrange hotNews:20190926 0 15 withscores

哨兵的作用

  • 监控主数据库和从数据库是否运行正常;
  • 主数据出现故障后自动将从数据库转化为主数据库

哨兵的安装配置

安装

配置

使用

1
2
3
4
# vim sentinel.conf
sentinel monitor master-name 127.0.0.1 6379 1
# 启动
redis-sentinel ./sentinel.conf

最后一个参数表示 最低通过票数 ,是哨兵在选举时用;如果是多个哨兵的话

1
2
3
4
5
6
7
# vim sentinel.conf
sentinel monitor master-name1 127.0.0.1 6379 1

sentinel monitor master-name2 127.0.0.1 6380 2

# 启动
redis-sentinel ./sentinel.conf

故障演练

环境 1主2从; 单哨兵或者多哨兵 故障演练原理是一样的

扩展

哨兵和集群?