我已经构建了一个简单的Django照片应用程序。用户可以上传照片,关注其他用户和照片。为了处理用户之间的关系(跟随和取消关注),我使用了coleifer名为django-relationships的软件包。这是一个很棒的包,使用起来非常简单。
一切都按预期运作。我目前有一个工作活动Feed。
我将Feed过滤为两部分:关注(我关注的用户的所有活动)&你(发生在我身上的所有活动)。我在我的iOS应用程序下面发布了两张图片,它使用我的Django照片应用程序,因为它是后端:
我想要做的是将聚合添加到以下Feed中。正如你所看到的,用户alexperri喜欢5次射击。我想将所有这些项目合并为一行。我不需要为“你”提要添加聚合,因为我希望看到每个单独的操作都发生在我身上。但是对于以下Feed,添加聚合是有意义的。有几个应用程序可以很好地进行聚合。 Fashionlista,Pinterest& Instagram做得很好。这是一个来自Instagram的例子,展示了我想要实现的目标:
在上面的示例中,您可以看到以下Feed,而lovetoronto喜欢5张照片。我开始在播放后使用Instagram来查看它是如何工作的。 Instagram关注源显示最多35个活动条目,每个条目最多可包含该活动类型的5个活动。 “lovetoronto喜欢5张照片”是一个活动条目,它显示了他喜欢的最新5张照片。由于lovetoronto进行了最新的动作,他处于领先地位。
我想实现相同的设置。
这是我目前的模型设置:
models.py
from django.db import models
from django.contrib.auth.models import User
class Photographer(models.Model):
user = models.OneToOneField(User, primary_key=True
likes = models.ManyToManyField('Photo', through = 'Likes',
related_name = 'likedby', blank = True)
class Photo(models.Model):
photographer = models.ForeignKey(Photographer, related_name = 'shot_owner')
created = models.DateTimeField(auto_now_add=True)
url = models.CharField(max_length=128)
class Likes(models.Model):
liked_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
photographer = models.ForeignKey(Photographer, related_name = 'liked_by')
photo = models.ForeignKey(Photo, null=True)
class Activity(models.Model):
actor = models.ForeignKey(Photographer, related_name = 'actor')
receiver = models.ForeignKey(Photographer, related_name = 'receiver')
action = models.CharField(max_length=12)
post = models.ForeignKey(Photo, null=True, blank=True)
time = models.DateTimeField(auto_now_add=True)
每次创建'Like'对象时,我也会创建一个Activity对象,actor是执行操作的人,接收者是执行操作的人,操作(在这种情况下是字符串,'喜欢'),发布(照片)和创建活动对象的时间。
我使用django-tastypie获取并创建'Like'& '活动'对象。
api.py
from tastypie.resources import ModelResource, ALL, ALL_WITH_RELATIONS
from tastypie.authentication import BasicAuthentication
from tastypie.authorization import DjangoAuthorization, Authorization
from photoapp.photodb.models import *
from tastypie.serializers import Serializer
from relationships.utils import positive_filter
from relationships.models import Relationship
from relationships.models import RelationshipStatus
class LikeResource(ModelResource):
user = fields.ForeignKey(BasicUserResource, 'user', full=True)
class Meta:
queryset = Photographer.objects.all()
allowed_methods = ['put']
resource_name = 'like'
fields = ['user']
default_format = 'application/json'
authorization = Authorization()
authentication = BasicAuthentication()
serializer = Serializer(formats=['json'])
always_return_data = True
include_resource_uri = False
def hydrate(self, bundle):
shot = Photo.objects.all().get(id = bundle.data['photo id'])
user = Photographer.objects.all().get(user = bundle.request.user)
if(bundle.obj.likes.filter(id = bundle.data['photo id']).exists()):
Likes.objects.all().filter(photographer=user).filter(photo=shot).delete()
Activity.objects.filter(actor__user = bundle.request.user,
post = shot, action = 'liked').delete()
else:
like = Likes(photographer = user, photo=shot)
like.save()
user_doing_the_liking = User.objects.get(
username=bundle.request.user.username)
user = Photographer.objects.all().get(user = bundle.request.user)
user_getting_liked = shot.photographer.user
photographer_getting_liked = shot.photographer
newActivity = Activity()
newActivity.actor = user
newActivity.receiver = photographer_getting_liked
newActivity.action = 'liked'
newActivity.post = shot
newActivity.save()
return bundle
class FollowingFeed(ModelResource):
actor = fields.ForeignKey(BasicPhotographerResource, 'actor', full=True)
receiver = fields.ForeignKey(BasicPhotographerResource, 'receiver', full=True)
post = fields.ForeignKey(BasicPostResource, attribute = 'post', full=True, null=True)
class Meta:
queryset = Activity.objects.all()
allowed_methods = ['get']
resource_name = 'following-feed'
fields = ['actor', 'receiver', 'action', 'post', 'id', 'time']
default_format = "application/json"
authorization = Authorization()
authentication = BasicAuthentication()
serializer = Serializer(formats=['json'])
always_return_data = True
include_resource_uri = False
def get_object_list(self, request):
return super(FollowingFeed, self).get_object_list(request)\
.filter(actor__user__in = request.user.relationships.following())\
.exclude(receiver__user = request.user)\
.exclude(actor__user = request.user).order_by('-time')
如何以聚合活动对象的方式修改FollowingFeed资源?我遇到了Feedly项目。如何在当前设置中使用它?
答案 0 :(得分:2)
我认为您不想进行任何数据库级聚合,因为您可能希望显示各个详细信息以及计数,例如“X喜欢5张照片”并显示5张照片。根据定义,聚合将排除个别数据。
相反,你应该在Python代码中进行分组和排序(或Javascript,因为我认为你使用的是HTTP API,但我更喜欢服务器端的API,它已经有了组织的东西)。
itertools.groupby可能有所帮助。我认为你需要分组(用户和动作),然后按每组中第一项的时间戳排序,这样你就可以看到“乔喜欢5张照片”,“安妮发布了2张照片”,“乔发布了一个照片“,”克莱尔喜欢3张照片“等等。
答案 1 :(得分:0)
在您的Feed资源中,您已经覆盖了get_object_list,我建议更改逻辑以执行聚合逻辑的原始查询。
def get_object_list(self, request):
query = "Your aggregation query logic here"
feed_model = self._meta.object_class
return feed_model.objects.raw(query)
这应该做的是必要的。但是,您需要考虑您的查询逻辑。如果您遇到任何其他问题,请告诉我。
谢谢!
答案 2 :(得分:0)
我认为最好的方法是修改Activity表来存储分组的活动。当新操作发生时,检查相同类型的现有操作,并编辑记录以使其“分组”,或添加新记录。您可以将ManyToMany关系添加到包含相关记录的所有潜在表,或者只将数据存储在包含足够信息的json字段中,以便在不对其他表对象进行查询的情况下呈现Feed中的活动。
如果资源过于密集,您可以对新活动的添加/编辑进行排队。你可能最好不要让活动表只是一个在渲染时不需要任何处理的直接进给。这不是最容易实现的解决方案,但我认为从长远来看它是有道理的。
答案 3 :(得分:0)
设置聚合时间,例如,您想在10分钟内或过去24小时内汇总所有“喜欢”吗?
然后你可以按这个时间框架过滤你的对象。
然后,您可以使用.values('model__field')方法应用分组。 Django生成包含'GROUP BY'
的sql然后最后添加一个聚合限制,以便当喜欢的数量超过此限制时,您将显示聚合视图而不是单个活动视图。
以下示例(伪,非实际):
if (activity.object.filter(time__gt=yourlowertime, time__lt=youruppertime).values(‘activity__action').count() > aggregation_limit) :
# show aggregated view
else:
# show normal view