2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-02-12 09:17:13 +00:00
Files
InvenTree/docs/docs/plugins/mixins/validation.md
Oliver fa0d892a62 [WIP] Generic parameters (#10699)
* Add ParameterTemplate model

- Data structure duplicated from PartParameterTemplate

* Apply data migration for templates

* Admin integration

* API endpoints for ParameterTemplate

* Scaffolding

* Add validator for ParameterTemplate model type

- Update migrations
- Make Parameter class abstract (for now)
- Validators

* API updates

- Fix options for model_type
- Add API filters

* Add definition for Parameter model

* Add django admin site integration

* Update InvenTreeParameterMixin class

- Fetch queryset of all linked Parameter instances
- Ensure deletion of linked instances

* API endpoints for Parameter instances

* Refactor UI table for parameter templates

* Add comment for later

* Add "enabled" field to ParameterTemplate model

* Add new field to serializer

* Rough-in new table

* Implement generic "parameter" table

* Enable parameters for Company model

* Change migration for part parameter

- Make it "universal"

* Remove code for ManufacturerPartParameter

* Fix for filters

* Add data import for parameter table

* Add verbose name to ParameterTemplate model

* Removed dead API code

* Update global setting

* Fix typos

* Check global setting for unit validation

* Use GenericForeignKey

* Add generic relationship to allow reverse lookups

* Fixes for table structure

* Add custom serializer field for ContentType with choices

* Adds ContentTypeField

- Handles representation of content type
- Provides human-readable options

* Refactor API filtering for endpoints

- Specify ContentType by ID, model or app label

* Revert change to parameters property

* Define GenericRelationship for linking model

* Refactoring some code

* Add a generic way to back-annotate and prefetch parameters for any model type

* Change panel position

* Directly annotate parameters against different model serializers

* remove defunct admin classes

* Run plugin validation against parameter

* Fix prefetching for PartSerializer

* Implement generic "filtering" against queryset

* Implement generic "ordering" by parameter

* Make parametric table generic

* Refactor segmented panels

* Consolidate part table views

* Fix for parametric part table

- Only display parameters for which we know there is a value

* Add parametric tables for company views

* Fix typo in file name

* Prefetch to reduce hits

* Add generic API mixin for filtering and ordering by parameter

* Fix hook for rebuilding template parameters

* Remove serializer

* Remove old models

* Fix code for copying parameters from category

* Implement more parametric tables:

- ManufacturerPart
- SupplierPart
- Fixes and enhancements

* Add parameter support for orders

* Add UI support for parameters against orders

* Update API version

* Update CHANGELOG.md

* Add parameter support for build orders

* Tweak frontend

* Add renderer

* Remove defunct endpoints

* Add migration requirement

* Require contenttypes to be updated

* Update migration

* Try using ID val

* Adjust migration dependencies

* fix params fixture

* fix schema export

* fix modelset

* Fixes for data migration

* tweak table

* Fix for Category Parameters

* Use branch of demo dataset for testing

* Add parameteric build order table

* disable broken imports

* remove old model from ruleset

* correct test

* Table tweaks

* fix test

* Remove old model type

* fix test

* fix test

* Refactor mixin to avoid specifying model type manually

* fix test

* fix resolve name

* remove unneeded import

* Tweak unit testing

* Fix unit test

* Enable bulk-create

* More fixes

* More unit test tweaks

* Enhancements

* Unit test fixes

* Add some migration tests

* Fix admin tests

* Fix part tests

* adapt expectation

* fix remaining typecheck

* Docs updates

* Rearrange models

* fix paramater caching

* fix doc links

* adjust assumption

* Adjust data migration unit tests

* docs fixes

* Fix docs link

* Fixes

* Tweak formatting

* Add doc for setting

* Add metadata view for parameters

* Add metadata view for ParamterTemplate

* Update CHANGELOG file

* Deconflict model_type fields

* Invert key:value

* Revert "Invert key:value"

This reverts commit d555658db2.

* fix assert

* Update API rev notes

* Initial unit tests for API

* Test parameter create / edit / delete via the API

* Add some more unit tests for the API

* Validate queryset annotation

- Add unit test with large dataset
- Ensure number of queries is fixed
- Fix for prefetching check

* Add breaking change info to CHANGELOG.md

* Ensure that parameters are removed when deleting the linked object

* Enhance type hinting

* Refactor part parameter exporter plugin

- Any model which supports parameters can use this now
- Update documentation

* Improve serializer field

* Adjust unit test

* Reimplement checks for locked parts

* Fix unit test for data migration

* Fix for unit test

* Allow disable edit for ParameterTable

* Fix supplier part import wizard

* Add unit tests for template API filtering

* Add playwright tests for purchasing index

* Add tests for manufacturing index page

* ui tests for sales index

* Add data migration tests for ManufacturerPartParameter

* Pull specific branch for python binding tests

* Specify target migration

* Remove debug statement

* Tweak migration unit tests

* Add options for spectacular

* Add explicit choice options

* Ensure empty string values are converted to None

* Don't use custom branch for python checks

* Fix for migration test

* Fix migration test

* Fix reference target

* Remove duplicate enum in spectactular.py

* Add null choice to custom serializer class

* [UI] Edit shipment details

- Pass "pending" status through to the form

* New migration strategy:

part.0144:
- Add new "enabled" field to PartParameterTemplate model
- Add new ContentType fields to the "PartParameterTemplate" and "PartParameter" models
- Data migration for existing "PartParameter" records

part.0145:
- Set NOT NULL constraints on new fields
- Remove the obsolete "part" field from the "PartParameter" model

* More migration updates:

- Create new "models" (without moving the existing tables)
- Data migration for PartCataegoryParameterTemplate model
- Remove PartParameterTemplate and PartParameter models

* Overhaul of migration strategy

- New models simply point to the old database tables
- Perform schema and data migrations on the old models first (in the part app)
- Swap model references in correct order

* Improve checks for data migrations

* Bug fix for data migration

* Add migration unit test to ensure that primary keys are maintained

* Add playwright test for company parameters

* Rename underlying database tables

* Fixes for migration unit tests

* Revert "Rename underlying database tables"

This reverts commit 477c692076.

* Fix for migration sequencing

* Simplify new playwright test

* Remove spectacular collision

* Monkey patch the drf-spectacular warn function

* Do not use custom branch for playwright testing

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-04 20:41:36 +11:00

11 KiB

title
title
Validation Mixin

ValidationMixin

The ValidationMixin class enables plugins to perform custom validation of objects within the database.

Any of the methods described below can be implemented in a custom plugin to provide functionality as required.

!!! info "More Info" For more information on any of the methods described below, refer to the InvenTree source code. [A working example is available as a starting point]({{ sourcefile("src/backend/InvenTree/plugin/samples/integration/validation_sample.py") }}).

!!! info "Multi Plugin Support" It is possible to have multiple plugins loaded simultaneously which support validation methods. For example when validating a field, if one plugin returns a null value (None) then the next plugin (if available) will be queried.

Model Deletion

Any model which inherits the PluginValidationMixin class is exposed to the plugin system for custom deletion validation. Before the model is deleted from the database, it is first passed to the plugin ecosystem to check if it really should be deleted.

A custom plugin may implement the validate_model_deletion method to perform custom validation on the model instance before it is deleted.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_model_deletion options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Model Validation

Any model which inherits the PluginValidationMixin mixin class is exposed to the plugin system for custom validation. Before the model is saved to the database (either when created, or updated), it is first passed to the plugin ecosystem for validation.

Any plugin which inherits the ValidationMixin can implement the validate_model_instance method, and run a custom validation routine.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_model_instance options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Error Messages

Any error messages must be raised as a ValidationError. The ValidationMixin class provides the raise_error method, which is a simple wrapper method which raises a ValidationError

Instance Errors

To indicate an instance validation error (i.e. the validation error applies to the entire model instance), the body of the error should be either a string, or a list of strings.

Field Errors

To indicate a field validation error (i.e. the validation error applies only to a single field on the model instance), the body of the error should be a dict, where the key(s) of the dict correspond to the model fields.

Note that an error can be which corresponds to multiple model instance fields.

Example Plugin

Presented below is a simple working example for a plugin which implements the validate_model_instance method:

from plugin import InvenTreePlugin
from plugin.mixins import ValidationMixin

import part.models


class MyValidationMixin(ValidationMixin, InvenTreePlugin):
    """Custom validation plugin."""

    def validate_model_instance(self, instance, deltas=None):
        """Custom model validation example.

        - A part name and category name must have the same starting letter
        - A PartCategory description field cannot be shortened after it has been created
        """

        if isinstance(instance, part.models.Part):
            if category := instance.category:
                if category.name[0] != part.name[0]:
                    self.raise_error({
                        "name": "Part name and category name must start with the same letter"
                    })

        if isinstance(instance, part.models.PartCategory):
            if deltas and 'description' in deltas:
                d_new = deltas['description']['new']
                d_old = deltas['description']['old']

                if len(d_new) < len(d_old):
                    self.raise_error({
                        "description": "Description cannot be shortened"
                    })

Field Validation

In addition to the general purpose model instance validation routine provided above, the following fields support custom validation routines:

Part Name

By default, part names are not subject to any particular naming conventions or requirements. However if custom validation is required, the validate_part_name method can be implemented to ensure that a part name conforms to a required convention.

If the custom method determines that the part name is objectionable, it should throw a ValidationError which will be handled upstream by parent calling methods.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_name options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Part IPN

Validation of the Part IPN (Internal Part Number) field is exposed to custom plugins via the validate_part_ipn method. Any plugins which extend the ValidationMixin class can implement this method, and raise a ValidationError if the IPN value does not match a required convention.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_ipn options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Parameter Values

Parameters can also have custom validation rules applied, by implementing the validate_parameter method. A plugin which implements this method should raise a ValidationError with an appropriate message if the parameter value does not match a required convention.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_parameter options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Batch Codes

Batch codes can be generated and/or validated by custom plugins.

Validate Batch Code

The validate_batch_code method allows plugins to raise an error if a batch code input by the user does not meet a particular pattern.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_batch_code options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Generate Batch Code

The generate_batch_code method can be implemented to generate a new batch code, based on a set of provided information.

::: plugin.base.integration.ValidationMixin.ValidationMixin.generate_batch_code options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

Serial Numbers

Requirements for serial numbers can vary greatly depending on the application. Rather than attempting to provide a "one size fits all" serial number implementation, InvenTree allows custom serial number schemes to be implemented via plugins.

The default InvenTree serial numbering system uses a simple algorithm to validate and increment serial numbers. More complex behaviors can be implemented using the ValidationMixin plugin class and the following custom methods:

Serial Number Validation

Custom serial number validation can be implemented using the validate_serial_number method. A proposed serial number is passed to this method, which then has the opportunity to raise a ValidationError to indicate that the serial number is not valid.

::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_serial_number options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

!!! info "Stock Item" If the stock_item argument is provided, then this stock item has already been assigned with the provided serial number. This stock item should be excluded from any subsequent checks for uniqueness. The stock_item parameter is optional, and may be None if the serial number is being validated in a context where no stock item is available.

Example

A plugin which requires all serial numbers to be valid hexadecimal values may implement this method as follows:

def validate_serial_number(self, serial: str, part: Part, stock_item: StockItem = None):
    """Validate the supplied serial number

    Arguments:
        serial: The proposed serial number (string)
        part: The Part instance for which this serial number is being validated
        stock_item: The StockItem instance for which this serial number is being validated
    """

    try:
        # Attempt integer conversion
        int(serial, 16)
    except ValueError:
        raise ValidationError("Serial number must be a valid hex value")

Serial Number Sorting

While InvenTree supports arbitrary text values in the serial number fields, behind the scenes it attempts to "coerce" these values into an integer representation for more efficient sorting.

A custom plugin can implement the convert_serial_to_int method to determine how a particular serial number is converted to an integer representation.

::: plugin.base.integration.ValidationMixin.ValidationMixin.convert_serial_to_int options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

!!! info "Not Required" If this method is not implemented, or the serial number cannot be converted to an integer, then the sorting algorithm falls back to the text (string) value

Serial Number Incrementing

A core component of the InvenTree serial number system is the ability to increment serial numbers - to determine the next serial number value in a sequence.

For custom serial number schemes, it is important to provide a method to generate the next serial number given a current value. The increment_serial_number method can be implemented by a plugin to achieve this.

::: plugin.base.integration.ValidationMixin.ValidationMixin.increment_serial_number options: show_bases: False show_root_heading: False show_root_toc_entry: False extra: show_source: True summary: False members: []

!!! info "Invalid Increment" If the provided number cannot be incremented (or an error occurs) the method should return None

Example

Continuing with the hexadecimal example as above, the method may be implemented as follows:

def increment_serial_number(self, serial: str):
    """Provide the next hexadecimal number in sequence"""

    try:
        val = int(serial, 16) + 1
        val = hex(val).upper()[2:]
    except ValueError:
        val = None

    return val

Sample Plugin

A sample plugin which implements custom validation routines is provided in the InvenTree source code:

::: plugin.samples.integration.validation_sample.SampleValidatorPlugin options: show_bases: False show_root_heading: False show_root_toc_entry: False show_source: True members: []