From 9884fe5c5e733effe39292429ef4f0fa3fb9b8aa Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 14 Jan 2021 08:15:05 +1100 Subject: [PATCH] Improve validators for 'filters' field --- InvenTree/InvenTree/helpers.py | 13 ++++++- .../migrations/0005_auto_20210113_2302.py | 24 ++++++++++++ InvenTree/label/models.py | 37 +++++++++++++++---- 3 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 InvenTree/label/migrations/0005_auto_20210113_2302.py diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 37a8bf82e1..e65e67951e 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -12,7 +12,7 @@ from decimal import Decimal from wsgiref.util import FileWrapper from django.http import StreamingHttpResponse -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError, FieldError from django.utils.translation import ugettext as _ from django.contrib.auth.models import Permission @@ -414,7 +414,7 @@ def extract_serial_numbers(serials, expected_quantity): return numbers -def validateFilterString(value): +def validateFilterString(value, model=None): """ Validate that a provided filter string looks like a list of comma-separated key=value pairs @@ -464,6 +464,15 @@ def validateFilterString(value): results[k] = v + # If a model is provided, verify that the provided filters can be used against it + if model is not None: + try: + query = model.objects.filter(**results) + except FieldError as e: + raise ValidationError( + str(e), + ) + return results diff --git a/InvenTree/label/migrations/0005_auto_20210113_2302.py b/InvenTree/label/migrations/0005_auto_20210113_2302.py new file mode 100644 index 0000000000..ad256412ac --- /dev/null +++ b/InvenTree/label/migrations/0005_auto_20210113_2302.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.7 on 2021-01-13 12:02 + +from django.db import migrations, models +import label.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('label', '0004_auto_20210111_2302'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitemlabel', + name='filters', + field=models.CharField(blank=True, help_text='Query filters (comma-separated list of key=value pairs', max_length=250, validators=[label.models.validate_stock_item_filters], verbose_name='Filters'), + ), + migrations.AlterField( + model_name='stocklocationlabel', + name='filters', + field=models.CharField(blank=True, help_text='Query filters (comma-separated list of key=value pairs', max_length=250, validators=[label.models.validate_stock_location_filters], verbose_name='Filters'), + ), + ] diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index 3e693b1e50..a34aa3831d 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -28,6 +28,20 @@ def rename_label(instance, filename): return os.path.join('label', 'template', instance.SUBDIR, filename) +def validate_stock_item_filters(filters): + + filters = validateFilterString(filters, model=stock.models.StockItem) + + return filters + + +def validate_stock_location_filters(filters): + + filters = validateFilterString(filters, model=stock.models.StockLocation) + + return filters + + class LabelTemplate(models.Model): """ Base class for generic, filterable labels. @@ -71,13 +85,6 @@ class LabelTemplate(models.Model): validators=[FileExtensionValidator(allowed_extensions=['html'])], ) - filters = models.CharField( - blank=True, max_length=250, - help_text=_('Query filters (comma-separated list of key=value pairs'), - verbose_name=_('Filters'), - validators=[validateFilterString] - ) - enabled = models.BooleanField( default=True, verbose_name=_('Enabled'), @@ -125,6 +132,14 @@ class StockItemLabel(LabelTemplate): SUBDIR = "stockitem" + filters = models.CharField( + blank=True, max_length=250, + help_text=_('Query filters (comma-separated list of key=value pairs'), + verbose_name=_('Filters'), + validators=[ + validate_stock_item_filters] + ) + def matches_stock_item(self, item): """ Test if this label template matches a given StockItem object @@ -170,6 +185,14 @@ class StockLocationLabel(LabelTemplate): SUBDIR = "stocklocation" + filters = models.CharField( + blank=True, max_length=250, + help_text=_('Query filters (comma-separated list of key=value pairs'), + verbose_name=_('Filters'), + validators=[ + validate_stock_location_filters] + ) + def matches_stock_location(self, location): """ Test if this label template matches a given StockLocation object