使用Redis构建简易社交网站(3)-状态与信息流

目的

本文目的:实现获取主页时间线和状态推送功能。(完整代码附在文章末尾)

相关知识

在我上一篇文章 《使用Redis构建简易社交网站(2)-处理用户关系》中提到了实现用户关注和取消关注功能。

那这篇文章将教会你掌握:1redis基本命令,2python基本命令。

redis基本命令

zadd:将成员加入到有序集合中,并确保其在正确的位置上。

conn = redis.Redis()
conn.zadd("testzset", "member2", 3)
conn.zadd("testzset", "member1", 2)
conn.zadd("testzset", "member3", 1)

执行后:

  1. member3
  2. member1
  3. member2

执行结果:111

zrange:返回有序集合中指定区间内的成员。

conn = redis.Redis()
conn.zrange("testzset", 0, 1)

执行结果:['member3', 'member1']

zrevrange:按分值递减的顺序返回有序集合中指定区间内的成员。

conn = redis.Redis()
conn.zrevrange("testzset", 0, -1)

执行结果:['member2', 'member1', 'member3']

hgetall:返回哈希表中所有的域-值对。

conn = redis.Redis()
conn.hgetall("testhash")

执行结果:{'field1': '2'}

hget:从哈希中获取指定域的值。

conn = redis.Redis()
conn.hget("testhash", "field1")

执行结果:2

pipeline:将多条命令按照先后顺序放进一个队列中,一般配合execute一同使用,原子性(atomic)地执行队列里的命令。

conn = redis.Redis()
pipe = conn.pipeline(True) # 事务开始
pipe.incr("counter")
pipe.incr("counter")
pipe.incr("counter")
pipe.execute() # 事务执行

执行结果:[1, 2, 3],通过下标即可获取对应命令的执行结果。

python基本命令

使用格式化拼接字符串:

"My name is %s, I'm %i years old"%('educoder', 2)

执行结果:"My name is educoder, I'm 2 years old"

将字符串转换为浮点数:

float("1.23")

执行结果:1.23

实战例题

编写 get_home_timeline(uid) 函数,实现获得主页时间线的功能,具体参数与要求如下:

  • 方法参数uid为要获取主页时间线的用户编号;
  • 获取动态编号的实现:从存储用户主页时间线的有序集合home:{uid}中按照分值递减的顺序取出所有成员;
  • 获取动态详情的实现:遍历动态编号,使用事务一次性获取每个动态编号对应动态详情哈希键post:{pid}的所有域-值对;
  • 返回主页时间线的实现:返回事务执行的结果。

编写 post(uid, content) 函数,实现发布动态并将动态推送给粉丝的功能,具体参数与要求如下:

  • 方法参数uid为要发布动态的用户编号,content为要发布的动态内容;
  • 发布动态的实现:调用第一关中实现的create_post方法,并接收返回的动态编号,若发布失败,则取消发布,返回None
  • 获取发布时间的实现:从新发布的动态编号对应的动态详情哈希键post:{pid}中获取posted域;
  • 更新个人主页的实现:将新发布的动态编号存储到个人主页有序集合键profile:{uid}中,分值为转为浮点数后的发布时间;
  • 更新粉丝主页时间线的实现:遍历用户的粉丝列表followers:{uid},将新发布的动态编号存储到每个粉丝的主页时间线的有序集合home:{follower_id}中,分值为转为浮点数后的发布时间;
  • 返回发布结果的实现:返回新发布的动态编号。
测试说明

测试输入:4

预期输出:

用户 4 关注 用户 1
关注结果: True

测试 post 方法...
创建动态: 1
创建动态: 2
用户 1 的动态列表: ['2', '1']
用户 4 的主页时间线动态编号: ['2', '1']

测试 get_home_timeline 方法...
用户 4 的主页时间线: [{'content': 'NEW post from user 1!!!', 'uid': '1', 'user_name': 'test_user1', 'id': '2'}, {'content': 'This is the first post from user 1', 'uid': '1', 'user_name': 'test_user1', 'id': '1'}]

code.py

#code.py
#-*- coding:utf-8 -*-

import re
import time
import redis

conn = redis.Redis()

# 获得主页时间线
def get_home_timeline(uid, page=1, count=30):
    # 请在下面完成要求的功能
    #********* Begin *********#
    post_ids = conn.zrevrange("home:%s"%(uid), 0, -1)

    pipe = conn.pipeline(True)
    for pid in post_ids:
        pipe.hgetall("post:%s"%(pid))

    return pipe.execute()
    #********* End *********#

# 发布动态并将动态推送给粉丝
def post(uid, content):
    # 请在下面完成要求的功能
    #********* Begin *********#
    pid = create_post(uid, content)
    if not pid:
        return None

    posted = conn.hget("post:%s"%(pid), "posted")
    conn.zadd("profile:%s"%(uid), pid, float(posted))
    followers = conn.zrange("followers:%s"%(uid), 0, -1)

    pipe = conn.pipeline(False)
    for follower in followers:
        pipe.zadd("home:%s"%(follower), pid, float(posted))
    pipe.execute()

    return pid
    #********* End *********#

# 关注用户
def follow(uid, other_uid):
    fkey1 = "following:%s"%(uid)
    fkey2 = "followers:%s"%(other_uid)

    if conn.zscore(fkey1, other_uid):
        return None

    now = time.time()
    pipe = conn.pipeline(True)
    pipe.zadd(fkey1, other_uid, now)
    pipe.zadd(fkey2, uid, now)
    following, followers = pipe.execute()

    posts = conn.zrevrange("profile:%s"%(other_uid), 0, 100, withscores=True)
    if posts:
        pipe.zadd("home:%s"%(uid), **dict(posts))

    pipe.hincrby("user:%s"%(uid), 'following', int(following))
    pipe.hincrby("user:%s"%(other_uid), 'followers', int(followers))
    pipe.execute()

    return True

# 取消关注
def unfollow(uid, other_uid):
    fkey1 = "following:%s"%(uid)
    fkey2 = "followers:%s"%(other_uid)

    if not conn.zscore(fkey1, other_uid):
        return None

    pipe = conn.pipeline(True)
    pipe.zrem(fkey1, other_uid)
    pipe.zrem(fkey2, uid)
    following, followers = pipe.execute()

    posts = conn.zrevrange("profile:%s"%(other_uid), 0, -1)
    if posts:
        pipe.zrem("home:%s"%(uid), *posts)

    pipe.hincrby("user:%s"%(uid), 'following', -int(following))
    pipe.hincrby("user:%s"%(other_uid), 'followers', -int(followers))
    pipe.execute()

    return True

# 创建新用户
def create_user(login_name, real_name):
    login_name = login_name.lower()
    if conn.hget("users", login_name):
        return None

    uid = conn.incr("user:id")
    pipe = conn.pipeline(True)
    pipe.hset("users", login_name, uid)
    pipe.hmset("user:%i"%(uid), {
        'login_name': login_name,
        'id': uid,
        'real_name': real_name,
        'followers': 0,
        'following': 0,
        'posts': 0,
        'last_signup': time.time(),
    })
    pipe.execute()

    return uid

# 为用户创建新动态
def create_post(uid, content):
    pipe = conn.pipeline(True)
    pipe.hget("user:%i"%(uid), 'login_name')
    pipe.incr("post:id")
    login_name, pid = pipe.execute()

    if not login_name:
        return None

    pipe.hmset("post:%i"%(pid), {
        'id': pid,
        'uid': uid,
        'content': content,
        'posted': time.time(),
        'user_name': login_name,
    })
    pipe.hincrby("user:%i"%(uid), 'posts')
    pipe.execute()

    return pid

 read.py

#read.py
#-*- coding:utf-8 -*-

import os
import sys
import time
import redis
import pprint
from code import *

conn = redis.Redis()
retry_time = 0
while True:
    try:
        conn.ping()
        break
    except redis.exceptions.ConnectionError:
        os.system("redis-server > /dev/null 2>&1 &")
        retry_time += 1
        if retry_time > 3:
            break

pipe = conn.pipeline(True)
pipe.delete("users", "user:id")
keys = (
    conn.keys("user:*") + conn.keys("followers:*") + conn.keys("following:*") +
    conn.keys("post:*") + conn.keys("profile:*") + conn.keys("home:*")
)
if keys:
    pipe.delete(*keys)
pipe.execute()

# 创建测试数据
join_str = " "
for i in xrange(10):
    login_name = "test_user%i"%(i+1)
    real_name = join_str.join(login_name.split("_")).capitalize()
    create_user(login_name, real_name)

uid = int(sys.stdin.readline().strip())

print "用户 %i 关注 用户 1"%(uid)
f_result = follow(uid, 1)
print "关注结果: " + str(f_result)
print

print "测试 post 方法..."
content = "This is the first post from user 1"
pid = post(1, content)
print "创建动态: " + str(pid)
content = "NEW post from user 1!!!"
pid = post(1, content)
print "创建动态: " + str(pid)
my_profile = conn.zrevrange("profile:1", 0, -1)
print "用户 1 的动态列表: " + str(my_profile)
home_timeline = conn.zrevrange("home:%i"%(uid), 0, -1)
print "用户 %i 的主页时间线动态编号: "%(uid) + str(home_timeline)
print

print "测试 get_home_timeline 方法..."
my_home = get_home_timeline(uid)
for info in my_home:
    info.pop("posted", "404")
print "用户 %i 的主页时间线: "%(uid) + str(my_home)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuhao_

谢谢你打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值