From 8222d77a6bfb217aa01d222a8b7b509f9d8bd84d Mon Sep 17 00:00:00 2001 From: Gresh <> Date: Wed, 2 Aug 2023 19:51:26 -0400 Subject: [PATCH] Refactor view filtersets: Extract CommonFilterSet base class for all filtersets to extend; Add document__slug__not_in field filter to CommonFilterSet; Move filtersets to seperate filters module; --- api/filters.py | 367 +++++++++++++++++++++++++++++++++++++++++++++++++ api/views.py | 235 +++---------------------------- 2 files changed, 385 insertions(+), 217 deletions(-) create mode 100644 api/filters.py diff --git a/api/filters.py b/api/filters.py new file mode 100644 index 00000000..8f4c7cce --- /dev/null +++ b/api/filters.py @@ -0,0 +1,367 @@ +import django_filters +from api import models + + +class CommonFilterSet(django_filters.FilterSet): + document__slug__not_in = django_filters.BaseInFilter( + field_name="document__slug", exclude=True + ) + + +class SpellFilter(CommonFilterSet): + level_int = django_filters.NumberFilter(field_name="spell_level") + concentration = django_filters.CharFilter(field_name="concentration") + components = django_filters.CharFilter(field_name="components") + spell_lists_not = django_filters.CharFilter(field_name="spell_lists", exclude=True) + + class Meta: + model = models.Spell + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "spell_level": ["exact", "range", "gt", "gte", "lt", "lte"], + "target_range_sort": ["exact", "range", "gt", "gte", "lt", "lte"], + "school": [ + "iexact", + "exact", + "in", + ], + "duration": [ + "iexact", + "exact", + "in", + ], + "requires_concentration": ["exact"], + "requires_verbal_components": ["exact"], + "requires_somatic_components": ["exact"], + "requires_material_components": ["exact"], + "casting_time": [ + "iexact", + "exact", + "in", + ], + "dnd_class": ["iexact", "exact", "in", "icontains"], + "spell_lists": ["exact"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class SpellListFilter(CommonFilterSet): + class Meta: + model = models.SpellList + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class MonsterFilter(CommonFilterSet): + class Meta: + model = models.Monster + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "cr": ["exact", "range", "gt", "gte", "lt", "lte"], + "armor_class": ["exact", "range", "gt", "gte", "lt", "lte"], + "type": ["iexact", "exact", "in", "icontains"], + "name": ["iexact", "exact"], + "page_no": ["exact", "range", "gt", "gte", "lt", "lte"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class BackgroundFilter(CommonFilterSet): + class Meta: + model = models.Background + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "skill_proficiencies": ["iexact", "exact", "icontains"], + "tool_proficiencies": ["iexact", "exact", "icontains"], + "languages": ["iexact", "exact", "icontains"], + "feature": ["iexact", "exact", "icontains"], + "feature_desc": ["iexact", "exact", "icontains"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class PlaneFilter(CommonFilterSet): + class Meta: + model = models.Plane + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class SectionFilter(CommonFilterSet): + class Meta: + model = models.Section + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "parent": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class FeatFilter(CommonFilterSet): + class Meta: + model = models.Feat + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": ["iexact", "exact", "in"], + } + + +class ConditionFilter(CommonFilterSet): + class Meta: + model = models.Condition + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class RaceFilter(CommonFilterSet): + class Meta: + model = models.Race + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": ["iexact", "exact", "in"], + "asi_desc": ["iexact", "exact", "icontains"], + "age": ["iexact", "exact", "icontains"], + "alignment": ["iexact", "exact", "icontains"], + "size": ["iexact", "exact", "icontains"], + "speed_desc": ["iexact", "exact", "icontains"], + "languages": ["iexact", "exact", "icontains"], + "vision": ["iexact", "exact", "icontains"], + "traits": ["iexact", "exact", "icontains"], + } + + +class SubraceFilter(CommonFilterSet): + # Unused, but could be implemented later. + class Meta: + model = models.Subrace + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class CharClassFilter(CommonFilterSet): + class Meta: + model = models.CharClass + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "hit_dice": ["iexact", "exact", "in"], + "hp_at_1st_level": ["iexact", "exact", "icontains"], + "hp_at_higher_levels": ["iexact", "exact", "icontains"], + "prof_armor": ["iexact", "exact", "icontains"], + "prof_weapons": ["iexact", "exact", "icontains"], + "prof_tools": ["iexact", "exact", "icontains"], + "prof_skills": ["iexact", "exact", "icontains"], + "equipment": ["iexact", "exact", "icontains"], + "spellcasting_ability": ["iexact", "exact", "icontains"], + "subtypes_name": ["iexact", "exact", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class ArchetypeFilter(CommonFilterSet): + # Unused but could be implemented later. + class Meta: + model = models.Archetype + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class MagicItemFilter(CommonFilterSet): + class Meta: + model = models.MagicItem + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "type": ["iexact", "exact", "icontains"], + "rarity": ["iexact", "exact", "icontains"], + "requires_attunement": ["iexact", "exact"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class WeaponFilter(CommonFilterSet): + class Meta: + model = models.Weapon + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "cost": ["iexact", "exact", "icontains"], + "damage_dice": ["iexact", "exact", "icontains"], + "damage_type": ["iexact", "exact", "icontains"], + "weight": ["iexact", "exact", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } + + +class ArmorFilter(CommonFilterSet): + class Meta: + model = models.Armor + fields = { + "slug": [ + "in", + "iexact", + "exact", + "in", + ], + "name": ["iexact", "exact"], + "desc": ["iexact", "exact", "in", "icontains"], + "cost": ["iexact", "exact", "icontains"], + "weight": ["iexact", "exact", "icontains"], + "document__slug": [ + "iexact", + "exact", + "in", + ], + } diff --git a/api/views.py b/api/views.py index 584cb09e..e565609a 100644 --- a/api/views.py +++ b/api/views.py @@ -1,11 +1,11 @@ from django.contrib.auth.models import User, Group -import django_filters from drf_haystack.viewsets import HaystackViewSet from rest_framework import viewsets from api import models from api import serializers +from api import filters from api.schema_generator import CustomSchema @@ -37,6 +37,7 @@ class ManifestViewSet(viewsets.ReadOnlyModelViewSet): queryset = models.Manifest.objects.all() serializer_class = serializers.ManifestSerializer + class SearchView(HaystackViewSet): """ list: API endpoint for returning a list of search results from the Open5e database. @@ -62,6 +63,7 @@ def get_queryset(self, *args, **kwargs): return queryset + class UserViewSet(viewsets.ReadOnlyModelViewSet): """ API endpoint that allows users to be viewed or edited. @@ -69,6 +71,7 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet): queryset = User.objects.all().order_by('-date_joined') serializer_class = serializers.UserSerializer + class GroupViewSet(viewsets.ReadOnlyModelViewSet): """ API endpoint that allows groups to be viewed or edited. @@ -76,6 +79,7 @@ class GroupViewSet(viewsets.ReadOnlyModelViewSet): queryset = Group.objects.all() serializer_class = serializers.GroupSerializer + class DocumentViewSet(viewsets.ReadOnlyModelViewSet): """ list: API endpoint for returning a list of documents. @@ -102,30 +106,6 @@ class DocumentViewSet(viewsets.ReadOnlyModelViewSet): 'license', ) -class SpellFilter(django_filters.FilterSet): - level_int = django_filters.NumberFilter(field_name='spell_level') - concentration = django_filters.CharFilter(field_name='concentration') - components = django_filters.CharFilter(field_name='components') - spell_lists_not = django_filters.CharFilter(field_name='spell_lists', exclude=True) - - class Meta: - model = models.Spell - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'spell_level': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'], - 'target_range_sort': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'], - 'school': ['iexact', 'exact', 'in', ], - 'duration': ['iexact', 'exact', 'in', ], - 'requires_concentration': ['exact'], - 'requires_verbal_components': ['exact'], - 'requires_somatic_components': ['exact'], - 'requires_material_components': ['exact'], - 'casting_time': ['iexact', 'exact', 'in', ], - 'dnd_class': ['iexact', 'exact', 'in', 'icontains'], - 'spell_lists' : ['exact'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class SpellViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -140,7 +120,7 @@ class SpellViewSet(viewsets.ReadOnlyModelViewSet): tags=['Spells'] ) queryset = models.Spell.objects.all() - filterset_class=SpellFilter + filterset_class=filters.SpellFilter serializer_class = serializers.SpellSerializer search_fields = ['dnd_class', 'name'] ordering_fields = '__all__' @@ -159,17 +139,6 @@ class SpellViewSet(viewsets.ReadOnlyModelViewSet): 'document__slug', ) -class SpellListFilter(django_filters.FilterSet): - - class Meta: - model = models.SpellList - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } - class SpellListViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -185,23 +154,8 @@ class SpellListViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.SpellList.objects.all() serializer_class = serializers.SpellListSerializer - filterset_class = SpellListFilter - -class MonsterFilter(django_filters.FilterSet): - - class Meta: - model = models.Monster - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'cr': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'], - 'armor_class': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'], - 'type': ['iexact', 'exact', 'in', 'icontains'], - 'name': ['iexact', 'exact'], - 'page_no': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'], - 'document__slug': ['iexact', 'exact', 'in', ] - } + filterset_class = filters.SpellListFilter + class MonsterViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -216,26 +170,11 @@ class MonsterViewSet(viewsets.ReadOnlyModelViewSet): tags=['Monsters'] ) queryset = models.Monster.objects.all() - filterset_class = MonsterFilter + filterset_class = filters.MonsterFilter serializer_class = serializers.MonsterSerializer search_fields = ['name'] -class BackgroundFilter(django_filters.FilterSet): - - class Meta: - model = models.Background - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'skill_proficiencies': ['iexact', 'exact', 'icontains'], - 'tool_proficiencies': ['iexact', 'exact', 'icontains'], - 'languages': ['iexact', 'exact', 'icontains'], - 'feature': ['iexact', 'exact', 'icontains'], - 'feature_desc': ['iexact', 'exact', 'icontains'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class BackgroundViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -253,19 +192,9 @@ class BackgroundViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.BackgroundSerializer ordering_fields = '__all__' ordering = ['name'] - filterset_class = BackgroundFilter + filterset_class = filters.BackgroundFilter search_fields = ['name'] -class PlaneFilter(django_filters.FilterSet): - - class Meta: - model = models.Plane - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class PlaneViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -281,19 +210,8 @@ class PlaneViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.Plane.objects.all() serializer_class = serializers.PlaneSerializer - filterset_class=PlaneFilter + filterset_class = filters.PlaneFilter -class SectionFilter(django_filters.FilterSet): - - class Meta: - model = models.Section - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'parent' : ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class SectionViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -311,18 +229,8 @@ class SectionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = serializers.SectionSerializer ordering_fields = '__all__' ordering=['name'] - filterset_class = SectionFilter - -class FeatFilter(django_filters.FilterSet): + filterset_class = filters.SectionFilter - class Meta: - model = models.Feat - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in'] - } class FeatViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -338,18 +246,8 @@ class FeatViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.Feat.objects.all() serializer_class = serializers.FeatSerializer - filterset_class = FeatFilter + filterset_class = filters.FeatFilter -class ConditionFilter(django_filters.FilterSet): - - class Meta: - model = models.Condition - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class ConditionViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -370,24 +268,6 @@ class ConditionViewSet(viewsets.ReadOnlyModelViewSet): 'document__slug', ) -class RaceFilter(django_filters.FilterSet): - - class Meta: - model = models.Race - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in'], - 'asi_desc': ['iexact', 'exact', 'icontains'], - 'age': ['iexact', 'exact', 'icontains'], - 'alignment': ['iexact', 'exact', 'icontains'], - 'size': ['iexact', 'exact', 'icontains'], - 'speed_desc':['iexact', 'exact', 'icontains'], - 'languages': ['iexact', 'exact', 'icontains'], - 'vision': ['iexact', 'exact', 'icontains'], - 'traits': ['iexact', 'exact', 'icontains'] - } class RaceViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -403,18 +283,8 @@ class RaceViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.Race.objects.all() serializer_class = serializers.RaceSerializer - filterset_class = RaceFilter + filterset_class = filters.RaceFilter -class SubraceFilter(django_filters.FilterSet): - # Unused, but could be implemented later. - class Meta: - model = models.Subrace - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class SubraceViewSet(viewsets.ReadOnlyModelViewSet): # Unused, but could be implemented later. @@ -436,26 +306,6 @@ class SubraceViewSet(viewsets.ReadOnlyModelViewSet): 'document__slug', ) -class CharClassFilter(django_filters.FilterSet): - - class Meta: - model = models.CharClass - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'hit_dice': ['iexact', 'exact', 'in'], - 'hp_at_1st_level': ['iexact', 'exact', 'icontains'], - 'hp_at_higher_levels': ['iexact', 'exact', 'icontains'], - 'prof_armor': ['iexact', 'exact', 'icontains'], - 'prof_weapons': ['iexact', 'exact', 'icontains'], - 'prof_tools': ['iexact', 'exact', 'icontains'], - 'prof_skills': ['iexact', 'exact', 'icontains'], - 'equipment': ['iexact', 'exact', 'icontains'], - 'spellcasting_ability': ['iexact', 'exact', 'icontains'], - 'subtypes_name': ['iexact', 'exact', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class CharClassViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -471,18 +321,8 @@ class CharClassViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.CharClass.objects.all() serializer_class = serializers.CharClassSerializer - filterset_class = CharClassFilter + filterset_class = filters.CharClassFilter -class ArchetypeFilter(django_filters.FilterSet): - # Unused but could be implemented later. - class Meta: - model = models.Archetype - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class ArchetypeViewSet(viewsets.ReadOnlyModelViewSet): # Unused but could be implemented later. @@ -504,19 +344,6 @@ class ArchetypeViewSet(viewsets.ReadOnlyModelViewSet): 'document__slug', ) -class MagicItemFilter(django_filters.FilterSet): - - class Meta: - model = models.MagicItem - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'type': ['iexact', 'exact', 'icontains'], - 'rarity': ['iexact', 'exact', 'icontains'], - 'requires_attunement': ['iexact', 'exact'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class MagicItemViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -532,23 +359,9 @@ class MagicItemViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.MagicItem.objects.all() serializer_class = serializers.MagicItemSerializer - filterset_class = MagicItemFilter + filterset_class = filters.MagicItemFilter search_fields = ['name'] -class WeaponFilter(django_filters.FilterSet): - - class Meta: - model = models.Weapon - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'cost': ['iexact', 'exact', 'icontains'], - 'damage_dice': ['iexact', 'exact', 'icontains'], - 'damage_type': ['iexact', 'exact', 'icontains'], - 'weight': ['iexact', 'exact', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class WeaponViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -564,21 +377,9 @@ class WeaponViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.Weapon.objects.all() serializer_class = serializers.WeaponSerializer - filterset_class = WeaponFilter + filterset_class = filters.WeaponFilter search_fields = ['name'] -class ArmorFilter(django_filters.FilterSet): - - class Meta: - model = models.Armor - fields = { - 'slug': ['in', 'iexact', 'exact', 'in', ], - 'name': ['iexact', 'exact'], - 'desc': ['iexact', 'exact', 'in', 'icontains'], - 'cost': ['iexact', 'exact', 'icontains'], - 'weight': ['iexact', 'exact', 'icontains'], - 'document__slug': ['iexact', 'exact', 'in', ] - } class ArmorViewSet(viewsets.ReadOnlyModelViewSet): """ @@ -594,5 +395,5 @@ class ArmorViewSet(viewsets.ReadOnlyModelViewSet): ) queryset = models.Armor.objects.all() serializer_class = serializers.ArmorSerializer - filterset_class = ArmorFilter + filterset_class = filters.ArmorFilter search_fields = ['name']