change_logging.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. from django.contrib.auth.models import User
  2. from django.contrib.contenttypes.fields import GenericForeignKey
  3. from django.contrib.contenttypes.models import ContentType
  4. from django.db import models
  5. from django.urls import reverse
  6. from extras.choices import *
  7. from ..querysets import ObjectChangeQuerySet
  8. __all__ = (
  9. 'ObjectChange',
  10. )
  11. class ObjectChange(models.Model):
  12. """
  13. Record a change to an object and the user account associated with that change. A change record may optionally
  14. indicate an object related to the one being changed. For example, a change to an interface may also indicate the
  15. parent device. This will ensure changes made to component models appear in the parent model's changelog.
  16. """
  17. time = models.DateTimeField(
  18. auto_now_add=True,
  19. editable=False,
  20. db_index=True
  21. )
  22. user = models.ForeignKey(
  23. to=User,
  24. on_delete=models.SET_NULL,
  25. related_name='changes',
  26. blank=True,
  27. null=True
  28. )
  29. user_name = models.CharField(
  30. max_length=150,
  31. editable=False
  32. )
  33. request_id = models.UUIDField(
  34. editable=False,
  35. db_index=True
  36. )
  37. action = models.CharField(
  38. max_length=50,
  39. choices=ObjectChangeActionChoices
  40. )
  41. changed_object_type = models.ForeignKey(
  42. to=ContentType,
  43. on_delete=models.PROTECT,
  44. related_name='+'
  45. )
  46. changed_object_id = models.PositiveBigIntegerField()
  47. changed_object = GenericForeignKey(
  48. ct_field='changed_object_type',
  49. fk_field='changed_object_id'
  50. )
  51. related_object_type = models.ForeignKey(
  52. to=ContentType,
  53. on_delete=models.PROTECT,
  54. related_name='+',
  55. blank=True,
  56. null=True
  57. )
  58. related_object_id = models.PositiveBigIntegerField(
  59. blank=True,
  60. null=True
  61. )
  62. related_object = GenericForeignKey(
  63. ct_field='related_object_type',
  64. fk_field='related_object_id'
  65. )
  66. object_repr = models.CharField(
  67. max_length=200,
  68. editable=False
  69. )
  70. prechange_data = models.JSONField(
  71. editable=False,
  72. blank=True,
  73. null=True
  74. )
  75. postchange_data = models.JSONField(
  76. editable=False,
  77. blank=True,
  78. null=True
  79. )
  80. objects = ObjectChangeQuerySet.as_manager()
  81. class Meta:
  82. ordering = ['-time']
  83. def __str__(self):
  84. return '{} {} {} by {}'.format(
  85. self.changed_object_type,
  86. self.object_repr,
  87. self.get_action_display().lower(),
  88. self.user_name
  89. )
  90. def save(self, *args, **kwargs):
  91. # Record the user's name and the object's representation as static strings
  92. if not self.user_name:
  93. self.user_name = self.user.username
  94. if not self.object_repr:
  95. self.object_repr = str(self.changed_object)
  96. return super().save(*args, **kwargs)
  97. def get_absolute_url(self):
  98. return reverse('extras:objectchange', args=[self.pk])
  99. def get_action_color(self):
  100. return ObjectChangeActionChoices.colors.get(self.action)