mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Add regex IPN filter for Part API
This commit is contained in:
		| @@ -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?'), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user