2013-12-03 10 views
17

Я создал простое приложение для фотографий Django. Пользователи могут загружать фотографии, следить за другими пользователями и фотографиями. Чтобы обрабатывать отношения между пользователями (после & unfollowing) Я использую пакет под названием django-relationships coleifer. Это отличный пакет и очень прост в использовании.Django Feed Feed (Feedly Integration?)

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

Я фильтрую фид на две части: Следуя (все действия пользователей, которых я придерживаюсь) & Вы (все действия, которые со мной произошли). Я отправил две фотографии ниже от моего приложения IOS, который использует мой Django приложение фото, как это фоновым:

enter image description here enter image description here

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

enter image description here

В приведенном выше примере, вы можете увидеть следующий канал и что 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) 

Каждый раз, когда «Как» объект создается, я создаю объект Activity, а актер будучи человеком кто сделал действие, получатель был человеком, к которому было выполнено действие, действие (в данном случае строка, «понравилось»), сообщение (фотография) и время создания объекта активности.

Я использую 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

Нужно ли вам группировать« действие »? Есть ли какой-либо другой тип действия, кроме 'понравился'? – mariodev

+0

@mariodev благодарит за быстрый ответ. Да, я хотел бы сгруппировать себя пользователем, а затем действия, которые они сделали. Также есть и другие действия. Существует следующее, комментирование и упоминание. Это в значительной степени. Поэтому вместо подачи, показывающего каждую из подобных действий alexperri, я хотел бы их заполнить. «Alexperri понравилось 5 фотографий» (Показывая последние 5 фотографий за последний час ... даже если Alexperri понравилось более 5 фотографий за последний час, я просто хочу показать последние 5). – noahandthewhale

+0

@mariodev, если alexperri понравилось 10 фотографий, alexperri последовал за бобом и кайлом в последний час. Я хотел бы отсортировать, какие действия были сделаны последними. Поэтому, если последнее действие, которое сделал alexperri, было «похоже», и если бы он был последним человеком всех людей, за которыми я слежу, кто сделал акцию, я бы проверил последние 100 мероприятий со всех людей, которых я придерживаюсь, возьмите последние 5 как действия от alexperri в этом списке и суммировать его (alexperri понравилось 5 фотографий).Затем я проверил бы, кто следующий человек из людей, которых я придерживаюсь, сделал второе последнее действие. Выполнить ту же логику. и т. д. – noahandthewhale

ответ

2

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

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

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

0

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

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

Спасибо!

+0

@Rahual Tanwani, спасибо за быстрый ответ, это логика запросов, которая заставляет меня почесывать мою голову, вот что я ищу – noahandthewhale

0

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

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

0

Укажите время агрегации, например, хотите ли вы объединить все «понравившиеся» в течение 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