Improvements for part.full_name (#5946)

* Improvements for part.full_name

- Compile and cache the template
- Reduces typical render time from ~20ms to ~0.2ms

* Force autoescape
This commit is contained in:
Oliver 2023-11-21 00:12:25 +11:00 committed by GitHub
parent cb537780dc
commit 317c2666b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 35 deletions

68
InvenTree/part/helpers.py Normal file
View File

@ -0,0 +1,68 @@
"""Various helper functions for the part app"""
import logging
from jinja2 import Environment
logger = logging.getLogger('inventree')
# Compiled template for rendering the 'full_name' attribute of a Part
_part_full_name_template = None
_part_full_name_template_string = ''
def compile_full_name_template(*args, **kwargs):
"""Recompile the template for rendering the 'full_name' attribute of a Part.
This function is called whenever the 'PART_NAME_FORMAT' setting is changed.
"""
from common.models import InvenTreeSetting
global _part_full_name_template
global _part_full_name_template_string
template_string = InvenTreeSetting.get_setting('PART_NAME_FORMAT', '')
# Skip if the template string has not changed
if template_string == _part_full_name_template_string and _part_full_name_template is not None:
return _part_full_name_template
_part_full_name_template_string = template_string
env = Environment(
autoescape=True,
variable_start_string='{{',
variable_end_string='}}'
)
# Compile the template
try:
_part_full_name_template = env.from_string(template_string)
except Exception:
_part_full_name_template = None
return _part_full_name_template
def render_part_full_name(part) -> str:
"""Render the 'full_name' attribute of a Part.
To improve render efficiency, we re-compile the template whenever the setting is changed
Args:
part: The Part object to render
"""
template = compile_full_name_template()
if template:
try:
return template.render(part=part)
except Exception as e:
logger.warning("exception while trying to create full name for part %s: %s", part.name, e)
# Fallback to the default format
elements = [el for el in [part.IPN, part.name, part.revision] if el]
return ' | '.join(elements)

View File

@ -27,7 +27,6 @@ from django_cleanup import cleanup
from djmoney.contrib.exchange.exceptions import MissingRate from djmoney.contrib.exchange.exceptions import MissingRate
from djmoney.contrib.exchange.models import convert_money from djmoney.contrib.exchange.models import convert_money
from djmoney.money import Money from djmoney.money import Money
from jinja2 import Template
from mptt.exceptions import InvalidMove from mptt.exceptions import InvalidMove
from mptt.managers import TreeManager from mptt.managers import TreeManager
from mptt.models import MPTTModel, TreeForeignKey from mptt.models import MPTTModel, TreeForeignKey
@ -40,6 +39,7 @@ import InvenTree.conversion
import InvenTree.fields import InvenTree.fields
import InvenTree.ready import InvenTree.ready
import InvenTree.tasks import InvenTree.tasks
import part.helpers as part_helpers
import part.settings as part_settings import part.settings as part_settings
import users.models import users.models
from build import models as BuildModels from build import models as BuildModels
@ -692,41 +692,9 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
@property @property
def full_name(self): def full_name(self):
"""Format a 'full name' for this Part based on the format PART_NAME_FORMAT defined in InvenTree settings. """Format a 'full name' for this Part based on the format PART_NAME_FORMAT defined in InvenTree settings"""
As a failsafe option, the following is done: return part_helpers.render_part_full_name(self)
- IPN (if not null)
- Part name
- Part variant (if not null)
Elements are joined by the | character
"""
full_name_pattern = InvenTreeSetting.get_setting('PART_NAME_FORMAT')
try:
context = {'part': self}
template_string = Template(full_name_pattern)
full_name = template_string.render(context)
return full_name
except Exception as attr_err:
logger.warning("exception while trying to create full name for part %s: %s", self.name, attr_err)
# Fallback to default format
elements = []
if self.IPN:
elements.append(self.IPN)
elements.append(self.name)
if self.revision:
elements.append(self.revision)
return ' | '.join(elements)
def get_absolute_url(self): def get_absolute_url(self):
"""Return the web URL for viewing this part.""" """Return the web URL for viewing this part."""