diff --git a/firefly/models/like.py b/firefly/models/like.py new file mode 100644 index 0000000..e19adb4 --- /dev/null +++ b/firefly/models/like.py @@ -0,0 +1,62 @@ +# 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): + self.product_id = None + self.product_type = None + + def __get__(self, instance, owner): + self.product_id = str(instance.id) + self.product_type = owner.__name__ + return self + + def __len__(self): + return Like.objects( + product_id=self.product_id, + 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: + 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.objects.filter( + product_id=self.product_id, + product_type=self.product_type, + user=user + ).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..c11cec4 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,8 @@ class Post(db.Document): comments = db.ListField(db.ReferenceField('Comment')) category = db.ReferenceField(Category) + likes = Likes() + def url(self): return url_for('post.detail', id=self.id) @@ -103,6 +106,8 @@ class Comment(db.Document): author = db.ReferenceField(User) ref_id = db.IntField(default=0) + likes = Likes() + @property def post_type(self): return self.__class__.__name__ 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..761f8e3 --- /dev/null +++ b/firefly/views/api/topic.py @@ -0,0 +1,24 @@ +# 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) + post.likes.add(current_user.id) + return '', 202 + + def delete(self, id): + post = Post.objects.get_or_404(id=id) + post.likes.delete(current_user.id) + return '', 204 diff --git a/tests/test_like.py b/tests/test_like.py new file mode 100644 index 0000000..1e585de --- /dev/null +++ b/tests/test_like.py @@ -0,0 +1,64 @@ +# 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 == 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