def push_tweet_to_cache(sender, instance, created, **kwargs): # 只有新建时才push, update时并不push if not created: return from tweets.services import TweetService TweetService.push_tweet_to_cache(instance)
def create(self, validated_data): user = self.context['request'].user content = validated_data['content'] tweet = Tweet.objects.create(user=user, content=content) if validated_data.get('files'): TweetService.create_tweet_photos(tweet, validated_data['files']) return tweet
def list(self, request, *args, **kwargs): """ 重载 list 方法,不列出所有 tweets,必须要求指定 user_id 作为筛选条件 """ user_id = request.query_params['user_id'] cached_tweets = TweetService.get_cached_tweet(user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: # 这句查询会被翻译为 # select * from twitter_tweets # where user_id = xxx # order by created_at desc # 这句 SQL 查询会用到 user 和 created_at 的联合索引 # 单纯的 user 索引是不够的 queryset = Tweet.objects.filter( user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) serializer = TweetSerializer( page, context={'request': request}, many=True, ) # 一般来说 json 格式的 response 默认都要用 hash 的格式 # 而不能用 list 的格式(约定俗成) return self.get_paginated_response(serializer.data)
def list(self, request, *args, **kwargs): """ overloading list function, make user_id as a requirement to filter """ user_id = request.query_params['user_id'] tweets = Tweet.objects.filter(user_id=user_id).prefetch_related('user') cached_tweets = TweetService.get_cached_tweets(user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: """ if 'user_id' not in request.query_params: return Response('missing user_id', status=400) The above line will be interpreted as following SQL:' select * from twitter_tweets where user_id=xxx order by created_at DESC this SQL query will be using composite index of user_id and created_at """ queryset = Tweet.objects.filter( user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) serializer = TweetSerializer( page, context={'request': request}, many=True, ) # usually response in JSON format should be included in hash # instead of a list return self.get_paginated_response(serializer.data)
def list(self, request, *args, **kwargs): tweets = TweetService.get_cached_tweets( user_id=request.query_params['user_id']) paginated_tweets = self.paginate_queryset(tweets) serializer = TweetSerializersWithCommentsAndLikes( paginated_tweets, context={'request': request}, many=True, ) return self.get_paginated_response(serializer.data)
def test_create_new_tweet_before_get_cached_tweets(self): tweet1 = self.create_tweet(self.user1, 'test tweet') RedisClient.clear() conn = RedisClient.get_connection() key = USER_TWEET_PATTERN.format(user_id=self.user1.id) self.assertEqual(conn.exists(key), False) tweet2 = self.create_tweet(self.user1, 'another tweet') self.assertEqual(conn.exists(key), True) tweets = TweetService.get_cached_tweets(self.user1) self.assertEqual([tweet.id for tweet in tweets], [tweet2.id, tweet1.id])
def test_create_tweet_before_get_cached_tweets(self): tweet1 = self.create_tweet(user=self.user1) RedisClient.clear() conn = RedisClient.get_connection() name = USER_TWEET_PATTERN.format(user_id=self.user1.id) self.assertFalse(conn.exists(name)) tweet2 = self.create_tweet(user=self.user1) self.assertTrue(conn.exists(name)) tweets = TweetService.load_tweets_through_cache(user_id=self.user1.id) self.assertEqual([t.id for t in tweets], [tweet2.id, tweet1.id])
def test_get_user_tweets(self): tweet_ids = [] for i in range(3): tweet = self.create_tweet(self.linghu, 'tweet {}'.format(i)) tweet_ids.append(tweet.id) tweet_ids = tweet_ids[::-1] RedisClient.clear() conn = RedisClient.get_connection() # cache miss tweets = TweetService.get_cached_tweets(self.linghu.id) self.assertEqual([t.id for t in tweets], tweet_ids) # cache hit tweets = TweetService.get_cached_tweets(self.linghu.id) self.assertEqual([t.id for t in tweets], tweet_ids) # cache updated new_tweet = self.create_tweet(self.linghu, 'new tweet') tweets = TweetService.get_cached_tweets(self.linghu.id) tweet_ids.insert(0, new_tweet.id) self.assertEqual([t.id for t in tweets], tweet_ids)
def list(self, request, *args, **kwargs): user_id = request.query_params['user_id'] cached_tweets = TweetService.get_cached_tweets(user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: queryset = Tweet.objects.filter( user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) serializer = TweetSerializer( page, context={'request': request}, many=True, ) return self.get_paginated_response(serializer.data)
def test_tweet_cache_limit_pagination(self): limit_size = settings.REDIS_LIST_LENGTH_LIMIT page_size = 20 tweets = [ self.create_tweet(user=self.user1) for i in range(limit_size + page_size) ] tweets = tweets[::-1] # cached tweets cached_tweets = TweetService.load_tweets_through_cache( user_id=self.user1.id) self.assertEqual(len(cached_tweets), limit_size) db_tweets = Tweet.objects.filter(user_id=self.user1.id) self.assertEqual(len(db_tweets), limit_size + page_size) # paginate all tweets paginated_tweets = self._paginate_to_get_tweets( client=self.user2_client, user_id=self.user1.id, ) self.assertEqual(len(paginated_tweets), limit_size + page_size) self.assertEqual([t['id'] for t in paginated_tweets], [t.id for t in tweets]) # create new tweet new_tweet = self.create_tweet(user=self.user1) def _test_tweets_after_pushed_new_tweet(): new_paginated_tweets = self._paginate_to_get_tweets( client=self.user2_client, user_id=self.user1.id, ) self.assertEqual(len(new_paginated_tweets), limit_size + page_size + 1) self.assertEqual(new_paginated_tweets[0]['id'], new_tweet.id) for i in range(limit_size + page_size): self.assertEqual(tweets[i].id, new_paginated_tweets[i + 1]['id']) _test_tweets_after_pushed_new_tweet() # cache expire self.clear_cache() _test_tweets_after_pushed_new_tweet()
def list(self, request, *args, **kwargs): user_id = request.query_params['user_id'] tweets = Tweet.objects.filter(user_id=user_id).prefetch_related('user') cached_tweets = TweetService.get_cached_tweets(user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: # select * from twitter_tweets where user_id = xxx # order by created_at desc queryset = Tweet.objects.filter(user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) serializer = TweetSerializer( page, context={'request': request}, many=True, ) return self.get_paginated_response(serializer.data)
def list(self, request, *args, **kwargs): user_id = request.query_params['user_id'] cached_tweets = TweetService.get_cached_tweets(user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: # 这句查询会被翻译为 # select * from twitter_tweets # where user_id = xxx # order by created_at desc # 这句 SQL 查询会用到 user 和 created_at 的联合索引 # 单纯的 user 索引是不够的 queryset = Tweet.objects.filter( user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) # many = true means it will return a list of dict serializer = TweetSerializer(page, context={'request': request}, many=True) return self.get_paginated_response(serializer.data)
def list(self, request): # select out all tweets of a specific user cached_tweets = TweetService.load_tweets_through_cache( user_id=request.query_params['user_id'] ) page = self.paginator.paginate_cached_list(cached_tweets, request) # cache not enough if not page: tweets = Tweet.objects.filter( user_id=request.query_params['user_id'] ) page = self.paginate_queryset(tweets) serializer = TweetSerializer( page, context={'request': request}, many=True, ) # wrap the list of tweet contents in endless pagination return self.get_paginated_response(serializer.data)
def list(self, request): """ 重载 list 方法,不列出所有 tweets,必须要求指定 user_id 作为筛选条件 """ user_id = request.query_params['user_id'] # get tweets from redis cache instead of DB cached_tweets = TweetService.get_cached_tweets(user_id=user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: queryset = Tweet.objects.filter(user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) serializer = TweetSerializer( page, context={'request': request}, many=True, ) # 一般来说 json 格式的 response 默认都要用 hash 的格式 # 而不能用 list 的格式(约定俗成) # return Response({'tweets': serializer.data}) return self.get_paginated_response(serializer.data)
def list(self, request, *args, **kwargs): user_id = request.query_params['user_id'] cached_tweets = TweetService.get_cached_tweets(user_id) page = self.paginator.paginate_cached_list(cached_tweets, request) if page is None: # 这句查询会被翻译为 # select * from twitter_tweets # where user_id = xxx # order by created_at desc # 这句sql查询语句会用到user和created_at的联合索引 # 单纯的user索引是不够的 queryset = Tweet.objects.filter( user_id=user_id).order_by('-created_at') page = self.paginate_queryset(queryset) serializer = TweetSerializer( page, context={'request': request}, many=True, ) # 传进去是是QuerySet,返回的是一个list of dict # 一般来说,json格式的response默认都要用的hash格式 # 而不能用list的格式(约定俗成) # 所以在最外面需要套上一个dict return self.get_paginated_response(serializer.data)
def push_tweet_to_cache(sender, instance, created, **kwargs): if not created: return from tweets.services import TweetService TweetService.push_tweets_to_cache(instance)