mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-12 10:05:39 +00:00
[Plugin] Enhanced custom validation (#6410)
* Use registry.get_plugin() - Instead of registry.plugins.get() - get_plugin checks registry hash - performs registry reload if necessary * Add PluginValidationMixin class - Allows the entire model to be validated via plugins - Called on model.full_clean() - Called on model.save() * Update Validation sample plugin * Fix for InvenTreeTree models * Refactor build.models - Expose models to plugin validation * Update stock.models * Update more models - common.models - company.models * Update more models - label.models - order.models - part.models * More model updates * Update docs * Fix for potential plugin edge case - plugin slug is globally unique - do not use get_or_create with two lookup fields - will throw an IntegrityError if you change the name of a plugin * Inherit DiffMixin into PluginValidationMixin - Allows us to pass model diffs through to validation - Plugins can validate based on what has *changed* * Update documentation * Add get_plugin_config helper function * Bug fix * Bug fix * Update plugin hash when calling set_plugin_state * Working on unit testing * More unit testing * Move get_plugin_config into registry.py * Move extract_int into InvenTree.helpers * Fix log formatting * Update model definitions - Ensure there are no changes to the migrations * Comment out format line * Fix access to get_plugin_config * Fix tests for SimpleActionPlugin * More unit test fixes
This commit is contained in:
@ -4,7 +4,7 @@ title: Validation Mixin
|
||||
|
||||
## ValidationMixin
|
||||
|
||||
The `ValidationMixin` class enables plugins to perform custom validation of various fields.
|
||||
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.
|
||||
|
||||
@ -14,6 +14,88 @@ Any of the methods described below can be implemented in a custom plugin to prov
|
||||
!!! 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 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.
|
||||
|
||||
The `validate_model_instance` method is passed the following arguments:
|
||||
|
||||
| Argument | Description |
|
||||
| --- | --- |
|
||||
| `instance` | The model instance to be validated |
|
||||
| `deltas` | A dict of field deltas (if the instance is being updated) |
|
||||
|
||||
```python
|
||||
def validate_model_instance(self, instance, deltas=None):
|
||||
"""Validate the supplied model instance.
|
||||
|
||||
Arguments:
|
||||
instance: The model instance to be validated
|
||||
deltas: A dict of field deltas (if the instance is being updated)
|
||||
"""
|
||||
...
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
Presented below is a simple working example for a plugin which implements the `validate_model_instance` method:
|
||||
|
||||
```python
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import ValidationMixin
|
||||
|
||||
import part.models
|
||||
|
||||
|
||||
class MyValidationMixin(Validationixin, 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.
|
||||
|
Reference in New Issue
Block a user