diff --git a/conntrackt/models.py b/conntrackt/models.py
--- a/conntrackt/models.py
+++ b/conntrackt/models.py
@@ -20,10 +20,13 @@
# Django imports.
+from django.contrib.admin.util import NestedObjects
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.query_utils import Q
+from django.utils.html import format_html
+from django.utils.text import capfirst
class SearchManager(models.Manager):
@@ -47,7 +50,94 @@ class SearchManager(models.Manager):
return self.filter(Q(name__icontains=search_term) | Q(description__icontains=search_term))
-class Project(models.Model):
+class RelatedCollectorMixin(object):
+ """
+ Implements model mixin for easily obtainning related items of a model
+ instance.
+
+ The mixin can be used for obtaining all model objects that are directly or
+ indirectly linked to the calling model object (through foreign key
+ relationships).
+
+ This mixin is very useful in delete views for warning the user about all of
+ the related items that will be deleted if the calling item is deleted as
+ well.
+ """
+
+ def get_dependant_objects(self):
+ """
+ Creates a list of model objects that depend (reference), both directly
+ and indirectly, on calling model object. The calling model object is
+ included as the first element of the list. This method call can be used
+ in order to obtain a list of model objects that would get deleted in
+ case the calling model object gets deleted.
+
+ Returns:
+ Nested list of model objects that depend (reference) calling model
+ object.
+ """
+
+ collector = NestedObjects(using='default')
+
+ collector.collect([self])
+
+ return collector.nested()
+
+ def get_dependant_objects_representation(self):
+ """
+ Creates a nested list of object representations that depend (reference),
+ both directly and indirectly, calling model object. This method call can
+ be used in order to obtain a list of string representations of model
+ objects that would get deleted in case the calling model object gets
+ deleted.
+
+ The resulting nested list can be shown to the user for
+ warning/notification purposes using the unordered_list template tag.
+
+ Each non-list element will be a string of format:
+
+ MODEL_NAME: OBJECT_REPRESENTATION
+
+ If object has a callable get_absolute_url method, the object
+ representation will be surrouned by HTML anchor tag () where
+ target (href) is set to the value of get_absolute_url() method call.
+
+ Returns:
+ Nested list of representations of model objects that depend
+ (reference) calling model object.
+ """
+
+ collector = NestedObjects(using='default')
+
+ collector.collect([self])
+
+ def formatter_callback(obj):
+ """
+ Creates model object representation in format:
+
+ MODEL_NAME: OBJECT_REPRESENTATION
+
+ If passed object has a callable get_absolute_url method, the
+ instance representation will be surrouned by an HTML anchor
+ () where target is set to value of the get_absolute_url()
+ method call.
+
+ Arguments:
+ obj - Model object whose representation should be returned.
+
+ Returns:
+ String represenation of passed model object.
+ """
+
+ try:
+ return format_html('{0}: {2}', capfirst(obj._meta.verbose_name), obj.get_absolute_url(), str(obj))
+ except AttributeError:
+ return format_html('{0}: {1}', capfirst(obj._meta.verbose_name), str(obj))
+
+ return collector.nested(formatter_callback)
+
+
+class Project(RelatedCollectorMixin, models.Model):
"""
Implements a model with information about a project. A project has some
basic settings, and mainly serves the purpose of grouping entities for
@@ -62,6 +152,7 @@ class Project(models.Model):
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True)
objects = SearchManager()
+ deletion_collect_models = ["Entity", "Interface"]
class Meta:
permissions = (("view", "Can view information"),)
@@ -82,7 +173,7 @@ class Project(models.Model):
return reverse("project", kwargs={'pk': self.pk})
-class Location(models.Model):
+class Location(RelatedCollectorMixin, models.Model):
"""
Implements a model with information about location. Locations can further be
assigned to entities, letting the user group different servers and equipment
@@ -118,7 +209,7 @@ class Location(models.Model):
return self.name
-class Entity(models.Model):
+class Entity(RelatedCollectorMixin, models.Model):
"""
Models an entity in a project. An entity can be a server, router, or any
other piece of networking equipment that has its own IP address.
@@ -215,7 +306,7 @@ class Entity(models.Model):
raise ValidationError("The entity cannot be moved to different project as long as it has valid communications with entities in current project.")
-class Interface(models.Model):
+class Interface(RelatedCollectorMixin, models.Model):
"""
Models a representation of an interface on an entity. It can be used for
representing the subnets as well.
@@ -261,7 +352,7 @@ class Interface(models.Model):
return '%s (%s/%s)' % (self.entity.name, self.address, self.netmask)
-class Communication(models.Model):
+class Communication(RelatedCollectorMixin, models.Model):
"""
Models a representation of allowed network communication. This lets the user
display the possible network connections that should be allowed. Information