本文目的:实现获取主页时间线和状态推送功能。(完整代码附在文章末尾)
在我上一篇文章 《使用Redis构建简易社交网站(2)-处理用户关系》中提到了实现用户关注和取消关注功能。
那这篇文章将教会你掌握:1
.redis
基本命令,2
.python
基本命令。
zadd:将成员加入到有序集合中,并确保其在正确的位置上。
conn = redis.Redis()
conn.zadd("testzset", "member2", 3)
conn.zadd("testzset", "member1", 2)
conn.zadd("testzset", "member3", 1)
执行后:
member3
member1
member2
执行结果:1
,1
,1
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]
,通过下标即可获取对应命令的执行结果。
使用格式化拼接字符串:
"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)