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.models import convert_money
from djmoney.money import Money
from jinja2 import Template
from mptt.exceptions import InvalidMove
from mptt.managers import TreeManager
from mptt.models import MPTTModel, TreeForeignKey
@ -40,6 +39,7 @@ import InvenTree.conversion
import InvenTree.fields
import InvenTree.ready
import InvenTree.tasks
import part.helpers as part_helpers
import part.settings as part_settings
import users.models
from build import models as BuildModels
@ -692,41 +692,9 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
@property
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:
- 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)
return part_helpers.render_part_full_name(self)
def get_absolute_url(self):
"""Return the web URL for viewing this part."""