mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Model introspection
- Find the class registered to the model (or log an error) - Pass the api_url through to the frontend
This commit is contained in:
		| @@ -17,15 +17,16 @@ import base64 | ||||
| from secrets import compare_digest | ||||
| from datetime import datetime, timedelta | ||||
|  | ||||
| from django.apps import apps | ||||
| from django.db import models, transaction | ||||
| from django.db.utils import IntegrityError, OperationalError | ||||
| from django.conf import settings | ||||
| from django.contrib.auth.models import User, Group | ||||
| from django.contrib.contenttypes.fields import GenericForeignKey | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.db.utils import IntegrityError, OperationalError | ||||
| from django.conf import settings | ||||
| from django.contrib.humanize.templatetags.humanize import naturaltime | ||||
| from django.urls import reverse | ||||
| from django.utils.timezone import now | ||||
| from django.contrib.humanize.templatetags.humanize import naturaltime | ||||
|  | ||||
| from djmoney.settings import CURRENCY_CHOICES | ||||
| from djmoney.contrib.exchange.models import convert_money | ||||
| @@ -587,6 +588,58 @@ class BaseInvenTreeSetting(models.Model): | ||||
|  | ||||
|         return setting.get('model', None) | ||||
|  | ||||
|     def model_class(self): | ||||
|         """ | ||||
|         Return the model class associated with this setting, if (and only if): | ||||
|  | ||||
|         - It has a defined 'model' parameter | ||||
|         - The 'model' parameter is of the form app.model | ||||
|         - The 'model' parameter has matches a known app model | ||||
|         """ | ||||
|  | ||||
|         model_name = self.model_name() | ||||
|  | ||||
|         if not model_name: | ||||
|             return None | ||||
|          | ||||
|         try: | ||||
|             (app, mdl) = model_name.strip().split('.') | ||||
|         except ValueError: | ||||
|             logger.error(f"Invalid 'model' parameter for setting {self.key} : '{model_name}'") | ||||
|             return None | ||||
|  | ||||
|         app_models = apps.all_models.get(app, None) | ||||
|  | ||||
|         if app_models is None: | ||||
|             logger.error(f"Error retrieving model class '{model_name}' for setting '{self.key}' - no app named '{app}'") | ||||
|             return None | ||||
|  | ||||
|         model = app_models.get(mdl, None) | ||||
|  | ||||
|         if model is None: | ||||
|             logger.error(f"Error retrieving model class '{model_name}' for setting '{self.key}' - no model named '{mdl}'") | ||||
|             return None | ||||
|  | ||||
|         # Looks like we have found a model! | ||||
|         return model | ||||
|  | ||||
|     def api_url(self): | ||||
|         """ | ||||
|         Return the API url associated with the linked model, | ||||
|         if provided, and valid! | ||||
|         """ | ||||
|  | ||||
|         model_class = self.model_class() | ||||
|  | ||||
|         if model_class: | ||||
|             # If a valid class has been found, see if it has registered an API URL | ||||
|             try: | ||||
|                 return model_class.get_api_url() | ||||
|             except: | ||||
|                 pass | ||||
|          | ||||
|         return None | ||||
|  | ||||
|     def is_bool(self): | ||||
|         """ | ||||
|         Check if this setting is required to be a boolean value | ||||
| @@ -617,7 +670,7 @@ class BaseInvenTreeSetting(models.Model): | ||||
|             return 'integer' | ||||
|  | ||||
|         elif self.is_model(): | ||||
|             return 'model' | ||||
|             return 'related field' | ||||
|  | ||||
|         else: | ||||
|             return 'string' | ||||
|   | ||||
| @@ -30,6 +30,8 @@ class SettingsSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|     model_name = serializers.CharField(read_only=True) | ||||
|  | ||||
|     api_url = serializers.CharField(read_only=True) | ||||
|  | ||||
|     def get_choices(self, obj): | ||||
|         """ | ||||
|         Returns the choices available for a given item | ||||
| @@ -78,6 +80,7 @@ class GlobalSettingsSerializer(SettingsSerializer): | ||||
|             'type', | ||||
|             'choices', | ||||
|             'model_name', | ||||
|             'api_url', | ||||
|         ] | ||||
|  | ||||
|  | ||||
| @@ -100,6 +103,7 @@ class UserSettingsSerializer(SettingsSerializer): | ||||
|             'type', | ||||
|             'choices', | ||||
|             'model_name', | ||||
|             'api_url', | ||||
|         ] | ||||
|  | ||||
|  | ||||
| @@ -129,6 +133,7 @@ class GenericReferencedSettingSerializer(SettingsSerializer): | ||||
|                 'type', | ||||
|                 'choices', | ||||
|                 'model_name', | ||||
|                 'api_url', | ||||
|             ] | ||||
|  | ||||
|         # set Meta class | ||||
|   | ||||
| @@ -137,7 +137,7 @@ class PluginSetting(common.models.BaseInvenTreeSetting): | ||||
|  | ||||
|         if 'settings' not in kwargs: | ||||
|  | ||||
|             plugin = kwargs.pop('plugin') | ||||
|             plugin = kwargs.pop('plugin', None) | ||||
|  | ||||
|             if plugin: | ||||
|  | ||||
|   | ||||
| @@ -68,7 +68,12 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi | ||||
|         'SELECT_COMPANY': { | ||||
|             'name': 'Company', | ||||
|             'description': 'Select a company object from the database', | ||||
|             'model': 'company.Company', | ||||
|             'model': 'company.company', | ||||
|         }, | ||||
|         'SELECT_PART': { | ||||
|             'name': 'Part', | ||||
|             'description': 'Select a part object from the database', | ||||
|             'model': 'part.part', | ||||
|         }, | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -22,9 +22,6 @@ | ||||
|         {{ setting.description }} | ||||
|     </td> | ||||
|     <td> | ||||
|         {% if setting.model_name %} | ||||
|         <b>Model name: {{ setting.model_name }}</b> | ||||
|         {% endif %} | ||||
|         {% if setting.is_bool %} | ||||
|         <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 %}> | ||||
|   | ||||
| @@ -71,9 +71,24 @@ function editSetting(key, options={}) { | ||||
|                     help_text: response.description, | ||||
|                     type: response.type, | ||||
|                     choices: response.choices, | ||||
|                     value: response.value, | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             // Foreign key lookup available! | ||||
|             if (response.type == 'related field') { | ||||
|                  | ||||
|                 if (response.model_name && response.api_url) { | ||||
|                     fields.value.type = 'related field'; | ||||
|                     fields.value.model = response.model_name.split('.').at(-1); | ||||
|                     fields.value.api_url = response.api_url; | ||||
|                 } else { | ||||
|                     // Unknown / unsupported model type, default to 'text' field | ||||
|                     fields.value.type = 'text'; | ||||
|                     console.warn(`Unsupported model type: '${response.model_name}' for setting '${response.key}'`); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             constructChangeForm(fields, { | ||||
|                 url: url, | ||||
|                 method: 'PATCH', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user