MattBroach / DjangoRestMultipleModels

View (and mixin) for serializing multiple models or querysets in Django Rest Framework
MIT License
549 stars 67 forks source link

Limit and include result count in flat=False response? #20

Closed softzer0 closed 7 years ago

softzer0 commented 8 years ago

Hi, is there a possibility of setting limit to the querysets, and then including in the response result count of the each queryset in the list, when flat is set to False? Here's an example showing that:

[
    {
        'play' : [
                {'genre': 'Comedy', 'title': "A Midsummer Night's Dream", 'pages': 350},
                {'genre': 'Tragedy', 'title': "Romeo and Juliet", 'pages': 300}
            ],
        count: 20
    },
    {
        'poem' : [
                {'title': 'Shall I compare thee to a summer's day?', 'stanzas': 1},
                {'title': 'As a decrepit father takes delight', 'stanzas': 1}
            ],
        count: 14
    }
]

(In that example, the results are limited to be maximum 2 from the each queryset.)

It would be very useful to have such feature because I want to make in front-end part of the site, to check whether each type has more results to be shown. I doubt it can be made based on pagination (since that is still not supported for this response type; but it would be really great if it becomes possible to be used with PageNumerPagination simultaneously on the each queryset) so I proposed this way of returning only a result count - the same what LimitOffsetPagination partly does, but it shouldn't cause a hassle to get that information.

MattBroach commented 8 years ago

Hey @MikiSoft: the limits themselves could be put directly into the querylist:

queryList = (
     (Play.objects.all()[:2], PlaySerializer),
     (Poem.objects.filter(style="Sonnet")[:2], PoemSerializer)
)

You could then overwrite the format_data function to add the counts to the queries. That could look something like this, all together:

class CountView(MultipleModelAPIView):
    queryList = ((Play.objects.all()[:2], PlaySerializer),
            (Poem.objects.filter(style="Sonnet")[:2], PoemSerializer))

    def format_data(self, new_data, query, results):
        label = query.queryset.model.__name__.lower()

        new_data = {label: new_data}
        new_data['count'] = query.queryset.model.objects.count()

        results.append(new_data)

        return results

That would match your desired output, I think. Let me know if that works for you.

softzer0 commented 7 years ago

Thanks, but what I want is to return count of the querysets (which will be always filtered), not of all objects on the targeted models. Sorry for not pointing out that.

softzer0 commented 7 years ago

I've solved it somehow, with a bit of monkey patching around and with a different kind of indicator:

from drf_multiple_model.mixins import Query

def has_more(queryset, request, *args, **kwargs):
    request.query_params[None] = queryset.count() == 3
    return queryset[:2]

class CountView(MultipleModelAPIView):
    def get_queryList(self):
        self.request.query_params._mutable = True

        return [Query(Play.objects.all()[:3], PlaySerializer, filter_fn=has_more),
                Query(Poem.objects.all()[:3], PoemSerializer, filter_fn=has_more)]

    def format_data(self, new_data, query, results):
        label = query.queryset.model.__name__.lower()

        new_data = {label: new_data}
        new_data['has_more'] = self.request.query_params.pop(None)

        results.append(new_data)

        return results

I still don't know how to solve efficiently for count, though - I think that due to the limitations in the database language itself it's unsolvable without evaluating the whole query before limiting it, like in the code above. But, in that case it would be maximum performance hit it could possibly have, unfortunately. Anyways, I'm closing now this issue. Thanks for the assistance!