Improve REST API with filters

This commit is contained in:
Yohann D'ANELLO 2020-03-11 11:15:03 +01:00
parent bc97eb1eb4
commit 417cd5da04
15 changed files with 78 additions and 36 deletions

View File

@ -1,7 +1,8 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from rest_framework.filters import SearchFilter
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
from ..models import ActivityType, Activity, Guest
@ -15,6 +16,8 @@ class ActivityTypeViewSet(viewsets.ModelViewSet):
"""
queryset = ActivityType.objects.all()
serializer_class = ActivityTypeSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'can_invite', ]
class ActivityViewSet(viewsets.ModelViewSet):
@ -25,6 +28,8 @@ class ActivityViewSet(viewsets.ModelViewSet):
"""
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'description', 'activity_type', ]
class GuestViewSet(viewsets.ModelViewSet):
@ -35,3 +40,5 @@ class GuestViewSet(viewsets.ModelViewSet):
"""
queryset = Guest.objects.all()
serializer_class = GuestSerializer
filter_backends = [SearchFilter]
search_fields = ['$name', ]

View File

@ -3,7 +3,9 @@
from django.conf.urls import url, include
from django.contrib.auth.models import User
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import routers, serializers, viewsets
from rest_framework.filters import SearchFilter
from activity.api.urls import register_activity_urls
from member.api.urls import register_members_urls
from note.api.urls import register_note_urls
@ -33,6 +35,9 @@ class UserViewSet(viewsets.ModelViewSet):
"""
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ]
search_fields = ['$username', '$first_name', '$last_name', ]
# Routers provide an easy way of automatically determining the URL conf.

View File

@ -1,6 +1,7 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .serializers import ChangelogSerializer
@ -15,3 +16,5 @@ class ChangelogViewSet(viewsets.ModelViewSet):
"""
queryset = Changelog.objects.all()
serializer_class = ChangelogSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['model', 'action', "instance_pk", 'user', 'ip',]

View File

@ -81,7 +81,7 @@ def save_object(sender, instance, **kwargs):
model = instance.__class__
fields = '__all__'
previous_json = JSONRenderer().render(CustomSerializer(previous).data).decode("UTF-8")
previous_json = JSONRenderer().render(CustomSerializer(previous).data).decode("UTF-8") if previous else None
instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
if previous_json == instance_json:

View File

@ -1,8 +0,0 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
app_name = 'logs'
# TODO User interface
urlpatterns = [
]

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from rest_framework import viewsets
from rest_framework.filters import SearchFilter
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
from ..models import Profile, Club, Role, Membership
@ -25,6 +26,8 @@ class ClubViewSet(viewsets.ModelViewSet):
"""
queryset = Club.objects.all()
serializer_class = ClubSerializer
filter_backends = [SearchFilter]
search_fields = ['$name', ]
class RoleViewSet(viewsets.ModelViewSet):
@ -35,6 +38,8 @@ class RoleViewSet(viewsets.ModelViewSet):
"""
queryset = Role.objects.all()
serializer_class = RoleSerializer
filter_backends = [SearchFilter]
search_fields = ['$name', ]
class MembershipViewSet(viewsets.ModelViewSet):

View File

@ -5,7 +5,8 @@ from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
TemplateTransaction
class NoteSerializer(serializers.ModelSerializer):
@ -111,6 +112,17 @@ class TransactionSerializer(serializers.ModelSerializer):
fields = '__all__'
class TemplateTransactionSerializer(serializers.ModelSerializer):
"""
REST API Serializer for Transactions.
The djangorestframework plugin will analyse the model `TemplateTransaction` and parse all fields in the API.
"""
class Meta:
model = TemplateTransaction
fields = '__all__'
class MembershipTransactionSerializer(serializers.ModelSerializer):
"""
REST API Serializer for Membership transactions.
@ -120,3 +132,11 @@ class MembershipTransactionSerializer(serializers.ModelSerializer):
class Meta:
model = MembershipTransaction
fields = '__all__'
class TransactionPolymorphicSerializer(PolymorphicSerializer):
model_serializer_mapping = {
Transaction: TransactionSerializer,
TemplateTransaction: TemplateTransactionSerializer,
MembershipTransaction: MembershipTransactionSerializer,
}

View File

@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from .views import NotePolymorphicViewSet, AliasViewSet, \
TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet, MembershipTransactionViewSet
TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet
def register_note_urls(router, path):
@ -15,4 +15,3 @@ def register_note_urls(router, path):
router.register(path + '/transaction/category', TemplateCategoryViewSet)
router.register(path + '/transaction/transaction', TransactionViewSet)
router.register(path + '/transaction/template', TransactionTemplateViewSet)
router.register(path + '/transaction/membership', MembershipTransactionViewSet)

View File

@ -2,13 +2,15 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.db.models import Q
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from rest_framework.filters import SearchFilter
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
NoteUserSerializer, AliasSerializer, \
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory
from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
class NoteViewSet(viewsets.ModelViewSet):
@ -139,6 +141,8 @@ class TemplateCategoryViewSet(viewsets.ModelViewSet):
"""
queryset = TemplateCategory.objects.all()
serializer_class = TemplateCategorySerializer
filter_backends = [SearchFilter]
search_fields = ['$name', ]
class TransactionTemplateViewSet(viewsets.ModelViewSet):
@ -149,6 +153,8 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
"""
queryset = TransactionTemplate.objects.all()
serializer_class = TransactionTemplateSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'amount', 'display', 'category', ]
class TransactionViewSet(viewsets.ModelViewSet):
@ -158,14 +164,6 @@ class TransactionViewSet(viewsets.ModelViewSet):
then render it on /api/note/transaction/transaction/
"""
queryset = Transaction.objects.all()
serializer_class = TransactionSerializer
class MembershipTransactionViewSet(viewsets.ModelViewSet):
"""
REST API View set.
The djangorestframework plugin will get all `MembershipTransaction` objects, serialize it to JSON with the given serializer,
then render it on /api/note/transaction/membership/
"""
queryset = MembershipTransaction.objects.all()
serializer_class = MembershipTransactionSerializer
serializer_class = TransactionPolymorphicSerializer
filter_backends = [SearchFilter]
search_fields = ['$reason', ]

View File

@ -3,7 +3,7 @@
"model": "note.note",
"pk": 1,
"fields": {
"polymorphic_ctype": 37,
"polymorphic_ctype": 40,
"balance": 0,
"is_active": true,
"display_image": "",
@ -14,7 +14,7 @@
"model": "note.note",
"pk": 2,
"fields": {
"polymorphic_ctype": 37,
"polymorphic_ctype": 40,
"balance": 0,
"is_active": true,
"display_image": "",
@ -25,7 +25,7 @@
"model": "note.note",
"pk": 3,
"fields": {
"polymorphic_ctype": 37,
"polymorphic_ctype": 40,
"balance": 0,
"is_active": true,
"display_image": "",
@ -36,7 +36,7 @@
"model": "note.note",
"pk": 4,
"fields": {
"polymorphic_ctype": 37,
"polymorphic_ctype": 40,
"balance": 0,
"is_active": true,
"display_image": "",
@ -47,7 +47,7 @@
"model": "note.note",
"pk": 5,
"fields": {
"polymorphic_ctype": 36,
"polymorphic_ctype": 39,
"balance": 0,
"is_active": true,
"display_image": "",
@ -58,7 +58,7 @@
"model": "note.note",
"pk": 6,
"fields": {
"polymorphic_ctype": 36,
"polymorphic_ctype": 39,
"balance": 0,
"is_active": true,
"display_image": "",

View File

@ -137,12 +137,14 @@ REST_FRAMEWORK = {
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
# TODO Maybe replace it with our custom permissions system
'rest_framework.permissions.DjangoModelPermissions'
'rest_framework.permissions.DjangoModelPermissions',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
]
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
}
# Internationalization

View File

@ -20,7 +20,6 @@ urlpatterns = [
path('accounts/', include('django.contrib.auth.urls')),
path('admin/doc/', include('django.contrib.admindocs.urls')),
path('admin/', admin.site.urls),
path('logs/', include('logs.urls')),
path('api/', include('api.urls')),
]

View File

@ -0,0 +1,5 @@
{% load crispy_forms_tags %}
{% load i18n %}
<h2>{% trans "Field filters" %}</h2>
{% crispy filter.form %}

View File

@ -0,0 +1,6 @@
{% load i18n %}
<h2>{% trans "Field filters" %}</h2>
<form class="form" action="" method="get">
{{ filter.form.as_p }}
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
</form>

View File

@ -0,0 +1 @@
{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %}