2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 03:56:43 +00:00

Simplify the various settings objects, to improve retrieval of 'parameters' from the base class

- Remove the GenericReferencedSettingsClass mixin
- Each subclass defines a very simple get_kwargs() method
- Now, at object level *and* class level we can perform lookup of settings and actually get proper data back
- Adds "model" option to setting (precursor of things to come)
This commit is contained in:
Oliver 2022-05-12 16:45:27 +10:00
parent 56f36d4b4b
commit e112d555d4
5 changed files with 105 additions and 114 deletions

View File

@ -136,6 +136,19 @@ class BaseInvenTreeSetting(models.Model):
return settings return settings
def get_kwargs(self):
"""
Construct kwargs for doing class-based settings lookup,
depending on *which* class we are.
This is necessary to abtract the settings object
from the implementing class (e.g plugins)
Subclasses should override this function to ensure the kwargs are correctly set.
"""
return {}
@classmethod @classmethod
def get_setting_definition(cls, key, **kwargs): def get_setting_definition(cls, key, **kwargs):
""" """
@ -319,11 +332,11 @@ class BaseInvenTreeSetting(models.Model):
value = setting.value value = setting.value
# Cast to boolean if necessary # Cast to boolean if necessary
if setting.is_bool(**kwargs): if setting.is_bool():
value = InvenTree.helpers.str2bool(value) value = InvenTree.helpers.str2bool(value)
# Cast to integer if necessary # Cast to integer if necessary
if setting.is_int(**kwargs): if setting.is_int():
try: try:
value = int(value) value = int(value)
except (ValueError, TypeError): except (ValueError, TypeError):
@ -390,19 +403,19 @@ class BaseInvenTreeSetting(models.Model):
@property @property
def name(self): def name(self):
return self.__class__.get_setting_name(self.key) return self.__class__.get_setting_name(self.key, **self.get_kwargs())
@property @property
def default_value(self): def default_value(self):
return self.__class__.get_setting_default(self.key) return self.__class__.get_setting_default(self.key, **self.get_kwargs())
@property @property
def description(self): def description(self):
return self.__class__.get_setting_description(self.key) return self.__class__.get_setting_description(self.key, **self.get_kwargs())
@property @property
def units(self): def units(self):
return self.__class__.get_setting_units(self.key) return self.__class__.get_setting_units(self.key, **self.get_kwargs())
def clean(self, **kwargs): def clean(self, **kwargs):
""" """
@ -512,12 +525,12 @@ class BaseInvenTreeSetting(models.Model):
except self.DoesNotExist: except self.DoesNotExist:
pass pass
def choices(self, **kwargs): def choices(self):
""" """
Return the available choices for this setting (or None if no choices are defined) Return the available choices for this setting (or None if no choices are defined)
""" """
return self.__class__.get_setting_choices(self.key, **kwargs) return self.__class__.get_setting_choices(self.key, **self.get_kwargs())
def valid_options(self): def valid_options(self):
""" """
@ -531,14 +544,14 @@ class BaseInvenTreeSetting(models.Model):
return [opt[0] for opt in choices] return [opt[0] for opt in choices]
def is_choice(self, **kwargs): def is_choice(self):
""" """
Check if this setting is a "choice" field Check if this setting is a "choice" field
""" """
return self.__class__.get_setting_choices(self.key, **kwargs) is not None return self.__class__.get_setting_choices(self.key, **self.get_kwargs()) is not None
def as_choice(self, **kwargs): def as_choice(self):
""" """
Render this setting as the "display" value of a choice field, Render this setting as the "display" value of a choice field,
e.g. if the choices are: e.g. if the choices are:
@ -547,7 +560,7 @@ class BaseInvenTreeSetting(models.Model):
then display 'A4 paper' then display 'A4 paper'
""" """
choices = self.get_setting_choices(self.key, **kwargs) choices = self.get_setting_choices(self.key, **self.get_kwargs())
if not choices: if not choices:
return self.value return self.value
@ -558,12 +571,28 @@ class BaseInvenTreeSetting(models.Model):
return self.value return self.value
def is_bool(self, **kwargs): def is_model(self):
"""
Check if this setting references a model instance in the database
"""
return self.model_name() is not None
def model_name(self):
"""
Return the model name associated with this setting
"""
setting = self.get_setting_definition(self.key, **self.get_kwargs())
return setting.get('model', None)
def is_bool(self):
""" """
Check if this setting is required to be a boolean value Check if this setting is required to be a boolean value
""" """
validator = self.__class__.get_setting_validator(self.key, **kwargs) validator = self.__class__.get_setting_validator(self.key, **self.get_kwargs())
return self.__class__.validator_is_bool(validator) return self.__class__.validator_is_bool(validator)
@ -576,17 +605,20 @@ class BaseInvenTreeSetting(models.Model):
return InvenTree.helpers.str2bool(self.value) return InvenTree.helpers.str2bool(self.value)
def setting_type(self, **kwargs): def setting_type(self):
""" """
Return the field type identifier for this setting object Return the field type identifier for this setting object
""" """
if self.is_bool(**kwargs): if self.is_bool():
return 'boolean' return 'boolean'
elif self.is_int(**kwargs): elif self.is_int():
return 'integer' return 'integer'
elif self.is_model():
return 'model'
else: else:
return 'string' return 'string'
@ -603,12 +635,12 @@ class BaseInvenTreeSetting(models.Model):
return False return False
def is_int(self, **kwargs): def is_int(self,):
""" """
Check if the setting is required to be an integer value: Check if the setting is required to be an integer value:
""" """
validator = self.__class__.get_setting_validator(self.key, **kwargs) validator = self.__class__.get_setting_validator(self.key, **self.get_kwargs())
return self.__class__.validator_is_int(validator) return self.__class__.validator_is_int(validator)
@ -651,88 +683,7 @@ class BaseInvenTreeSetting(models.Model):
@property @property
def protected(self): def protected(self):
return self.__class__.is_protected(self.key) return self.__class__.is_protected(self.key, **self.get_kwargs())
class GenericReferencedSettingClass:
"""
This mixin can be used to add reference keys to static properties
Sample:
```python
class SampleSetting(GenericReferencedSettingClass, common.models.BaseInvenTreeSetting):
class Meta:
unique_together = [
('sample', 'key'),
]
REFERENCE_NAME = 'sample'
@classmethod
def get_setting_definition(cls, key, **kwargs):
# mysampledict contains the dict with all settings for this SettingClass - this could also be a dynamic lookup
kwargs['settings'] = mysampledict
return super().get_setting_definition(key, **kwargs)
sample = models.charKey( # the name for this field is the additonal key and must be set in the Meta class an REFERENCE_NAME
max_length=256,
verbose_name=_('sample')
)
```
"""
REFERENCE_NAME = None
def _get_reference(self):
"""
Returns dict that can be used as an argument for kwargs calls.
Helps to make overriden calls generic for simple reuse.
Usage:
```python
some_random_function(argument0, kwarg1=value1, **self._get_reference())
```
"""
return {
self.REFERENCE_NAME: getattr(self, self.REFERENCE_NAME)
}
"""
We override the following class methods,
so that we can pass the modified key instance as an additional argument
"""
def clean(self, **kwargs):
kwargs[self.REFERENCE_NAME] = getattr(self, self.REFERENCE_NAME)
super().clean(**kwargs)
def is_bool(self, **kwargs):
kwargs[self.REFERENCE_NAME] = getattr(self, self.REFERENCE_NAME)
return super().is_bool(**kwargs)
@property
def name(self):
return self.__class__.get_setting_name(self.key, **self._get_reference())
@property
def default_value(self):
return self.__class__.get_setting_default(self.key, **self._get_reference())
@property
def description(self):
return self.__class__.get_setting_description(self.key, **self._get_reference())
@property
def units(self):
return self.__class__.get_setting_units(self.key, **self._get_reference())
def choices(self):
return self.__class__.get_setting_choices(self.key, **self._get_reference())
def settings_group_options(): def settings_group_options():
@ -1558,6 +1509,16 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
return self.__class__.get_setting(self.key, user=self.user) return self.__class__.get_setting(self.key, user=self.user)
def get_kwargs(self):
"""
Explicit kwargs required to uniquely identify a particular setting object,
in addition to the 'key' parameter
"""
return {
'user': self.user,
}
class PriceBreak(models.Model): class PriceBreak(models.Model):
""" """

View File

@ -28,6 +28,8 @@ class SettingsSerializer(InvenTreeModelSerializer):
choices = serializers.SerializerMethodField() choices = serializers.SerializerMethodField()
model_name = serializers.CharField(read_only=True)
def get_choices(self, obj): def get_choices(self, obj):
""" """
Returns the choices available for a given item Returns the choices available for a given item
@ -75,6 +77,7 @@ class GlobalSettingsSerializer(SettingsSerializer):
'description', 'description',
'type', 'type',
'choices', 'choices',
'model_name',
] ]
@ -96,6 +99,7 @@ class UserSettingsSerializer(SettingsSerializer):
'user', 'user',
'type', 'type',
'choices', 'choices',
'model_name',
] ]
@ -124,6 +128,7 @@ class GenericReferencedSettingSerializer(SettingsSerializer):
'description', 'description',
'type', 'type',
'choices', 'choices',
'model_name',
] ]
# set Meta class # set Meta class

View File

@ -102,7 +102,7 @@ class PluginConfig(models.Model):
return ret return ret
class PluginSetting(common.models.GenericReferencedSettingClass, common.models.BaseInvenTreeSetting): class PluginSetting(common.models.BaseInvenTreeSetting):
""" """
This model represents settings for individual plugins This model represents settings for individual plugins
""" """
@ -112,7 +112,13 @@ class PluginSetting(common.models.GenericReferencedSettingClass, common.models.B
('plugin', 'key'), ('plugin', 'key'),
] ]
REFERENCE_NAME = 'plugin' plugin = models.ForeignKey(
PluginConfig,
related_name='settings',
null=False,
verbose_name=_('Plugin'),
on_delete=models.CASCADE,
)
@classmethod @classmethod
def get_setting_definition(cls, key, **kwargs): def get_setting_definition(cls, key, **kwargs):
@ -131,7 +137,7 @@ class PluginSetting(common.models.GenericReferencedSettingClass, common.models.B
if 'settings' not in kwargs: if 'settings' not in kwargs:
plugin = kwargs.pop('plugin', None) plugin = kwargs.pop('plugin')
if plugin: if plugin:
@ -142,16 +148,18 @@ class PluginSetting(common.models.GenericReferencedSettingClass, common.models.B
return super().get_setting_definition(key, **kwargs) return super().get_setting_definition(key, **kwargs)
plugin = models.ForeignKey( def get_kwargs(self):
PluginConfig, """
related_name='settings', Explicit kwargs required to uniquely identify a particular setting object,
null=False, in addition to the 'key' parameter
verbose_name=_('Plugin'), """
on_delete=models.CASCADE,
) return {
'plugin': self.plugin,
}
class NotificationUserSetting(common.models.GenericReferencedSettingClass, common.models.BaseInvenTreeSetting): class NotificationUserSetting(common.models.BaseInvenTreeSetting):
""" """
This model represents notification settings for a user This model represents notification settings for a user
""" """
@ -161,8 +169,6 @@ class NotificationUserSetting(common.models.GenericReferencedSettingClass, commo
('method', 'user', 'key'), ('method', 'user', 'key'),
] ]
REFERENCE_NAME = 'method'
@classmethod @classmethod
def get_setting_definition(cls, key, **kwargs): def get_setting_definition(cls, key, **kwargs):
from common.notifications import storage from common.notifications import storage
@ -171,6 +177,17 @@ class NotificationUserSetting(common.models.GenericReferencedSettingClass, commo
return super().get_setting_definition(key, **kwargs) return super().get_setting_definition(key, **kwargs)
def get_kwargs(self):
"""
Explicit kwargs required to uniquely identify a particular setting object,
in addition to the 'key' parameter
"""
return {
'method': self.method,
'user': self.user,
}
method = models.CharField( method = models.CharField(
max_length=255, max_length=255,
verbose_name=_('Method'), verbose_name=_('Method'),

View File

@ -65,6 +65,11 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi
], ],
'default': 'A', 'default': 'A',
}, },
'SELECT_COMPANY': {
'name': 'Company',
'description': 'Select a company object from the database',
'model': 'company.Company',
},
} }
NAVIGATION = [ NAVIGATION = [

View File

@ -22,6 +22,9 @@
{{ setting.description }} {{ setting.description }}
</td> </td>
<td> <td>
{% if setting.model_name %}
<b>Model name: {{ setting.model_name }}</b>
{% endif %}
{% if setting.is_bool %} {% if setting.is_bool %}
<div class='form-check form-switch'> <div class='form-check form-switch'>
<input class='form-check-input boolean-setting' fieldname='{{ setting.key.upper }}' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' {% if setting.as_bool %}checked=''{% endif %} {% if plugin %}plugin='{{ plugin.slug }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}{% if notification_setting %}notification='{{request.user.id}}'{% endif %}> <input class='form-check-input boolean-setting' fieldname='{{ setting.key.upper }}' pk='{{ setting.pk }}' setting='{{ setting.key.upper }}' id='setting-value-{{ setting.key.upper }}' type='checkbox' {% if setting.as_bool %}checked=''{% endif %} {% if plugin %}plugin='{{ plugin.slug }}'{% endif %}{% if user_setting %}user='{{request.user.id}}'{% endif %}{% if notification_setting %}notification='{{request.user.id}}'{% endif %}>