当前位置: 首页 > article >正文

使用Python+Redis实现文章投票网站后端功能

1.实现投票功能,2.创建文章数据,3.对文章进行排序。

实现投票功能

实现投票功能,要注重文章的时效性与投票的公平性,所以需要给投票功能加上一些约束条件:

  • 文章发布满一个星期后,不再允许用户对该文章投票
  • 一个用户对一篇文章只能投一次票

所以我们需要使用:

  • 一个有序集合 time,存储文章的发布时间
  • 一个集合 voted:*,存储已投票用户名单
    • 其中 * 是被投票文章的 ID
  • 一个有序集合 score,存储文章的得票数
 
  1. ONE_WEEK_IN_SECONDS = 7 * 24 * 60 * 60
  2. def article_vote(r, user_id, article_id):
  3. # 使用 time.time() 获取当前时间
  4. # 减去一周的秒数,从而获取一周前的Unix时间
  5. cutoff = time.time() - ONE_WEEK_IN_SECONDS
  6. if r.zscore('time', article_id) < cutoff:
  7. return
  8. if r.sadd('voted:' + article_id, user_id):
  9. r.zincrby('score', article_id, 1)

当用户尝试投票时,使用 ZSCORE 命令读取 time 有序集合,得到这篇文章的发布时间,再判断文章的发布时间是否超过一周。ZSCORE 命令的语法如下:

r.zscore(key, member)

  • key :是有序集合的键名
  • member :是有序集合中的某个成员

若未超过,则使用 SADD 命令尝试将用户追加到这篇文章的已投票用户名单中,如果添加成功,则说明该用户未投过票。SADD 命令的语法是:

r.sadd(key, member)

  • key :是集合的键名
  • member :是要添加进集合的元素

由于集合中的元素是唯一的,所以sadd函数会根据member是否存在在集合中做出不同返回:

  • 若该元素不存在在集合中,返回 True
  • 若该元素已存在在集合中,返回 False

所以返回为 True 时使用 ZINCRBY 命令来为文章的投票数加 1zincrby 函数语法如下:

r.zincrby(key, member, increment)

  • key :是有序集合的键名
  • member :是有序集合中要增加分值的成员
  • increment :是要增加的分值
创建文章数据

现在系统中还缺少文章数据,所以我们要提供一个创建文章的函数,并把文章数据存储到 Redis 中。创建文章的步骤如下:

  • 创建新的文章 ID
  • 将文章作者加入到这篇文章的已投票用户名单中
  • 存储文章详细信息到 Redis 
  • 将文章的发布时间和初始投票数加入到 time 和 score 两个有序集合中
  1. def post_article(r, user, title, link):
  2. # 创建新的文章ID,使用一个整数计数器对 article 键执行自增
  3. # 如果该键不存在,article 的值会先被初始化为 0
  4. # 然后再执行自增命令
  5. article_id = str(r.incr('article'))
  6. voted = 'voted:' + article_id
  7. r.sadd(voted, user)
  8. r.expire(voted, ONE_WEEK_IN_SECONDS)
  9. now = time.time()
  10. article = 'article:' + article_id
  11. r.hmset(article, {
  12. 'title': title,
  13. 'link': link,
  14. 'poster': user,
  15. })
  16. r.zadd('score', article_id, 1)
  17. r.zadd('time', article_id, now)
  18. return article_id

将文章作者加入已投票用户名单中和之前一样,这里不再赘述,但在这里我们需要为这个已投票用户名单设置一个过期时间,让它在一周后(到期后)自动删除,减少 Redis 的内存消耗。为设置过期时间的命令是:

r.expire(key, seconds)

  • key :要设置过期时间的键名
  • seconds :过期时间的长度(单位:秒)

这里我们要设置的时间是一周,所以我们可以使用上面定义好的全局变量 ONE_WEEK_IN_SECONDS

接下来要存储文章详细信息了,前面介绍过 hset 可以执行单个字段(域)的设置,这里我们使用 hmset 一次性设置多个字段(域),其语法如下:

r.hmset(key, {field: value, [field: value ...]})

我们可以使用 Python 的散列来一次性存储多个字段(域)到 Redis,只需要将整个散列当作 key 对应的值通过 hmset 函数设置进去就行。

最后,将初始投票数和创建时间设置到 scoretime 中都可以通过 ZADD 命令来实现:

r.zadd(key, member, score)

  • key :有序集合的键名
  • member :要加入有序集合的成员
  • score :该成员的分值

这里需要注意的是,因为该篇文章的作者已经被加入到该文章已投票用户名单中,为了保持数据一致性,我们需要将文章的初始投票数设为 1

对文章进行排序

实现了文章投票和创建文章功能,接下来我们就需要将评分最高的文章最新发布的文章Redis 中取出了。

  • 首先我们要根据排序方式的不同:

    • 按评分排序,则从 score 有序集合中取出一定量的文章 IDscore有序集合存放文章ID和对应的投票数)
    • 按时间排序,则从 time 有序集合中取出一定量的文章 IDtime有序集合存放文章ID和对应的发布时间)
  • 构成一个有序文章信息列表,每个元素都:

    • 使用 HGETALL 命令,取出每篇文章的全部信息
  1. def get_articles(r, start, end, order='score'):
  2. ids = r.zrevrange(order, start, end)
  3. articles = []
  4. for id in ids:
  5. article_data = r.hgetall(id)
  6. article_data['id'] = id
  7. articles.append(article_data)
  8. return articles

这里因为需要对有序集合进行排序,所以我们在取出文章 ID 时需要使用到 ZREVRANGE 命令,以分值从大到小的排序方式取出文章 IDZREVRANGE 命令的语法是:

r.zrevrange(key, start, stop)

  • key :有序集合的键名
  • start :开始的数组下标
  • stop :结束的数组下标

得到多个文章 ID 后,我们还需要根据每一个文章 ID 获取文章的全部信息,这时就需要使用到 HGETALL 命令,它的语法如下:

r.hgetall(key)

  • key :哈希的键名

我们取出文章的全部信息后,还为文章信息添加了一个字段 id。这是因为文章 IDRedis 中是作为键名存储的,不在值当中,所以我们需要附加这个字段到文章信息中。

实现这些方法后,我们大体实现了一个文章投票的后端处理逻辑,能够为文章投票并能根据投票结果改变文章的排序情况。

编程要求

根据提示,在右侧Begin-End区域补充代码,完成简化版文章投票网站的后端处理逻辑:

  • article_vote() 函数中:

    • 该方法作用是:对文章投票
    • 参数说明:
      • r:Redis 客户端
  • user_id:投票用户

  • article_id:被投票文章

    • 已提供一周前 Unix 时间戳,存放在变量 cutoff
    • 当满足以下条件时,为文章投一票:
      • 该文章发布不超过一周
  • 该用户没有为该文章投过票

  • post_article() 函数中:

    • 该方法作用是:创建文章
    • 参数说明:
      • r:Redis 客户端
  • user:发布用户

  • title:文章标题

  • link:文章链接

    • 已提供:
      • article_id,新文章 ID
  • voted,新文章已投票用户名单存储键名

  • article,新文章详细信息存储键名

  • now,文章创建时间

    • 按照 ID 递增的顺序依次创建文章
    • 保证发布文章的用户不能给自己的文章投票
    • 文章在发布一周后删除已投票用户名单
    • 存储文章详细信息到 Redis 中,包括字段:
      • 文章标题
  • 文章链接

  • 发布用户

    • 存储文章的发布时间和初始投票数
      • 初始投票数为 1
  • get_articles() 函数中:

    • 该方法作用是:对文章进行排序
    • 参数说明:
      • r:Redis 客户端
      • start:从排序为 start 的文章开始获取
  • end:到排序为 end 的文章结束获取

  • order:排序方式,分为两种:

    • time:按时间排序
    • score:按投票数排序
  • 已提供文章信息空列表,articles

  • 实现按时间/投票数排序

  • 将排序后的文章及其全部信息组成一个列表:

    • 按照不同排序规则取出排序在参数提供的区间范围内的文章
    • 及每篇文章的全部信息,包括文章 ID
      #!/usr/bin/env python
      #-*- coding:utf-8 -*-
      import time
      ONE_WEEK_IN_SECONDS = 7 * 24 * 60 * 60
      def article_vote(r, user_id, article_id):
          cutoff = time.time() - ONE_WEEK_IN_SECONDS
          # 请在下面完成要求的功能
          #********* Begin *********#
          if r.zscore('time', article_id) < cutoff:
              return
          if r.sadd('voted:' + article_id, user_id):
              r.zincrby('score', article_id, 1)
          #********* End *********#
      def post_article(r, user, title, link):
          article_id = str(r.incr('article'))
          voted = 'voted:' + article_id
          now = time.time()
          article = 'article:' + article_id
          # 请在下面完成要求的功能
          #********* Begin *********#
          r.sadd(voted, user)
          r.expire(voted, ONE_WEEK_IN_SECONDS)
          r.hmset(article, {
              'title': title,
              'link': link,
              'poster': user,
          })
          r.zadd('score', article_id, 1)
          r.zadd('time', article_id, now)
          #********* End *********#
          return article_id
      def get_articles(r, start, end, order='score'):
          articles = []
          # 请在下面完成要求的功能
          #********* Begin *********#
          ids = r.zrevrange(order, start, end)
          for id in ids:
              article_data = r.hgetall(id)
              article_data['id'] = id
              articles.append(article_data)
          #********* End *********#
          return articles


http://www.kler.cn/a/147898.html

相关文章:

  • 安全见闻1-5
  • vue请求数据报错,设置支持跨域请求,以及2种请求方法axios或者async与await
  • C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
  • C++初阶:类和对象(上)
  • 【数据结构】交换排序——冒泡排序 和 快速排序
  • 【STM32F1】——无线收发模块RF200与串口通信
  • 【文献阅读笔记】关于GANomaly的异常检测方法
  • 【开源威胁情报挖掘1】引言 + 开源威胁情报挖掘框架 + 开源威胁情报采集与识别提取
  • C#,《小白学程序》第十九课:随机数(Random)第六,随机生成任意长度的大数(BigInteger)
  • PTA:百钱买百鸡 - C/C++ 数组及字符串
  • Vue组件的自定义事件$emit
  • ArcGIS10.x系列 Python工具箱教程
  • TypeScript和JavaScript有什么不同
  • 实战Flask+BootstrapTable最实用服务端分页查询动态表头及数据(ajax方式)
  • 群晖NAS配置之自有服务器ngrok实现内网穿透
  • bluez inquiry 流程梳理--从代码层面理解bluez架构
  • opencv-医学图像预处理
  • LeetCode算法题解(动态规划)|LeetCode198. 打家劫舍、LeetCode213. 打家劫舍 II、LeetCode337. 打家劫舍 III
  • 小程序中的大道理--综述
  • Android12:内置第三方应用,权限控制器已停止运行,应用app已停止运行
  • PC行内编辑
  • 一篇文章搞懂 JavaScript 箭头函数
  • 力扣2.两数相加
  • IDEA:Command line is too long
  • 嵌入式常见协议---IIC协议
  • 《使用Python将Excel数据批量写入MongoDB数据库》