mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Parameter validation via plugin (#4958)
* Expose part parameter validation to plugins - Allow ValidationMixin plugins to validate part parameter values * Update sample plugin * Catch and re-throw error * Update docs * Improve plugin docs * Simplify validation sample * Calculate numeric value first
This commit is contained in:
parent
1d85b70313
commit
b0338e181e
@ -572,7 +572,7 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
|
||||
"""Ensure that the IPN (internal part number) is valid for this Part"
|
||||
|
||||
- Validation is handled by custom plugins
|
||||
- By default, no validation checks are perfomed
|
||||
- By default, no validation checks are performed
|
||||
"""
|
||||
|
||||
from plugin.registry import registry
|
||||
@ -3505,6 +3505,24 @@ class PartParameter(MetadataMixin, models.Model):
|
||||
'data': _('Invalid choice for parameter value')
|
||||
})
|
||||
|
||||
self.calculate_numeric_value()
|
||||
|
||||
# Run custom validation checks (via plugins)
|
||||
from plugin.registry import registry
|
||||
|
||||
for plugin in registry.with_mixin('validation'):
|
||||
|
||||
# Note: The validate_part_parameter function may raise a ValidationError
|
||||
try:
|
||||
result = plugin.validate_part_parameter(self, self.data)
|
||||
if result:
|
||||
break
|
||||
except ValidationError as exc:
|
||||
# Re-throw the ValidationError against the 'data' field
|
||||
raise ValidationError({
|
||||
'data': exc.message
|
||||
})
|
||||
|
||||
def calculate_numeric_value(self):
|
||||
"""Calculate a numeric value for the parameter data.
|
||||
|
||||
|
@ -21,6 +21,7 @@ class ValidationMixin:
|
||||
|
||||
- Part names
|
||||
- Part IPN (internal part number) values
|
||||
- Part parameter values
|
||||
- Serial numbers
|
||||
- Batch codes
|
||||
|
||||
@ -150,6 +151,21 @@ class ValidationMixin:
|
||||
"""
|
||||
return None
|
||||
|
||||
def validate_part_parameter(self, parameter, data):
|
||||
"""Validate a parameter value.
|
||||
|
||||
Arguments:
|
||||
parameter: The parameter we are validating
|
||||
data: The proposed parameter value
|
||||
|
||||
Returns:
|
||||
None or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError if the proposed parameter value is objectionable
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NavigationMixin:
|
||||
"""Mixin that enables custom navigation links with the plugin."""
|
||||
|
@ -18,7 +18,7 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin):
|
||||
SLUG = "validator"
|
||||
TITLE = "Custom Validator Plugin"
|
||||
DESCRIPTION = "A sample plugin for demonstrating custom validation functionality"
|
||||
VERSION = "0.2"
|
||||
VERSION = "0.3.0"
|
||||
|
||||
SETTINGS = {
|
||||
'ILLEGAL_PART_CHARS': {
|
||||
@ -78,6 +78,17 @@ class CustomValidationMixin(SettingsMixin, ValidationMixin, InvenTreePlugin):
|
||||
if self.get_setting('IPN_MUST_CONTAIN_Q') and 'Q' not in ipn:
|
||||
raise ValidationError("IPN must contain 'Q'")
|
||||
|
||||
def validate_part_parameter(self, parameter, data):
|
||||
"""Validate part parameter data.
|
||||
|
||||
These examples are silly, but serve to demonstrate how the feature could be used
|
||||
"""
|
||||
|
||||
if parameter.template.name.lower() in ['length', 'width']:
|
||||
d = int(data)
|
||||
if d >= 100:
|
||||
raise ValidationError("Value must be less than 100")
|
||||
|
||||
def validate_serial_number(self, serial: str, part):
|
||||
"""Validate serial number for a given StockItem
|
||||
|
||||
|
BIN
docs/docs/assets/images/plugin/enable_events.png
Normal file
BIN
docs/docs/assets/images/plugin/enable_events.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
BIN
docs/docs/assets/images/plugin/enable_schedule.png
Normal file
BIN
docs/docs/assets/images/plugin/enable_schedule.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
@ -8,6 +8,15 @@ The `EventMixin` class enables plugins to respond to certain triggered events.
|
||||
|
||||
When a certain (server-side) event occurs, the background worker passes the event information to any plugins which inherit from the `EventMixin` base class.
|
||||
|
||||
!!! tip "Enable Event Integration"
|
||||
The *Enable Event Integration* option must first be enabled to allow plugins to respond to events.
|
||||
|
||||
{% with id="events", url="plugin/enable_events.png", description="Enable event integration" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
### Example
|
||||
|
||||
Implementing classes must provide a `process_event` function:
|
||||
|
||||
```python
|
||||
@ -29,7 +38,7 @@ class EventPlugin(EventMixin, InvenTreePlugin):
|
||||
|
||||
### Events
|
||||
|
||||
Events are passed through using a string identifier, e.g. 'build.completed'
|
||||
Events are passed through using a string identifier, e.g. `build.completed`
|
||||
|
||||
The arguments (and keyword arguments) passed to the receiving function depend entirely on the type of event.
|
||||
|
||||
|
@ -21,36 +21,37 @@ Panel content can be rendered by returning HTML directly, or by rendering from a
|
||||
|
||||
Each plugin can register templates simply by providing a 'templates' directory in its root path.
|
||||
|
||||
The convention is that each 'templates' directory contains a subdirectory with the same name as the plugin :
|
||||
* e.g. templates/myplugin/my_template.html
|
||||
The convention is that each 'templates' directory contains a subdirectory with the same name as the plugin (e.g. `templates/myplugin/my_template.html`)
|
||||
|
||||
|
||||
In this case, the template can then be loaded (from any plugin!) by loading "myplugin/my_template.html".
|
||||
|
||||
In this case, the template can then be loaded (from any plugin!) by loading `myplugin/my_template.html`.
|
||||
|
||||
|
||||
### Javascript
|
||||
|
||||
Custom code can be provided which will run when the particular panel is first loaded (by selecting it from the side menu).
|
||||
|
||||
To add some javascript code, you can add a reference to a function that will be called when the panel is loaded with the 'javascript' key in the panel description :
|
||||
```
|
||||
{
|
||||
'title': "Updates",
|
||||
'description': "Latest updates for this part",
|
||||
'javascript': 'alert("You just loaded this panel!")',
|
||||
}
|
||||
To add some javascript code, you can add a reference to a function that will be called when the panel is loaded with the 'javascript' key in the panel description:
|
||||
|
||||
```python
|
||||
{
|
||||
'title': "Updates",
|
||||
'description': "Latest updates for this part",
|
||||
'javascript': 'alert("You just loaded this panel!")',
|
||||
}
|
||||
```
|
||||
|
||||
Or to add a template file that will be rendered as javascript code, from the plugin template folder, with the 'javascript_template' key in the panel description :
|
||||
```
|
||||
{
|
||||
'title': "Updates",
|
||||
'description': "Latest updates for this part",
|
||||
'javascript_template': 'pluginTemplatePath/myJavascriptFile.js',
|
||||
}
|
||||
Or to add a template file that will be rendered as javascript code, from the plugin template folder, with the 'javascript_template' key in the panel description:
|
||||
|
||||
```python
|
||||
{
|
||||
'title': "Updates",
|
||||
'description': "Latest updates for this part",
|
||||
'javascript_template': 'pluginTemplatePath/myJavascriptFile.js',
|
||||
}
|
||||
```
|
||||
note : see convention for template directory above.
|
||||
|
||||
Note : see convention for template directory above.
|
||||
|
||||
## Example Implementation
|
||||
|
||||
|
@ -11,6 +11,13 @@ The ScheduleMixin class provides a plugin with the ability to call functions at
|
||||
- Plugin member functions can be called
|
||||
- Global functions can be specified using dotted notation
|
||||
|
||||
!!! tip "Enable Schedule Integration"
|
||||
The *Enable Schedule Integration* option but be enabled, for scheduled plugin events to be activated.
|
||||
|
||||
{% with id="schedule", url="plugin/enable_schedule.png", description="Enable schedule integration" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
### Example
|
||||
|
||||
An example of a plugin which supports scheduled tasks:
|
||||
@ -50,3 +57,6 @@ class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, InvenTreePlugin):
|
||||
secret_value = self.get_setting('SECRET')
|
||||
print(f"foo - SECRET = {secret_value})
|
||||
```
|
||||
|
||||
!!! 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](https://github.com/inventree/InvenTree/blob/master/InvenTree/plugin/samples/integration/scheduled_task.py).
|
||||
|
@ -11,8 +11,13 @@ The *SettingsMixin* allows the plugin to save and load persistent settings to th
|
||||
|
||||
Use the class constant `SETTINGS` for a dict of settings that should be added as global database settings.
|
||||
|
||||
The dict must be formatted similar to the following sample that shows how to use validator choices and default. Take a look at the settings defined in `InvenTree.common.models.InvenTreeSetting` for all possible parameters.
|
||||
The dict must be formatted similar to the following sample that shows how to use validator choices and default.
|
||||
|
||||
Take a look at the settings defined in `InvenTree.common.models.InvenTreeSetting` for all possible parameters.
|
||||
|
||||
### Example
|
||||
|
||||
Below is a simple example of how a plugin can implement settings:
|
||||
|
||||
``` python
|
||||
class PluginWithSettings(SettingsMixin, InvenTreePlugin):
|
||||
@ -60,6 +65,9 @@ class PluginWithSettings(SettingsMixin, InvenTreePlugin):
|
||||
}
|
||||
```
|
||||
|
||||
!!! info "More Info"
|
||||
For more information on any of the methods described below, refer to the InvenTree source code.
|
||||
|
||||
!!! tip "Hidden Settings"
|
||||
Plugin settings can be hidden from the settings page by marking them as 'hidden'
|
||||
|
||||
|
@ -9,14 +9,14 @@ The `ValidationMixin` class enables plugins to perform custom validation of vari
|
||||
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.
|
||||
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](https://github.com/inventree/InvenTree/blob/master/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.
|
||||
|
||||
### 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 implemente to ensure that a part name conforms to a required convention.
|
||||
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 implement 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.
|
||||
|
||||
@ -24,6 +24,10 @@ If the custom method determines that the part name is *objectionable*, it should
|
||||
|
||||
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.
|
||||
|
||||
### Part Parameter Values
|
||||
|
||||
[Part parameters](../../part/parameter.md) can also have custom validation rules applied, by implementing the `validate_part_parameter` method. A plugin which implements this method should raise a `ValidationError` with an appropriate message if the part parameter value does not match a required convention.
|
||||
|
||||
### Batch Codes
|
||||
|
||||
[Batch codes](../../stock/tracking.md#batch-codes) can be generated and/or validated by custom plugins.
|
||||
@ -36,7 +40,7 @@ The `generate_batch_code` method can be implemented to generate a new batch code
|
||||
|
||||
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](../../stock/tracking.md#serial-numbers) uses a simple algorithm to validate and increment serial numbers. More complex behaviours can be implemented using the `ValidationMixin` plugin class and the following custom methods:
|
||||
The default InvenTree [serial numbering system](../../stock/tracking.md#serial-numbers) 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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user