From d647edc140a80517b8ca0ae9abbf2733d056cf62 Mon Sep 17 00:00:00 2001 From: Rakib Hasan Date: Sat, 7 Jan 2017 20:20:35 -0500 Subject: [PATCH 1/4] added foreignkey for a site inside the site object, and added a validation check to prevent a site from being nested in itself. --- nsot/api/urls.py | 1 + nsot/migrations/0027_site_parent.py | 20 ++++++++++++++++++++ nsot/models.py | 14 ++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 nsot/migrations/0027_site_parent.py diff --git a/nsot/api/urls.py b/nsot/api/urls.py index eb85f4d..cfe412d 100644 --- a/nsot/api/urls.py +++ b/nsot/api/urls.py @@ -34,6 +34,7 @@ sites_router.register(r'interfaces', views.InterfaceViewSet) sites_router.register(r'networks', views.NetworkViewSet) sites_router.register(r'values', views.ValueViewSet) +sites_router.register(r'sites',views.SiteViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. diff --git a/nsot/migrations/0027_site_parent.py b/nsot/migrations/0027_site_parent.py new file mode 100644 index 0000000..584e46f --- /dev/null +++ b/nsot/migrations/0027_site_parent.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('nsot', '0026_model_field_verbose_names'), + ] + + operations = [ + migrations.AddField( + model_name='site', + name='parent', + field=models.ForeignKey(related_name='parent_site', on_delete=django.db.models.deletion.PROTECT, verbose_name='Parent site', to='nsot.Site', help_text='ID of a site that this site is nested in', null=True), + ), + ] diff --git a/nsot/models.py b/nsot/models.py index d8aa71c..a2c669a 100644 --- a/nsot/models.py +++ b/nsot/models.py @@ -61,6 +61,12 @@ class Site(models.Model): description = models.TextField( default='', blank=True, help_text='A helpful description for the Site.' ) + parent = models.ForeignKey( + 'self', db_index=True, related_name='parent_site', on_delete=models.PROTECT, + verbose_name='Parent site', + help_text='ID of a site that this site is nested in', + null=True + ) def __unicode__(self): return self.name @@ -68,8 +74,16 @@ def __unicode__(self): def clean_name(self, value): return validators.validate_name(value) + def clean_foreign_key(self, value): + if value != None and value.name == self.name: + raise exc.ValidationError({ + 'parent': 'A site cannot be nested in itself' + }) + return value + def clean_fields(self, exclude=None): self.name = self.clean_name(self.name) + self.site = self.clean_foreign_key(self.parent) def save(self, *args, **kwargs): self.full_clean() # First validate fields are correct From 9e5d8e72605123fbffb6e45d5d12d165a9a077b6 Mon Sep 17 00:00:00 2001 From: Rakib Hasan Date: Thu, 12 Jan 2017 00:22:13 -0500 Subject: [PATCH 2/4] Added a validator to check if a user is trying to set a parent to one of the site's children or grand children --- nsot/api/urls.py | 2 +- .../{0027_site_parent.py => 0029_site_parent.py} | 2 +- nsot/models.py | 16 +++++++++++++--- nsot/validators.py | 11 +++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) rename nsot/migrations/{0027_site_parent.py => 0029_site_parent.py} (90%) diff --git a/nsot/api/urls.py b/nsot/api/urls.py index cfe412d..fa232dd 100644 --- a/nsot/api/urls.py +++ b/nsot/api/urls.py @@ -34,7 +34,7 @@ sites_router.register(r'interfaces', views.InterfaceViewSet) sites_router.register(r'networks', views.NetworkViewSet) sites_router.register(r'values', views.ValueViewSet) -sites_router.register(r'sites',views.SiteViewSet) +sites_router.register(r'sites', views.SiteViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. diff --git a/nsot/migrations/0027_site_parent.py b/nsot/migrations/0029_site_parent.py similarity index 90% rename from nsot/migrations/0027_site_parent.py rename to nsot/migrations/0029_site_parent.py index 584e46f..9c90eef 100644 --- a/nsot/migrations/0027_site_parent.py +++ b/nsot/migrations/0029_site_parent.py @@ -8,7 +8,7 @@ class Migration(migrations.Migration): dependencies = [ - ('nsot', '0026_model_field_verbose_names'), + ('nsot', '0028_populate_interface_device_hostname'), ] operations = [ diff --git a/nsot/models.py b/nsot/models.py index a2c669a..99fe3b1 100644 --- a/nsot/models.py +++ b/nsot/models.py @@ -74,16 +74,25 @@ def __unicode__(self): def clean_name(self, value): return validators.validate_name(value) - def clean_foreign_key(self, value): + def clean_foreign_key(self, value): if value != None and value.name == self.name: raise exc.ValidationError({ 'parent': 'A site cannot be nested in itself' }) - return value + return validators.validate_parent(self.id, value) def clean_fields(self, exclude=None): self.name = self.clean_name(self.name) - self.site = self.clean_foreign_key(self.parent) + # if self.id is None then we are creating a site + if not self.id is None: + # check if we are changing name or description of site + s = Site.objects.filter(id=self.id)[0] + if s.name != self.name or s.description != self.description: + # we are not upating a site's parent + return + else: + # other wise we are updating a site's parent + self.parent = self.clean_foreign_key(self.parent) def save(self, *args, **kwargs): self.full_clean() # First validate fields are correct @@ -94,6 +103,7 @@ def to_dict(self): 'id': self.id, 'name': self.name, 'description': self.description, + 'parent': self.parent } diff --git a/nsot/validators.py b/nsot/validators.py index 053e6db..58519b7 100644 --- a/nsot/validators.py +++ b/nsot/validators.py @@ -69,3 +69,14 @@ def validate_email(value): 'email': err.message }) return value + + +def validate_parent(site_id, value): + v = value + while v != None: + if v.id == site_id: + raise exc.ValidationError({ + 'parent': 'Site with id %d is a child or grand child of site with id %d'%(value.id, site_id) + }) + v = v.parent + return value From 38863ca173ae2e78186371f901f288d4c3ab9888 Mon Sep 17 00:00:00 2001 From: Rakib Hasan Date: Sat, 14 Jan 2017 23:29:58 -0500 Subject: [PATCH 3/4] fixing a comment in Site model --- nsot/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nsot/models.py b/nsot/models.py index 99fe3b1..e8bc696 100644 --- a/nsot/models.py +++ b/nsot/models.py @@ -83,7 +83,7 @@ def clean_foreign_key(self, value): def clean_fields(self, exclude=None): self.name = self.clean_name(self.name) - # if self.id is None then we are creating a site + # if self.id is not None then we are creating a nested site if not self.id is None: # check if we are changing name or description of site s = Site.objects.filter(id=self.id)[0] From a73de6f266862bae860534cde00c965fab0ef9ab Mon Sep 17 00:00:00 2001 From: Rakib Hasan Date: Wed, 25 Jan 2017 23:55:14 -0500 Subject: [PATCH 4/4] added new migration nested sites --- nsot/migrations/{0029_site_parent.py => 0030_site_parent.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename nsot/migrations/{0029_site_parent.py => 0030_site_parent.py} (90%) diff --git a/nsot/migrations/0029_site_parent.py b/nsot/migrations/0030_site_parent.py similarity index 90% rename from nsot/migrations/0029_site_parent.py rename to nsot/migrations/0030_site_parent.py index 9c90eef..0ccab66 100644 --- a/nsot/migrations/0029_site_parent.py +++ b/nsot/migrations/0030_site_parent.py @@ -8,7 +8,7 @@ class Migration(migrations.Migration): dependencies = [ - ('nsot', '0028_populate_interface_device_hostname'), + ('nsot', '0029_auto__add_circuit'), ] operations = [