mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add regex IPN filter for Part API
This commit is contained in:
parent
800cb9606a
commit
376428b80b
@ -5,9 +5,10 @@ Provides a JSON API for the Part app
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
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.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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import status
|
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 filters, serializers
|
||||||
from rest_framework import generics
|
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.money import Money
|
||||||
from djmoney.contrib.exchange.models import convert_money
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
from djmoney.contrib.exchange.exceptions import MissingRate
|
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 Part, PartCategory, BomItem
|
||||||
from .models import PartParameter, PartParameterTemplate
|
from .models import PartParameter, PartParameterTemplate
|
||||||
@ -405,6 +407,74 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return response
|
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):
|
class PartList(generics.ListCreateAPIView):
|
||||||
""" API endpoint for accessing a list of Part objects
|
""" API endpoint for accessing a list of Part objects
|
||||||
|
|
||||||
@ -427,8 +497,8 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
serializer_class = part_serializers.PartSerializer
|
serializer_class = part_serializers.PartSerializer
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
|
filterset_class = PartFilter
|
||||||
|
|
||||||
starred_parts = None
|
starred_parts = None
|
||||||
|
|
||||||
@ -541,6 +611,10 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
params = self.request.query_params
|
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)
|
queryset = super().filter_queryset(queryset)
|
||||||
|
|
||||||
# Filter by "uses" query - Limit to parts which use the provided part
|
# Filter by "uses" query - Limit to parts which use the provided part
|
||||||
@ -578,6 +652,17 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
else:
|
else:
|
||||||
queryset = queryset.filter(IPN='')
|
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)
|
# Filter by whether the BOM has been validated (or not)
|
||||||
bom_valid = params.get('bom_valid', None)
|
bom_valid = params.get('bom_valid', None)
|
||||||
|
|
||||||
@ -643,36 +728,6 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
except (ValueError, PartCategory.DoesNotExist):
|
except (ValueError, PartCategory.DoesNotExist):
|
||||||
pass
|
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
|
# Filer by 'depleted_stock' status -> has no stock and stock items
|
||||||
depleted_stock = params.get('depleted_stock', None)
|
depleted_stock = params.get('depleted_stock', None)
|
||||||
|
|
||||||
@ -722,14 +777,7 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
filter_fields = [
|
filter_fields = [
|
||||||
'is_template',
|
|
||||||
'variant_of',
|
'variant_of',
|
||||||
'assembly',
|
|
||||||
'component',
|
|
||||||
'trackable',
|
|
||||||
'purchaseable',
|
|
||||||
'salable',
|
|
||||||
'active',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
ordering_fields = [
|
ordering_fields = [
|
||||||
|
19
InvenTree/part/migrations/0070_alter_part_variant_of.py
Normal file
19
InvenTree/part/migrations/0070_alter_part_variant_of.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
@ -692,7 +692,6 @@ class Part(MPTTModel):
|
|||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'is_template': True,
|
'is_template': True,
|
||||||
'active': True,
|
|
||||||
},
|
},
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
help_text=_('Is this part a variant of another part?'),
|
help_text=_('Is this part a variant of another part?'),
|
||||||
|
Loading…
Reference in New Issue
Block a user