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:
@ -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
|
||||
|
||||
|
@ -28,3 +28,4 @@ registry = Registry()
|
||||
registry['model_features'] = {
|
||||
feature: collections.defaultdict(set) for feature in EXTRAS_FEATURES
|
||||
}
|
||||
registry['denormalized_fields'] = collections.defaultdict(list)
|
||||
|
54
netbox/netbox/denormalized.py
Normal file
54
netbox/netbox/denormalized.py
Normal 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')
|
Reference in New Issue
Block a user