diff --git a/nsot/api/urls.py b/nsot/api/urls.py index eb85f4d..fa232dd 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/0030_site_parent.py b/nsot/migrations/0030_site_parent.py new file mode 100644 index 0000000..0ccab66 --- /dev/null +++ b/nsot/migrations/0030_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', '0029_auto__add_circuit'), + ] + + 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..e8bc696 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,25 @@ 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 validators.validate_parent(self.id, value) + def clean_fields(self, exclude=None): self.name = self.clean_name(self.name) + # 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] + 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 @@ -80,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