From patchwork Fri Nov 18 01:18:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Finucane X-Patchwork-Id: 696376 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tKgBW22zTz9s2G for ; Fri, 18 Nov 2016 12:19:15 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="iFUqbQkf"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3tKgBW0tRSzDt3D for ; Fri, 18 Nov 2016 12:19:15 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="iFUqbQkf"; dkim-atps=neutral X-Original-To: patchwork@lists.ozlabs.org Delivered-To: patchwork@lists.ozlabs.org Received: from burlywood.maple.relay.mailchannels.net (burlywood.maple.relay.mailchannels.net [23.83.214.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3tKgBR2m0jzDt09 for ; Fri, 18 Nov 2016 12:19:11 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="key not found in DNS" (0-bit key; unprotected) header.d=that.guru header.i=@that.guru header.b="iFUqbQkf"; dkim-atps=neutral X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id B35E1E16EE for ; Fri, 18 Nov 2016 01:19:06 +0000 (UTC) Received: from one.mxroute.com (ip-10-220-3-24.us-west-2.compute.internal [10.220.3.24]) by relay.mailchannels.net (Postfix) with ESMTPA id 531FDE0A8A for ; Fri, 18 Nov 2016 01:19:05 +0000 (UTC) X-Sender-Id: mxroute|x-authuser|stephen@that.guru Received: from one.mxroute.com ([UNAVAILABLE]. [10.16.27.41]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384) by 0.0.0.0:2500 (trex/5.7.8); Fri, 18 Nov 2016 01:19:06 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: mxroute|x-authuser|stephen@that.guru X-MailChannels-Auth-Id: mxroute X-MC-Loop-Signature: 1479431945554:3539891241 X-MC-Ingress-Time: 1479431945554 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=that.guru; s=default; h=Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:MIME-Version :Content-Type:Content-Transfer-Encoding:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=SNlxLFJ4sZpssSOZYsNXO88jGtHgSK2vC5Kg2ybUIDI=; b=iFUqbQkflwPZxRFhZp+0Fj7V5T pKK2HlGPuMheM+L/+5npSP1WZ1hE56uWv3dVw2tOXDFcGXg5yD7FcLQicgRIB6IwC0fooT3GzNVcY xFRijjIUG2wQ8JYkEu0kUxiz5IJkJ8sRRH3P9l3MAUJVieGLe2oXedpSK+/Ov18Cg6cPBqB8uNXxJ howraBy1pmOeF5pfJhnn9BaxMXvvMavw7NfvzLC0AXM1dqKhKvjUs3HStrg/z7AMZynWzlsFarCJ/ QNb9Su4pfSfX0g+8+jXVkQT5h3sCFEJ+62bpTmX82YNnmqtbNsvhA8iUuSMO8Ka5yHgYG/wyPDD/K 2gV1Wp3Q==; From: Stephen Finucane To: patchwork@lists.ozlabs.org Subject: [PATCH 1/3] REST: Integrate django-filter support Date: Fri, 18 Nov 2016 01:18:43 +0000 Message-Id: <1479431925-24865-1-git-send-email-stephen@that.guru> X-Mailer: git-send-email 2.7.4 X-OutGoing-Spam-Status: No, score=-10.0 X-AuthUser: stephen@that.guru X-BeenThere: patchwork@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Patchwork development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: patchwork-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Patchwork" This mostly works out of the box, thanks to Django REST Framework. Mostly unique fields, like name or email, are excluded as these will be handled separately. Signed-off-by: Stephen Finucane --- patchwork/api/check.py | 2 ++ patchwork/api/cover.py | 2 ++ patchwork/api/filters.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++ patchwork/api/patch.py | 2 ++ patchwork/api/series.py | 3 ++- patchwork/settings/base.py | 6 ++++- requirements-test.txt | 2 +- 7 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 patchwork/api/filters.py diff --git a/patchwork/api/check.py b/patchwork/api/check.py index 43463fe..9a5c22a 100644 --- a/patchwork/api/check.py +++ b/patchwork/api/check.py @@ -26,6 +26,7 @@ from rest_framework.serializers import HiddenField from rest_framework.serializers import HyperlinkedModelSerializer from rest_framework.serializers import HyperlinkedIdentityField +from patchwork.api.filters import CheckFilter from patchwork.api import MultipleFieldLookupMixin from patchwork.models import Check from patchwork.models import Patch @@ -89,6 +90,7 @@ class CheckMixin(object): queryset = Check.objects.prefetch_related('patch', 'user') serializer_class = CheckSerializer + filter_class = CheckFilter class CheckListCreate(CheckMixin, ListCreateAPIView): diff --git a/patchwork/api/cover.py b/patchwork/api/cover.py index 2674be3..153176c 100644 --- a/patchwork/api/cover.py +++ b/patchwork/api/cover.py @@ -25,6 +25,7 @@ from rest_framework.serializers import HyperlinkedModelSerializer from rest_framework.serializers import HyperlinkedRelatedField from rest_framework.serializers import SerializerMethodField +from patchwork.api.filters import CoverLetterFilter from patchwork.models import CoverLetter @@ -67,6 +68,7 @@ class CoverLetterList(ListAPIView): queryset = CoverLetter.objects.all().prefetch_related( 'series').select_related('submitter').defer('content', 'headers') serializer_class = CoverLetterListSerializer + filter_class = CoverLetterFilter class CoverLetterDetail(RetrieveAPIView): diff --git a/patchwork/api/filters.py b/patchwork/api/filters.py new file mode 100644 index 0000000..a07f464 --- /dev/null +++ b/patchwork/api/filters.py @@ -0,0 +1,61 @@ +# Patchwork - automated patch tracking system +# Copyright (C) 2016 Stephen Finucane +# +# This file is part of the Patchwork package. +# +# Patchwork is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Patchwork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Patchwork; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from django_filters import FilterSet +from django_filters import IsoDateTimeFilter + +from patchwork.models import Check +from patchwork.models import CoverLetter +from patchwork.models import Patch +from patchwork.models import Series + + +class TimestampMixin(object): + + # TODO(stephenfin): These should filter on a 'updated_at' field instead + before = IsoDateTimeFilter(name='date', lookup_expr='lt') + since = IsoDateTimeFilter(name='date', lookup_expr='gte') + + +class SeriesFilter(TimestampMixin, FilterSet): + + class Meta: + model = Series + fields = ['submitter'] + + +class CoverLetterFilter(TimestampMixin, FilterSet): + + class Meta: + model = CoverLetter + fields = ['series', 'submitter'] + + +class PatchFilter(FilterSet): + + class Meta: + model = Patch + fields = ['series', 'submitter', 'delegate', 'state', 'archived'] + + +class CheckFilter(TimestampMixin, FilterSet): + + class Meta: + model = Check + fields = ['user', 'state', 'context'] diff --git a/patchwork/api/patch.py b/patchwork/api/patch.py index 8d308e8..c1c5030 100644 --- a/patchwork/api/patch.py +++ b/patchwork/api/patch.py @@ -27,6 +27,7 @@ from rest_framework.serializers import ChoiceField from rest_framework.serializers import HyperlinkedModelSerializer from rest_framework.serializers import SerializerMethodField +from patchwork.api.filters import PatchFilter from patchwork.api import PatchworkPermission from patchwork.api import STATE_CHOICES from patchwork.models import Patch @@ -118,6 +119,7 @@ class PatchList(ListAPIView): 'content', 'diff', 'headers') permission_classes = (PatchworkPermission,) serializer_class = PatchListSerializer + filter_class = PatchFilter class PatchDetail(RetrieveUpdateAPIView): diff --git a/patchwork/api/series.py b/patchwork/api/series.py index fead4ca..a7f1344 100644 --- a/patchwork/api/series.py +++ b/patchwork/api/series.py @@ -21,6 +21,7 @@ from rest_framework.generics import ListAPIView from rest_framework.generics import RetrieveAPIView from rest_framework.serializers import HyperlinkedModelSerializer +from patchwork.api.filters import SeriesFilter from patchwork.api import PatchworkPermission from patchwork.models import Series @@ -51,7 +52,7 @@ class SeriesMixin(object): class SeriesList(SeriesMixin, ListAPIView): """List series.""" - pass + filter_class = SeriesFilter class SeriesDetail(SeriesMixin, RetrieveAPIView): diff --git a/patchwork/settings/base.py b/patchwork/settings/base.py index 35ac96a..dd2d3d0 100644 --- a/patchwork/settings/base.py +++ b/patchwork/settings/base.py @@ -133,7 +133,8 @@ try: import rest_framework # NOQA INSTALLED_APPS += [ - 'rest_framework' + 'rest_framework', + 'django_filters', ] except ImportError: pass @@ -144,6 +145,9 @@ REST_FRAMEWORK = { 'rest_framework.versioning.NamespaceVersioning' ), 'DEFAULT_PAGINATION_CLASS': 'patchwork.api.LinkHeaderPagination', + 'DEFAULT_FILTER_BACKENDS': ( + 'django_filters.rest_framework.DjangoFilterBackend', + ) } # diff --git a/requirements-test.txt b/requirements-test.txt index 7cb5ae9..c664976 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -3,4 +3,4 @@ django-debug-toolbar==1.5 python-dateutil>2.0,<3.0 selenium>2.0,<3.0 djangorestframework>=3.4,<3.5 -drf-nested-routers>=0.11.1,<0.12 +django-filter>=0.15,<0.16