Лента активности Django (интеграция с кормом?)

Я создал простое фото-приложение на Django. Пользователи могут загружать фотографии, подписываться на других пользователей и ставить лайки по фотографиям. Для управления отношениями между пользователями (подписок и отмены подписки) я использую пакет под названием django-Relations от coleifer . Это отличный пакет и очень простой в использовании.

Все работает как надо. В настоящее время у меня есть лента о рабочей активности.

Я разделяю ленту на два раздела: «Подписка» (все действия пользователей, на которых я подписан) и «Вы» (все действия, которые произошли со мной). Ниже я разместил две картинки из моего приложения для iOS, которое использует мое фото-приложение Django в качестве серверной части:

введите описание изображения здесь введите описание изображения здесь

Что я хотел бы сделать, так это добавить агрегирование в следующий канал. Как видите, пользователю alexperri понравилось 5 снимков. Я хотел бы объединить все эти элементы в одну строку. Мне не нужно добавлять агрегацию для ленты «Вы», так как я хотел бы видеть каждое отдельное действие, происходящее со мной. Однако для следующего канала имеет смысл добавить агрегирование. Есть несколько приложений, которые очень хорошо выполняют агрегирование. Fashionlista, Pinterest и Instagram делают это хорошо. Вот пример из Instagram, чтобы показать, чего я пытаюсь достичь:

введите описание изображения здесь

В приведенном выше примере вы можете увидеть следующую ленту, и что 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, причем актером является лицо, выполнившее действие, а получателем - человек, которому было выполнено действие, действие (в данном случае строка, понравилось '), пост (фото) и время создания объекта активности.

Я использую django-deliciouspie для получения и создания объектов «Нравится» и «Действия».

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') 

Как я могу изменить ресурс FollowFeed таким образом, чтобы он агрегировал объекты деятельности? Я наткнулся на проект Feedly. Как я могу использовать его с моей текущей настройкой?


person deadlock    schedule 03.12.2013    source источник
comment
Вам также нужно сгруппировать по action? Есть ли другие типы действий, кроме liked?   -  person mariodev    schedule 12.12.2013
comment
@mariodev благодарит за быстрый ответ. Да, я хотел бы сгруппировать пользователя, а затем действия, которые они сделали. Также есть другие действия. Есть подписка, комментарии и упоминания. Это почти все. Поэтому вместо фида, показывающего все похожие действия Александра, я хотел бы их объединить. Алексперри понравились 5 фотографий (показаны 5 последних фотографий за последний час ... даже если Алексперри понравилось больше 5 фотографий за последний час, я просто хочу показать 5 последних).   -  person deadlock    schedule 12.12.2013
comment
@mariodev, если Алексперри понравились 10 фотографий, а Александр следил за Бобом и Кайлом за последний час. Я хотел бы отсортировать, какое действие было выполнено последним. Итак, если последнее действие, которое совершил Алексперри, было лайком, и если бы он был последним человеком из всех людей, за которыми я следую, которые совершили действие, я бы проверил последние 100 действий всех людей, на которых я подписан, захватил последние 5 подобных действий от alexperri в этом списке и объедините его (alexperri понравилось 5 фотографий). Затем я проверял, кто из людей, за которыми я следую, совершил второе последнее действие. Выполните ту же логику. и т.д...   -  person deadlock    schedule 12.12.2013
comment
Итак, как же работает остальная часть действия ... в случае подписки, вы перечисляете последних подписанных пользователей? Что вы перечисляете для каждого типа действия? Мне кажется, что таблица Activity используется только для лайков ..   -  person mariodev    schedule 12.12.2013
comment
@mariodev зацените LikeResource, где я делаю newActivity = Activity (). Я делаю то же самое для FollowResource, CommentResource и MentioningResource. Я просто не стал добавлять эти ресурсы в вопрос, так как вопрос был бы слишком большим. Я хотел сделать вопрос простым. Так, например, в MentioningResource я выполняю логику, необходимую для упоминания, а затем выполняю newActivity = Activity (). newActivity.action будет «упоминать». Получатель NewActivity = user_being_mentioned. newActivity.post = выстрел. newActivity.save () Есть ли в этом смысл? дайте мне знать, если я не понимаю   -  person deadlock    schedule 16.12.2013
comment
@noahandthewhale Хороший у вас проект. Я тоже работаю над приложением с бэкэндом Django. Вы используете какую-либо библиотеку для доступа к API? Какие-нибудь рекомендации?   -  person webjunkie    schedule 28.08.2014
comment
@webjunkie привет! Я использую в основном json-модуль python. Используя json.loads и json.dumps и заключив его в HttpResponse. Вот это тебе поможет. Взгляните на ответ: stackoverflow.com/questions/2428092/. Если вам нужна помощь, просто напишите мне в Твиттере [at] riegiegodwin   -  person deadlock    schedule 02.09.2014


Ответы (4)


Я не думаю, что вы хотите проводить агрегацию на уровне базы данных, потому что вы, вероятно, хотите показать отдельные детали, а также счетчики, например. «Х лайк 5 фото» и покажи 5 фото. Агрегация по определению исключает отдельные данные.

Вместо этого вам следует выполнять группировку и сортировку в коде Python (или Javascript, поскольку я думаю, что вы используете HTTP API, но я бы предпочел API на стороне сервера, в котором все уже было организовано).

Может помочь itertools.groupby. Я думаю, вам нужно сгруппировать по (пользователю и действию), а затем отсортировать по метке времени первого элемента в каждой группе, чтобы вы могли увидеть «Джо понравились 5 фотографий», «Энн опубликовала 2 фотографии», «Джо опубликовал фото »,« Клэр понравились 3 фотографии »и т. д.

person spookylukey    schedule 08.08.2014

В своем ресурсе канала вы уже переопределяете 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)

Это должно сделать то, что требуется. Однако вам нужно подумать о логике вашего запроса. Сообщите мне, если у вас возникнут другие проблемы.

Спасибо!

person Rahul Tanwani    schedule 10.12.2013
comment
@Rahual Tanwani, спасибо за быстрый ответ, это логика запроса, которая заставляет меня чесать голову, это то, что я ищу - person deadlock; 10.12.2013

Я думаю, что лучший способ сделать это - изменить таблицу действий для хранения сгруппированных действий. Когда происходит новое действие, проверьте существующие действия того же типа и либо отредактируйте запись, чтобы сделать ее «сгруппированной», либо добавьте новую запись. Вы можете добавить отношения ManyToMany ко всей потенциальной таблице, содержащей связанные записи, или просто сохранить данные в поле json, которое содержит достаточно информации для отображения активности в фиде, не выполняя запросов к другим объектам таблицы.

Если это слишком ресурсоемко, вы можете поставить в очередь добавление / редактирование новых действий. Вероятно, вам лучше оставить таблицу действий просто прямым потоком, который не требует никакой обработки при рендеринге. Это не самое простое решение для реализации, но я думаю, что в конечном итоге оно имеет смысл.

person Pierre Drescher    schedule 16.12.2013

Установите время агрегирования, например, хотите ли вы агрегировать все «лайки» за 10-минутный период или за последние 24 часа?

Затем вы можете отфильтровать свой объект по этому временному интервалу.

Затем вы можете применить группировку с помощью метода .values ​​(‘model__field’). Django генерирует sql, содержащий "GROUP BY"

И, наконец, добавьте ограничение на агрегацию, чтобы, когда количество лайков превышает этот предел, вы отображали агрегированное представление, а не отдельное представление активности.

Пример ниже (псевдо, не актуально):

if (activity.object.filter(time__gt=yourlowertime, time__lt=youruppertime).values(‘activity__action').count() > aggregation_limit) :
     # show aggregated view
else:
     # show normal view
person HungryArthur    schedule 24.01.2014