Социальные штуки в Django с Redis часть 2
В прошлой части я рассказал, как использую Redis в Django для показа онлайн пользователей. Во второй же части я расскажу, как сделать показ новых комментариев для постов (количество у каждого поста и подсветка новых в посте).
Разберемся сначала, из каких частей будет состоять вся “индикация”:
1. Тэг, показывающий сколько новых комментариев в топике у данного пользователя
2. Функция, возвращающая номера новых комментариев в посте и удаляющая их из базы
3. Функция добавляющая новый комментарий в список ‘новых’ для всех пользователей
Для понимания работы этих функций, рассмотрим ключи в базе:
‘users:%username%:posts’ – содержит номера новых постов
‘users:%username%:%post.id%’ – содержит id комментариев для поста
‘users:%username%’ – общее количество новых комментариев
Теперь, рассмотрим функции по работе с базой и их применение для каждого случая.
Все начинается с того, что при сохранении комментария его id записывается в базу для каждого юзера:
def add_comment(sender, **kwargs): comment = kwargs['instance'] if redis_db.exists('users'): users = redis_db.smembers('users') if comment.author.username not in users: redis_db.sadd('users', comment.author.username) users.add(comment.author.username) else: redis_db.sadd('users', comment.author.username) users = redis_db.smembers('users') for user in users: if user == comment.author.username: continue if redis_db.exists('users:%s' % user): if int(redis_db.get('users:%s' % user)) < getattr(settings, 'UNREAD_LIMIT', 100): redis_db.incr('users:%s' % user) redis_db.sadd('users:%s:%s' % (user, comment.post.id), comment.id) redis_db.sadd('users:%s:posts' % user, comment.post.id) else: continue else: redis_db.set('users:%s' % user, 0)
Работает код просто, сначала достаем список со всеми пользователями (ключ ‘users’), а потом проходимся по каждому и добавляем номер комментария в ключ с номером поста (‘users:%username%:%post.id%’) и увеличиваем количество новых комментариев.
Данная функция сделана как сигнал и используется при сохранении комментария:
post_save.connect(add_comment, sender=Comment)
Для отображения количества новых комментариев у каждого поста, используется такой тэг:
register = template.Library() @register.tag(name='newcom_count') def get_count_newcom(parser, token): bits = token.split_contents() if len(bits) == 3: return NewcomCountNode(bits[1], bits[2]) elif len(bits) == 5 and bits[3] == 'as': return NewcomCountNode(bits[1], bits[2], bits[4]) else: raise template.TemplateSyntaxError, "%r tag requires a two argument, username and post_id" % token.contents.split()[0] class NewcomCountNode(template.Node): def __init__(self, username, count, varname=None): self.username = template.Variable(username) self.count = template.Variable(count) self.varname = varname def render(self, context): try: username = self.username.resolve(context) except template.VariableDoesNotExist: username = '' try: count = self.count.resolve(context) except template.VariableDoesNotExist: count = 1 if self.varname: context[self.varname] = get_post_newcom(username, count) return '' return get_post_newcom(username, count)
Он используется в темплейтах, как
{% newcom_count user.username post.id as newcom %}
<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px; white-space: normal;">Функция для работы с базой:</span>
def get_post_newcom(username, post_id): try: count = redis_db.scard('users:%s:%s' % (username, post_id)) except: count = 0 return int(count)
Она всего-лишь достает количество новых комментариев, если таких нет, то вернет 0.
В самом посте, чтобы узнать какой комментарий является новым, используем такую функцию:
def del_comment(post, username): if redis_db.srem('users:%s:posts' % username, post.id): comments = redis_db.smembers('users:%s:%s' % (username, post.id)) redis_db.delete('users:%s:%s' % (username, post.id)) count = int(redis_db.get('users:%s' % username)) redis_db.set('users:%s' % username, count - len(comments)) else: comments = [] return [int(x) for x in comments]
На вход она принимает instance post и имя пользователя, а на выход отдает список с id комментариев. В своей работе, она также удаляет из ключа новые посты. Передача его в шаблон, позволяет простой проверкой узнавать, новый или нет комментарий.
Я использую во вьюхе вывода поста:
newcomments = del_comment(post, request.user.username)
А в шаблоне делаю проверку:
{% if comment.id in newcom %}
На этом все и заканчивается. Как видно из кода, все достаточно просто, по сравнению с использованием реляционных баз.
Тэги: Django, Python, redis, комментари

