Add regex IPN filter for Part API

This commit is contained in:
Oliver 2021-07-08 17:02:45 +10:00
parent 800cb9606a
commit 376428b80b
3 changed files with 109 additions and 43 deletions

View File

@ -5,9 +5,10 @@ Provides a JSON API for the Part app
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django_filters.rest_framework import DjangoFilterBackend
from django.conf.urls import url, include
from django.urls import reverse
from django.http import JsonResponse
from django.db.models import Q, F, Count, Min, Max, Avg
from django.db.models import Q, F, Count, Min, Max, Avg, query
from django.utils.translation import ugettext_lazy as _
from rest_framework import status
@ -15,12 +16,13 @@ from rest_framework.response import Response
from rest_framework import filters, serializers
from rest_framework import generics
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as rest_filters
from djmoney.money import Money
from djmoney.contrib.exchange.models import convert_money
from djmoney.contrib.exchange.exceptions import MissingRate
from django.conf.urls import url, include
from django.urls import reverse
from .models import Part, PartCategory, BomItem
from .models import PartParameter, PartParameterTemplate
@ -405,6 +407,74 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
return response
class PartFilter(rest_filters.FilterSet):
"""
Custom filters for the PartList endpoint.
Uses the django_filters extension framework
"""
# Exact match for IPN
ipn = rest_filters.CharFilter(
label='Filter by exact IPN (internal part number)',
field_name='IPN',
lookup_expr="iexact"
)
# Regex match for IPN
ipn_regex = rest_filters.CharFilter(
field_name='IPN', lookup_expr='iregex'
)
# low_stock filter
low_stock = rest_filters.BooleanFilter(method='filter_low_stock')
def filter_low_stock(self, queryset, name, value):
"""
Filter by "low stock" status
"""
value = str2bool(value)
if value:
# Ignore any parts which do not have a specified 'minimum_stock' level
queryset = queryset.exclude(minimum_stock=0)
# Filter items which have an 'in_stock' level lower than 'minimum_stock'
queryset = queryset.filter(Q(in_stock__lt=F('minimum_stock')))
else:
# Filter items which have an 'in_stock' level higher than 'minimum_stock'
queryset = queryset.filter(Q(in_stock__gte=F('minimum_stock')))
return queryset
# has_stock filter
has_stock = rest_filters.BooleanFilter(method='filter_has_stock')
def filter_has_stock(self, queryset, name, value):
value = str2bool(value)
if value:
queryset = queryset.filter(Q(in_stock__gt=0))
else:
queryset = queryset.filter(Q(in_stock__lte=0))
return queryset
is_template = rest_filters.CharFilter()
assembly = rest_filters.BooleanFilter()
component = rest_filters.BooleanFilter()
trackable = rest_filters.BooleanFilter()
purchaseable = rest_filters.BooleanFilter()
salable = rest_filters.BooleanFilter()
active = rest_filters.BooleanFilter()
class PartList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of Part objects
@ -427,8 +497,8 @@ class PartList(generics.ListCreateAPIView):
"""
serializer_class = part_serializers.PartSerializer
queryset = Part.objects.all()
filterset_class = PartFilter
starred_parts = None
@ -541,6 +611,10 @@ class PartList(generics.ListCreateAPIView):
params = self.request.query_params
# Annotate calculated data to the queryset
# (This will be used for further filtering)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
queryset = super().filter_queryset(queryset)
# Filter by "uses" query - Limit to parts which use the provided part
@ -578,6 +652,17 @@ class PartList(generics.ListCreateAPIView):
else:
queryset = queryset.filter(IPN='')
# Filter by IPN
"""
ipn = params.get('ipn', None)
if ipn is not None:
queryset = queryset.filter(IPN=ipn)
"""
# Filter by IPN (regex support)
# Filter by whether the BOM has been validated (or not)
bom_valid = params.get('bom_valid', None)
@ -643,36 +728,6 @@ class PartList(generics.ListCreateAPIView):
except (ValueError, PartCategory.DoesNotExist):
pass
# Annotate calculated data to the queryset
# (This will be used for further filtering)
queryset = part_serializers.PartSerializer.annotate_queryset(queryset)
# Filter by whether the part has stock
has_stock = params.get("has_stock", None)
if has_stock is not None:
has_stock = str2bool(has_stock)
if has_stock:
queryset = queryset.filter(Q(in_stock__gt=0))
else:
queryset = queryset.filter(Q(in_stock__lte=0))
# If we are filtering by 'low_stock' status
low_stock = params.get('low_stock', None)
if low_stock is not None:
low_stock = str2bool(low_stock)
if low_stock:
# Ignore any parts which do not have a specified 'minimum_stock' level
queryset = queryset.exclude(minimum_stock=0)
# Filter items which have an 'in_stock' level lower than 'minimum_stock'
queryset = queryset.filter(Q(in_stock__lt=F('minimum_stock')))
else:
# Filter items which have an 'in_stock' level higher than 'minimum_stock'
queryset = queryset.filter(Q(in_stock__gte=F('minimum_stock')))
# Filer by 'depleted_stock' status -> has no stock and stock items
depleted_stock = params.get('depleted_stock', None)
@ -722,14 +777,7 @@ class PartList(generics.ListCreateAPIView):
]
filter_fields = [
'is_template',
'variant_of',
'assembly',
'component',
'trackable',
'purchaseable',
'salable',
'active',
]
ordering_fields = [

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.4 on 2021-07-08 07:02
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0069_auto_20210701_0509'),
]
operations = [
migrations.AlterField(
model_name='part',
name='variant_of',
field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.part', verbose_name='Variant Of'),
),
]

View File

@ -692,7 +692,6 @@ class Part(MPTTModel):
null=True, blank=True,
limit_choices_to={
'is_template': True,
'active': True,
},
on_delete=models.SET_NULL,
help_text=_('Is this part a variant of another part?'),