mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-25 16:56:10 -06:00
6347 initial counter field
This commit is contained in:
parent
4208b79514
commit
2e6bc7b53a
68
netbox/utilities/counter.py
Normal file
68
netbox/utilities/counter.py
Normal file
@ -0,0 +1,68 @@
|
||||
from django.db.models import F
|
||||
from django.db.models.signals import post_delete, post_save, pre_save
|
||||
|
||||
from .fields import CounterCacheField
|
||||
|
||||
counters = {}
|
||||
|
||||
|
||||
class Counter(object):
|
||||
counter_name = None
|
||||
foreign_key_field = None
|
||||
child_model = None
|
||||
parent_model = None
|
||||
|
||||
def __init__(self, counter_name, foreign_key_field):
|
||||
self.counter_name = counter_name
|
||||
self.foreign_key_field = foreign_key_field.field
|
||||
self.child_model = self.foreign_key_field.model
|
||||
self.parent_model = self.foreign_key_field.related_model
|
||||
|
||||
self.connect()
|
||||
|
||||
def validate(self):
|
||||
counter_field, _, _, _ = self.parent_model._meta.get_field_by_name(self.counter_name)
|
||||
if not isinstance(counter_field, CounterCacheField):
|
||||
raise TypeError(
|
||||
f"{self.counter_name} should be a CounterCacheField on {self.parent_model}, but is {type(counter_field)}"
|
||||
)
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Hook up post_save, post_delete signal handlers to the fk field to change the count
|
||||
"""
|
||||
name = f"{self.parent_model._meta.model_name}.{self.child_model._meta.model_name}.{self.foreign_key_field.name}"
|
||||
counted_name = f"{name}-{self.counter_name}"
|
||||
|
||||
def post_save_receiver_counter(sender, instance, **kwargs):
|
||||
self.increment(instance, 1)
|
||||
|
||||
post_save.connect(
|
||||
post_save_receiver_counter, sender=self.child_model, weak=False, dispatch_uid=f'{counted_name}_post_save'
|
||||
)
|
||||
|
||||
def post_delete_receiver_counter(sender, instance, **kwargs):
|
||||
self.increment(instance, 1)
|
||||
|
||||
post_delete.connect(
|
||||
post_delete_receiver_counter,
|
||||
sender=self.child_model,
|
||||
weak=False,
|
||||
dispatch_uid=f'{counted_name}_post_delete',
|
||||
)
|
||||
|
||||
counters[counted_name] = self
|
||||
|
||||
def parent_id(self, child):
|
||||
return getattr(child, self.foreign_key_field.attname)
|
||||
|
||||
def set_counter_field(self, parent_id, value):
|
||||
return self.parent_model.objects.filter(pk=parent_id).update(**{self.counter_name: value})
|
||||
|
||||
def increment(self, child, amount):
|
||||
parent_id = self.parent_id(child)
|
||||
return self.set_counter_field(parent_id, F(self.counter_name) + amount)
|
||||
|
||||
|
||||
def connect_counter(counter_name, foreign_key_field):
|
||||
return Counter(counter_name, foreign_key_field)
|
@ -143,3 +143,12 @@ class RestrictedGenericForeignKey(GenericForeignKey):
|
||||
self.name,
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
class CounterCacheField(models.BigIntegerField):
|
||||
"""
|
||||
Counter field to keep track of related model counts.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['default'] = kwargs.get('default', 0)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
Loading…
Reference in New Issue
Block a user