1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Closes #9903: Implement a mechanism for automatically updating denormalized fields

This commit is contained in:
jeremystretch
2022-08-02 13:49:34 -04:00
parent 29a611c729
commit e96620260a
3 changed files with 56 additions and 0 deletions

View File

@ -124,6 +124,7 @@ Custom field UI visibility has no impact on API operation.
* [#9261](https://github.com/netbox-community/netbox/issues/9261) - `NetBoxTable` no longer automatically clears pre-existing calls to `prefetch_related()` on its queryset
* [#9434](https://github.com/netbox-community/netbox/issues/9434) - Enabled `django-rich` test runner for more user-friendly output
* [#9903](https://github.com/netbox-community/netbox/issues/9903) - Implement a mechanism for automatically updating denormalized fields
### REST API Changes

View File

@ -28,3 +28,4 @@ registry = Registry()
registry['model_features'] = {
feature: collections.defaultdict(set) for feature in EXTRAS_FEATURES
}
registry['denormalized_fields'] = collections.defaultdict(list)

View File

@ -0,0 +1,54 @@
import logging
from django.db.models.signals import post_save
from django.dispatch import receiver
from extras.registry import registry
logger = logging.getLogger('netbox.denormalized')
def register(model, field_name, mappings):
"""
Register a denormalized model field to ensure that it is kept up-to-date with the related object.
Args:
model: The class being updated
field_name: The name of the field related to the triggering instance
mappings: Dictionary mapping of local to remote fields
"""
logger.debug(f'Registering denormalized field {model}.{field_name}')
field = model._meta.get_field(field_name)
rel_model = field.related_model
registry['denormalized_fields'][rel_model].append(
(model, field_name, mappings)
)
@receiver(post_save)
def update_denormalized_fields(sender, instance, created, raw, **kwargs):
"""
Check if the sender has denormalized fields registered, and update them as necessary.
"""
# Skip for new objects or those being populated from raw data
if created or raw:
return
# Look up any denormalized fields referencing this model from the application registry
for model, field_name, mappings in registry['denormalized_fields'].get(sender, []):
logger.debug(f'Updating denormalized values for {model}.{field_name}')
filter_params = {
field_name: instance.pk,
}
update_params = {
# Map the denormalized field names to the instance's values
denorm: getattr(instance, origin) for denorm, origin in mappings.items()
}
# TODO: Improve efficiency here by placing conditions on the query?
# Update all the denormalized fields with the triggering object's new values
count = model.objects.filter(**filter_params).update(**update_params)
logger.debug(f'Updated {count} rows')