From 891b122d295000308e62f2633c4cdc64f036eebc Mon Sep 17 00:00:00 2001 From: killpanda Date: Sun, 13 Sep 2015 15:44:41 +0800 Subject: [PATCH 1/6] Created a pluggable Like model --- firefly/models/like.py | 63 +++++++++++++++++++++++++++++++++++++++++ firefly/models/topic.py | 16 +++++++++++ 2 files changed, 79 insertions(+) create mode 100644 firefly/models/like.py diff --git a/firefly/models/like.py b/firefly/models/like.py new file mode 100644 index 0000000..c8e9535 --- /dev/null +++ b/firefly/models/like.py @@ -0,0 +1,63 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import datetime + +from firefly.ext import db +from .user import User + + +class Likes(object): + def __init__(self, product_type): + self.product_type = product_type + self._instance = None + + def __get__(self, instance, owner): + self._instance = instance + return self + + def __len__(self): + return Like.objects( + product_id=self.product_id, + product_type=self.product_type + ).count() + + def productidgetter(self, func): + return func + + @property + def product_id(self): + return str(self.productidgetter(self._instance)) + + def add(self, user_id): + user = User.objects(id=user_id).first() + if user: + return Like.objects.create( + product_id=self.product_id, + product_type=self.product_type, + user=user + ) + + def delete(self, user_id): + user = User.objects(id=user_id).first() + if user: + like = Like.objects.filter( + product_id=self.product_id, + product_type=self.product_type, + user=user + ).first() + if like: + like.delete() + + +class Like(db.Document): + id = db.SequenceField(primary_key=True) + created_at = db.DateTimeField(default=datetime.utcnow, required=True) + product_type = db.StringField(required=True) + product_id = db.StringField(required=True, + unique_with=['user', 'product_type']) + user = db.ReferenceField(User) + + meta = { + 'indexes': ['product_type', 'product_id'] + } diff --git a/firefly/models/topic.py b/firefly/models/topic.py index cac1163..636d95b 100644 --- a/firefly/models/topic.py +++ b/firefly/models/topic.py @@ -10,6 +10,7 @@ from firefly.views.utils import timesince from firefly.models.consts import CATEGORY_COLORS from .user import User +from .like import Likes __all__ = ["Category", "Post", "Video", "Image", "Comment"] @@ -58,6 +59,12 @@ class Post(db.Document): comments = db.ListField(db.ReferenceField('Comment')) category = db.ReferenceField(Category) + likes = Likes('Post') + + @likes.productidgetter + def product_id(self): + return self.id + def url(self): return url_for('post.detail', id=self.id) @@ -84,14 +91,17 @@ def recent_activity_time(self): class Video(Post): + likes = Likes('Video') embed_code = db.StringField(required=True) class Image(Post): + likes = Likes('Image') image_url = db.StringField(required=True, max_length=255) class Quote(Post): + likes = Likes('Quote') content = db.StringField(required=True) author = db.ReferenceField(User) @@ -103,6 +113,12 @@ class Comment(db.Document): author = db.ReferenceField(User) ref_id = db.IntField(default=0) + likes = Likes('Comment') + + @likes.productidgetter + def product_id(self): + return str(self.id) + @property def post_type(self): return self.__class__.__name__ From 1ce2f5d8a40666fbf1f812f587d39598c1d2567e Mon Sep 17 00:00:00 2001 From: killpanda Date: Tue, 15 Sep 2015 17:59:06 +0800 Subject: [PATCH 2/6] Add like, unlike url route --- firefly/views/api/__init__.py | 2 ++ firefly/views/api/topic.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 firefly/views/api/topic.py diff --git a/firefly/views/api/__init__.py b/firefly/views/api/__init__.py index ac4e80b..c247299 100644 --- a/firefly/views/api/__init__.py +++ b/firefly/views/api/__init__.py @@ -6,6 +6,7 @@ from .category import CategoryApi, CategoryListApi from .comment import ReplyApi from .user import FollowUserApi, BlockUserApi +from .topic import LikePostApi bp = Blueprint('api', __name__, url_prefix='/api') api = Api(bp) @@ -14,3 +15,4 @@ api.add_resource(FollowUserApi, '/users//follow') api.add_resource(BlockUserApi, '/users//block') api.add_resource(ReplyApi, '/posts//replies') +api.add_resource(LikePostApi, '/posts//like', endpoint='like') diff --git a/firefly/views/api/topic.py b/firefly/views/api/topic.py new file mode 100644 index 0000000..4c99c2d --- /dev/null +++ b/firefly/views/api/topic.py @@ -0,0 +1,26 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask_restful import Resource +from flask_security import login_required +from flask_login import current_user + +from firefly.models.topic import Post + + +class LikePostApi(Resource): + + method_decorators = [login_required] + + def put(self, id): + post = Post.objects.get_or_404(id=id) + if post: + post.likes.add(current_user.id) + return '', 201 + + def delete(self, id): + post = Post.objects.get_or_404(id=id) + if post: + post.likes.delete(current_user.id) + return '', 201 From 9dd43847a665eddeb8bded901ba2adcefb94d435 Mon Sep 17 00:00:00 2001 From: killpanda Date: Tue, 15 Sep 2015 18:00:37 +0800 Subject: [PATCH 3/6] Add unit test for like feature --- tests/test_like.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 tests/test_like.py diff --git a/tests/test_like.py b/tests/test_like.py new file mode 100644 index 0000000..01375ce --- /dev/null +++ b/tests/test_like.py @@ -0,0 +1,60 @@ +# coding: utf-8 + +from __future__ import absolute_import +import pytest + +from flask import url_for +from flask_login import current_user + +from firefly.models.user import User +from firefly.models.topic import Category, Post + + +@pytest.mark.usefixtures('client_class') +class TestLike: + + users = [] + + def setup(self): + c = Category.objects.create( + name='python', description='描述', _slug='python-slug' + ) + Post.objects.create( + title='标题test', content='内容test', category=c + ) + + self.users = [] + for x in range(3): + self.users.append( + User.create_user( + username='user' + str(x), + password='password123', + email='user' + str(x) + '@firefly.dev' + ) + ) + + def login(self, email): + form = { + 'email': email, + 'password': 'password123' + } + self.client.post( + url_for('home.login'), data=form, + follow_redirects=True + ) + assert current_user.is_authenticated() + + def test_like(self): + post = Post.objects.first() + assert len(post.likes) == 0 + + for user in self.users: + self.login(user.email) + url = url_for('api.like', id=post.id) + rv = self.client.put(url) + assert rv.status_code == 201 + assert len(post.likes) == len(self.users) + + rv = self.client.delete(url) + assert rv.status_code == 201 + assert len(post.likes) == 2 From 08b07d0c82c9118681c952765f27b2f1a649e340 Mon Sep 17 00:00:00 2001 From: killpanda Date: Sat, 26 Sep 2015 16:22:01 +0800 Subject: [PATCH 4/6] refactory Like model --- firefly/models/like.py | 17 ++++++----------- firefly/models/topic.py | 15 ++------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/firefly/models/like.py b/firefly/models/like.py index c8e9535..ed0d101 100644 --- a/firefly/models/like.py +++ b/firefly/models/like.py @@ -8,12 +8,14 @@ class Likes(object): - def __init__(self, product_type): - self.product_type = product_type - self._instance = None + + def __init__(self): + self.product_id = None + self.product_type = None def __get__(self, instance, owner): - self._instance = instance + self.product_id = str(instance.id) + self.product_type = owner.__name__ return self def __len__(self): @@ -22,13 +24,6 @@ def __len__(self): product_type=self.product_type ).count() - def productidgetter(self, func): - return func - - @property - def product_id(self): - return str(self.productidgetter(self._instance)) - def add(self, user_id): user = User.objects(id=user_id).first() if user: diff --git a/firefly/models/topic.py b/firefly/models/topic.py index 636d95b..c11cec4 100644 --- a/firefly/models/topic.py +++ b/firefly/models/topic.py @@ -59,11 +59,7 @@ class Post(db.Document): comments = db.ListField(db.ReferenceField('Comment')) category = db.ReferenceField(Category) - likes = Likes('Post') - - @likes.productidgetter - def product_id(self): - return self.id + likes = Likes() def url(self): return url_for('post.detail', id=self.id) @@ -91,17 +87,14 @@ def recent_activity_time(self): class Video(Post): - likes = Likes('Video') embed_code = db.StringField(required=True) class Image(Post): - likes = Likes('Image') image_url = db.StringField(required=True, max_length=255) class Quote(Post): - likes = Likes('Quote') content = db.StringField(required=True) author = db.ReferenceField(User) @@ -113,11 +106,7 @@ class Comment(db.Document): author = db.ReferenceField(User) ref_id = db.IntField(default=0) - likes = Likes('Comment') - - @likes.productidgetter - def product_id(self): - return str(self.id) + likes = Likes() @property def post_type(self): From 91a6fa7e0f059fc9e12ba0c26c09815c972caa70 Mon Sep 17 00:00:00 2001 From: killpanda Date: Sat, 26 Sep 2015 16:38:19 +0800 Subject: [PATCH 5/6] change like api response status code --- firefly/models/like.py | 6 ++---- firefly/views/api/topic.py | 10 ++++------ tests/test_like.py | 6 +++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/firefly/models/like.py b/firefly/models/like.py index ed0d101..04592ce 100644 --- a/firefly/models/like.py +++ b/firefly/models/like.py @@ -36,13 +36,11 @@ def add(self, user_id): def delete(self, user_id): user = User.objects(id=user_id).first() if user: - like = Like.objects.filter( + Like.objects.filter( product_id=self.product_id, product_type=self.product_type, user=user - ).first() - if like: - like.delete() + ).delete() class Like(db.Document): diff --git a/firefly/views/api/topic.py b/firefly/views/api/topic.py index 4c99c2d..761f8e3 100644 --- a/firefly/views/api/topic.py +++ b/firefly/views/api/topic.py @@ -15,12 +15,10 @@ class LikePostApi(Resource): def put(self, id): post = Post.objects.get_or_404(id=id) - if post: - post.likes.add(current_user.id) - return '', 201 + post.likes.add(current_user.id) + return '', 202 def delete(self, id): post = Post.objects.get_or_404(id=id) - if post: - post.likes.delete(current_user.id) - return '', 201 + post.likes.delete(current_user.id) + return '', 204 diff --git a/tests/test_like.py b/tests/test_like.py index 01375ce..d696d2c 100644 --- a/tests/test_like.py +++ b/tests/test_like.py @@ -52,9 +52,9 @@ def test_like(self): self.login(user.email) url = url_for('api.like', id=post.id) rv = self.client.put(url) - assert rv.status_code == 201 + assert rv.status_code == 202 assert len(post.likes) == len(self.users) - rv = self.client.delete(url) - assert rv.status_code == 201 + rv = self.client.delete(url, buffered=True) + assert rv.status_code == 204 assert len(post.likes) == 2 From 2cb3c871bf6846f68f5c3e193179e8154f458e06 Mon Sep 17 00:00:00 2001 From: killpanda Date: Thu, 1 Oct 2015 20:13:09 +0800 Subject: [PATCH 6/6] add __getitem__ to Likes Model --- firefly/models/like.py | 6 ++++++ tests/test_like.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/firefly/models/like.py b/firefly/models/like.py index 04592ce..e19adb4 100644 --- a/firefly/models/like.py +++ b/firefly/models/like.py @@ -24,6 +24,12 @@ def __len__(self): product_type=self.product_type ).count() + def __getitem__(self, position): + return Like.objects( + product_id=self.product_id, + product_type=self.product_type, + )[position] + def add(self, user_id): user = User.objects(id=user_id).first() if user: diff --git a/tests/test_like.py b/tests/test_like.py index d696d2c..1e585de 100644 --- a/tests/test_like.py +++ b/tests/test_like.py @@ -55,6 +55,10 @@ def test_like(self): assert rv.status_code == 202 assert len(post.likes) == len(self.users) + assert post.likes[0].user == self.users[0] + assert post.likes[1].user == self.users[1] + assert post.likes[2].user == self.users[2] + rv = self.client.delete(url, buffered=True) assert rv.status_code == 204 assert len(post.likes) == 2