[RFC 09/11] REST: Add query parameters to filter patches by

Finucane, Stephen stephen.finucane at intel.com
Tue May 10 00:06:55 AEST 2016


On 15 Apr 13:24, Andy Doan wrote:
> This adds generic filtering support to the PatchworkViewSet as well as
> useful filtering options for listing patches.
> 
> DRF has some filter capabilities, but they can really slow down the web
> interface for large data sets and are a little complex. This approach is
> pretty simple and works efficiently.
> 
> The "options" method is now overridden to show the user available
> filters when supported.
> 
> Signed-off-by: Andy Doan <andy.doan at linaro.org>
> ---
>  patchwork/tests/test_rest_api.py | 24 ++++++++++++++++++++++++
>  patchwork/views/rest_api.py      | 38 +++++++++++++++++++++++++++++++++++++-
>  2 files changed, 61 insertions(+), 1 deletion(-)
> 
> diff --git a/patchwork/tests/test_rest_api.py b/patchwork/tests/test_rest_api.py
> index fe21fcf..037c4e8 100644
> --- a/patchwork/tests/test_rest_api.py
> +++ b/patchwork/tests/test_rest_api.py
> @@ -17,6 +17,7 @@
>  # along with Patchwork; if not, write to the Free Software
>  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
>  
> +import datetime
>  import unittest
>  
>  from django.conf import settings
> @@ -158,6 +159,29 @@ class TestPatchAPI(APITestCase):
>          patch = resp.data['results'][0]
>          self.assertEqual(patches[0].name, patch['name'])
>  
> +    def test_query_filters(self):
> +        """Test out our filtering support."""
> +        patches = create_patches(4)
> +        project = Project.objects.create(
> +            linkname='foo', name='Foo', listid='foo.example.com')
> +        patches[0].project = project
> +        patches[0].save()
> +        resp = self.client.get('/api/1.0/patches/?project=Foo')

I have a feeling we've discussed this before (and it doesn't really
belong in this patch), but why are we not nesting patches under
projects? Seems like a clear hierarchy to me.

> +        self.assertEqual(status.HTTP_200_OK, resp.status_code)
> +        self.assertEqual(1, resp.data['count'])
> +        self.assertEqual(patches[0].id, resp.data['results'][0]['id'])
> +
> +        ts = datetime.datetime.now().isoformat()
> +        new_patches = create_patches(2)
> +
> +        resp = self.client.get('/api/1.0/patches/?until=%s' % ts)
> +        self.assertEqual(status.HTTP_200_OK, resp.status_code)
> +        self.assertEqual(len(patches), resp.data['count'])
> +
> +        resp = self.client.get('/api/1.0/patches/?since=%s' % ts)
> +        self.assertEqual(status.HTTP_200_OK, resp.status_code)
> +        self.assertEqual(len(new_patches), resp.data['count'])
> +
>      def test_get(self):
>          """Validate we can get a specific project."""
>          patches = create_patches()
> diff --git a/patchwork/views/rest_api.py b/patchwork/views/rest_api.py
> index 2e7fcbb..ae86627 100644
> --- a/patchwork/views/rest_api.py
> +++ b/patchwork/views/rest_api.py
> @@ -63,8 +63,25 @@ class AuthenticatedReadOnly(permissions.BasePermission):
>  class PatchworkViewSet(ModelViewSet):
>      pagination_class = PageSizePagination
>  
> +    # a dict of the request-query-param -> django ORM query parameter
> +    query_filters = {}
> +
>      def get_queryset(self):
> -        return self.serializer_class.Meta.model.objects.all()
> +        qs = self.serializer_class.Meta.model.objects.all()
> +        filters = {}
> +        for param, val in self.request.query_params.items():
> +            name = self.query_filters.get(param)
> +            if name:
> +                filters[name] = val
> +        return qs.filter(**filters)
> +    def options(self, request, *args, **kwargs):
> +        # add our query filters to make the "options" command a little more
> +        # helpful.
> +        resp = super(PatchworkViewSet, self).options(request, *args, **kwargs)
> +        if self.query_filters:
> +            resp.data['query_filters'] = self.query_filters.keys()
> +        return resp
>  
>  
>  def create_model_serializer(model_class, read_only=None):
> @@ -76,11 +93,30 @@ def create_model_serializer(model_class, read_only=None):
>  
>  
>  class PatchViewSet(PatchworkViewSet):
> +    """Listings support the following query filters:
> +        * project=<project-name>
> +        * since=<YYYY-MM-DDTHH:MM:SS.mm>
> +        * until=<YYYY-MM-DDTHH:MM:SS.mm>
> +        * state=<state-name>
> +        * submitter=<name>
> +        * delegate=<name>
> +
> +       eg: GET /api/1.0/patches/?project=p&since=2016-01-01&submitter=User+Name
> +    """
>      permission_classes = (PatchworkPermission,)
>      serializer_class = create_model_serializer(
>          Patch, ('project', 'name', 'date', 'submitter', 'diff', 'content',
>                  'hash', 'msgid'))
>  
> +    query_filters = {
> +        'project': 'project__name',
> +        'submitter': 'submitter__name',
> +        'delegate': 'delegate__name',
> +        'state': 'state__name',
> +        'since': 'date__gt',
> +        'until': 'date__lt',
> +    }
> +

This seems to be a reinvention of parts of 'filters.py'. Could that
code be integrated here in any way?

>  
>  class PeopleViewSet(PatchworkViewSet):
>      permission_classes = (AuthenticatedReadOnly,)
> -- 
> 2.7.4
> 
> _______________________________________________
> Patchwork mailing list
> Patchwork at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/patchwork


More information about the Patchwork mailing list