Skip to content

Commit

Permalink
Move abstract ChangeLoggedModel under extras
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Jul 9, 2020
1 parent c3a7939 commit 8d7001f
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 172 deletions.
3 changes: 1 addition & 2 deletions netbox/circuits/models.py
Expand Up @@ -6,10 +6,9 @@
from dcim.constants import CONNECTION_STATUS_CHOICES
from dcim.fields import ASNField
from dcim.models import CableTermination
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from extras.models import ChangeLoggedModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.querysets import RestrictedQuerySet
from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object
from .choices import *
from .querysets import CircuitQuerySet
Expand Down
3 changes: 1 addition & 2 deletions netbox/dcim/models/__init__.py
Expand Up @@ -21,12 +21,11 @@
from dcim.constants import *
from dcim.fields import ASNField
from dcim.elevations import RackElevationSVG
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.models import ChangeLoggedModel, ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.choices import ColorChoices
from utilities.fields import ColorField, NaturalOrderingField
from utilities.querysets import RestrictedQuerySet
from utilities.models import ChangeLoggedModel
from utilities.mptt import TreeManager
from utilities.utils import serialize_object, to_meters
from utilities.validators import ExclusionValidator
Expand Down
6 changes: 4 additions & 2 deletions netbox/extras/models/__init__.py
@@ -1,11 +1,13 @@
from .change_logging import ChangeLoggedModel, ObjectChange
from .customfields import CustomField, CustomFieldChoice, CustomFieldModel, CustomFieldValue
from .models import (
ConfigContext, ConfigContextModel, CustomLink, ExportTemplate, Graph, ImageAttachment, JobResult, ObjectChange,
Report, Script, Webhook,
ConfigContext, ConfigContextModel, CustomLink, ExportTemplate, Graph, ImageAttachment, JobResult, Report, Script,
Webhook,
)
from .tags import Tag, TaggedItem

__all__ = (
'ChangeLoggedModel',
'ConfigContext',
'ConfigContextModel',
'CustomField',
Expand Down
155 changes: 155 additions & 0 deletions netbox/extras/models/change_logging.py
@@ -0,0 +1,155 @@
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.urls import reverse

from utilities.querysets import RestrictedQuerySet
from utilities.utils import serialize_object
from extras.choices import *


#
# Change logging
#

class ChangeLoggedModel(models.Model):
"""
An abstract model which adds fields to store the creation and last-updated times for an object. Both fields can be
null to facilitate adding these fields to existing instances via a database migration.
"""
created = models.DateField(
auto_now_add=True,
blank=True,
null=True
)
last_updated = models.DateTimeField(
auto_now=True,
blank=True,
null=True
)

class Meta:
abstract = True

def to_objectchange(self, action):
"""
Return a new ObjectChange representing a change made to this object. This will typically be called automatically
by extras.middleware.ChangeLoggingMiddleware.
"""
return ObjectChange(
changed_object=self,
object_repr=str(self),
action=action,
object_data=serialize_object(self)
)


class ObjectChange(models.Model):
"""
Record a change to an object and the user account associated with that change. A change record may optionally
indicate an object related to the one being changed. For example, a change to an interface may also indicate the
parent device. This will ensure changes made to component models appear in the parent model's changelog.
"""
time = models.DateTimeField(
auto_now_add=True,
editable=False,
db_index=True
)
user = models.ForeignKey(
to=User,
on_delete=models.SET_NULL,
related_name='changes',
blank=True,
null=True
)
user_name = models.CharField(
max_length=150,
editable=False
)
request_id = models.UUIDField(
editable=False
)
action = models.CharField(
max_length=50,
choices=ObjectChangeActionChoices
)
changed_object_type = models.ForeignKey(
to=ContentType,
on_delete=models.PROTECT,
related_name='+'
)
changed_object_id = models.PositiveIntegerField()
changed_object = GenericForeignKey(
ct_field='changed_object_type',
fk_field='changed_object_id'
)
related_object_type = models.ForeignKey(
to=ContentType,
on_delete=models.PROTECT,
related_name='+',
blank=True,
null=True
)
related_object_id = models.PositiveIntegerField(
blank=True,
null=True
)
related_object = GenericForeignKey(
ct_field='related_object_type',
fk_field='related_object_id'
)
object_repr = models.CharField(
max_length=200,
editable=False
)
object_data = JSONField(
editable=False
)

objects = RestrictedQuerySet.as_manager()

csv_headers = [
'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
'related_object_type', 'related_object_id', 'object_repr', 'object_data',
]

class Meta:
ordering = ['-time']

def __str__(self):
return '{} {} {} by {}'.format(
self.changed_object_type,
self.object_repr,
self.get_action_display().lower(),
self.user_name
)

def save(self, *args, **kwargs):

# Record the user's name and the object's representation as static strings
if not self.user_name:
self.user_name = self.user.username
if not self.object_repr:
self.object_repr = str(self.changed_object)

return super().save(*args, **kwargs)

def get_absolute_url(self):
return reverse('extras:objectchange', args=[self.pk])

def to_csv(self):
return (
self.time,
self.user,
self.user_name,
self.request_id,
self.get_action_display(),
self.changed_object_type,
self.changed_object_id,
self.related_object_type,
self.related_object_id,
self.object_repr,
self.object_data,
)
119 changes: 3 additions & 116 deletions netbox/extras/models/models.py
Expand Up @@ -2,7 +2,6 @@
import uuid
from collections import OrderedDict

import django_rq
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
Expand All @@ -15,12 +14,13 @@
from django.utils import timezone
from rest_framework.utils.encoders import JSONEncoder

from utilities.querysets import RestrictedQuerySet
from utilities.utils import deepmerge, render_jinja2
from extras.choices import *
from extras.constants import *
from extras.models import ChangeLoggedModel
from extras.querysets import ConfigContextQuerySet
from extras.utils import extras_features, FeatureQuery, image_upload
from utilities.querysets import RestrictedQuerySet
from utilities.utils import deepmerge, render_jinja2


#
Expand Down Expand Up @@ -684,116 +684,3 @@ def enqueue_job(cls, func, name, obj_type, user, *args, **kwargs):
func.delay(*args, job_id=str(job_result.job_id), job_result=job_result, **kwargs)

return job_result


#
# Change logging
#

class ObjectChange(models.Model):
"""
Record a change to an object and the user account associated with that change. A change record may optionally
indicate an object related to the one being changed. For example, a change to an interface may also indicate the
parent device. This will ensure changes made to component models appear in the parent model's changelog.
"""
time = models.DateTimeField(
auto_now_add=True,
editable=False,
db_index=True
)
user = models.ForeignKey(
to=User,
on_delete=models.SET_NULL,
related_name='changes',
blank=True,
null=True
)
user_name = models.CharField(
max_length=150,
editable=False
)
request_id = models.UUIDField(
editable=False
)
action = models.CharField(
max_length=50,
choices=ObjectChangeActionChoices
)
changed_object_type = models.ForeignKey(
to=ContentType,
on_delete=models.PROTECT,
related_name='+'
)
changed_object_id = models.PositiveIntegerField()
changed_object = GenericForeignKey(
ct_field='changed_object_type',
fk_field='changed_object_id'
)
related_object_type = models.ForeignKey(
to=ContentType,
on_delete=models.PROTECT,
related_name='+',
blank=True,
null=True
)
related_object_id = models.PositiveIntegerField(
blank=True,
null=True
)
related_object = GenericForeignKey(
ct_field='related_object_type',
fk_field='related_object_id'
)
object_repr = models.CharField(
max_length=200,
editable=False
)
object_data = JSONField(
editable=False
)

objects = RestrictedQuerySet.as_manager()

csv_headers = [
'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
'related_object_type', 'related_object_id', 'object_repr', 'object_data',
]

class Meta:
ordering = ['-time']

def __str__(self):
return '{} {} {} by {}'.format(
self.changed_object_type,
self.object_repr,
self.get_action_display().lower(),
self.user_name
)

def save(self, *args, **kwargs):

# Record the user's name and the object's representation as static strings
if not self.user_name:
self.user_name = self.user.username
if not self.object_repr:
self.object_repr = str(self.changed_object)

return super().save(*args, **kwargs)

def get_absolute_url(self):
return reverse('extras:objectchange', args=[self.pk])

def to_csv(self):
return (
self.time,
self.user,
self.user_name,
self.request_id,
self.get_action_display(),
self.changed_object_type,
self.changed_object_id,
self.related_object_type,
self.related_object_id,
self.object_repr,
self.object_data,
)
2 changes: 1 addition & 1 deletion netbox/extras/models/tags.py
Expand Up @@ -2,9 +2,9 @@
from django.utils.text import slugify
from taggit.models import TagBase, GenericTaggedItemBase

from extras.models import ChangeLoggedModel
from utilities.choices import ColorChoices
from utilities.fields import ColorField
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet


Expand Down
3 changes: 1 addition & 2 deletions netbox/ipam/models.py
Expand Up @@ -10,9 +10,8 @@
from taggit.managers import TaggableManager

from dcim.models import Device, Interface
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from extras.models import ChangeLoggedModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet
from utilities.utils import serialize_object
from virtualization.models import VirtualMachine, VMInterface
Expand Down
3 changes: 1 addition & 2 deletions netbox/secrets/models.py
Expand Up @@ -14,9 +14,8 @@
from taggit.managers import TaggableManager

from dcim.models import Device
from extras.models import CustomFieldModel, TaggedItem
from extras.models import ChangeLoggedModel, CustomFieldModel, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet
from .exceptions import InvalidKey
from .hashers import SecretValidationHasher
Expand Down
3 changes: 1 addition & 2 deletions netbox/tenancy/models.py
Expand Up @@ -4,9 +4,8 @@
from mptt.models import MPTTModel, TreeForeignKey
from taggit.managers import TaggableManager

from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from extras.models import ChangeLoggedModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.mptt import TreeManager
from utilities.querysets import RestrictedQuerySet
from utilities.utils import serialize_object
Expand Down

0 comments on commit 8d7001f

Please sign in to comment.