Подтвердить что ты не робот

Ядерная активность Django (интегрированная интеграция?)

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

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

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

enter image description hereenter image description here

Что я хотел бы сделать, это добавить агрегацию в следующий канал. Как вы можете видеть, пользователю 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)

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

Я использую django-tastypie для создания и создания объектов "Like" и "Activity".

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

Спасибо!

Ответ 3

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

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

Ответ 4

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