Repository: openwisp/openwisp-ipam (language: python)

Issue:
[bug] Creating subnet without name
It is possible to create subnet without a name through admin. 
Such subnets become inaccessible from the changelist view. 
We should add appropriate validation and update the `name` field of the Subnet model to not be blank. 

Originally reported by @Aryamanz29
[bug] Creating subnet without name
It is possible to create subnet without a name through admin. 
Such subnets become inaccessible from the changelist view. 
We should add appropriate validation and update the `name` field of the Subnet model to not be blank. 

Originally reported by @Aryamanz29


Discussion / hints:

Fix patch:
diff --git a/openwisp_ipam/base/models.py b/openwisp_ipam/base/models.py
index a466c7d..a86c59e 100644
--- a/openwisp_ipam/base/models.py
+++ b/openwisp_ipam/base/models.py
@@ -21,7 +21,7 @@ class CsvImportException(Exception):
 
 
 class AbstractSubnet(ShareableOrgMixin, TimeStampedEditableModel):
-    name = models.CharField(max_length=100, blank=True, db_index=True)
+    name = models.CharField(max_length=100, db_index=True)
     subnet = NetworkField(
         db_index=True,
         help_text=_(
@@ -44,9 +44,7 @@ class Meta:
         unique_together = ('subnet', 'organization')
 
     def __str__(self):
-        if self.name:
-            return f'{self.name} {self.subnet}'
-        return str(self.subnet)
+        return f'{self.name} {self.subnet}'
 
     def clean(self):
         if not self.subnet:
diff --git a/openwisp_ipam/migrations/0001_initial.py b/openwisp_ipam/migrations/0001_initial.py
index e2dee14..d84b593 100644
--- a/openwisp_ipam/migrations/0001_initial.py
+++ b/openwisp_ipam/migrations/0001_initial.py
@@ -97,7 +97,7 @@ class Migration(migrations.Migration):
                         verbose_name='modified',
                     ),
                 ),
-                ('name', models.CharField(blank=True, db_index=True, max_length=100)),
+                ('name', models.CharField(db_index=True, max_length=100)),
                 (
                     'subnet',
                     openwisp_ipam.base.fields.NetworkField(


Test patch:
diff --git a/openwisp_ipam/tests/__init__.py b/openwisp_ipam/tests/__init__.py
index 726b4e2..b4788a0 100644
--- a/openwisp_ipam/tests/__init__.py
+++ b/openwisp_ipam/tests/__init__.py
@@ -23,6 +23,7 @@ def _get_extra_fields(self, **kwargs):
 
     def _create_subnet(self, **kwargs):
         options = dict(
+            name='test subnet',
             subnet='',
             description='',
         )
diff --git a/openwisp_ipam/tests/test_api.py b/openwisp_ipam/tests/test_api.py
index b4da7bb..0538332 100644
--- a/openwisp_ipam/tests/test_api.py
+++ b/openwisp_ipam/tests/test_api.py
@@ -104,7 +104,9 @@ def test_unvailable_request_api(self):
         self.assertEqual(response.data, None)
 
     def test_create_subnet_api(self):
-        post_data = self._post_data(subnet='10.0.0.0/32', description='Testing')
+        post_data = self._post_data(
+            name='Subnet', subnet='10.0.0.0/32', description='Testing'
+        )
         response = self.client.post(
             reverse('ipam:subnet_list_create'),
             data=post_data,
diff --git a/openwisp_ipam/tests/test_models.py b/openwisp_ipam/tests/test_models.py
index 5083dbe..8ed7410 100644
--- a/openwisp_ipam/tests/test_models.py
+++ b/openwisp_ipam/tests/test_models.py
@@ -108,10 +108,6 @@ def test_unavailable_request_ip(self):
         ipaddr = subnet.request_ip()
         self.assertEqual(ipaddr, None)
 
-    def test_subnet_string_representation(self):
-        subnet = Subnet(subnet='entry subnet')
-        self.assertEqual(str(subnet), str(subnet.subnet))
-
     def test_subnet_string_representation_with_name(self):
         subnet = Subnet(subnet='entry subnet', name='test1')
         self.assertEqual(str(subnet), '{0} {1}'.format(subnet.name, str(subnet.subnet)))
@@ -313,6 +309,22 @@ def test_save_blank_subnet_fails(self):
         else:
             self.fail('ValidationError not raised')
 
+    def test_save_none_subnet_name_fails(self):
+        try:
+            self._create_subnet(name=None)
+        except ValidationError as err:
+            self.assertTrue(err.message_dict['name'] == ['This field cannot be null.'])
+        else:
+            self.fail('ValidationError not raised')
+
+    def test_save_blank_subnet_name_fails(self):
+        try:
+            self._create_subnet(name='')
+        except ValidationError as err:
+            self.assertTrue(err.message_dict['name'] == ['This field cannot be blank.'])
+        else:
+            self.fail('ValidationError not raised')
+
     def test_retrieves_ipv4_ipnetwork_type(self):
         instance = self._create_subnet(subnet='10.1.2.0/24')
         instance = Subnet.objects.get(pk=instance.pk)
diff --git a/openwisp_ipam/tests/test_multitenant.py b/openwisp_ipam/tests/test_multitenant.py
index 910c10d..9a56801 100644
--- a/openwisp_ipam/tests/test_multitenant.py
+++ b/openwisp_ipam/tests/test_multitenant.py
@@ -50,8 +50,8 @@ def test_multitenancy_subnet_queryset(self):
         data = self._create_multitenancy_test_env()
         self._test_multitenant_admin(
             url=reverse(f'admin:{self.app_label}_subnet_changelist'),
-            visible=[data['subnet1']],
-            hidden=[data['subnet2']],
+            visible=[data['subnet1'].subnet],
+            hidden=[data['subnet2'].subnet],
         )
 
     def test_import_subnet_permission(self):
diff --git a/tests/openwisp2/sample_ipam/migrations/0001_initial.py b/tests/openwisp2/sample_ipam/migrations/0001_initial.py
index ef519ee..f1bad3e 100644
--- a/tests/openwisp2/sample_ipam/migrations/0001_initial.py
+++ b/tests/openwisp2/sample_ipam/migrations/0001_initial.py
@@ -50,7 +50,7 @@ class Migration(migrations.Migration):
                         verbose_name='modified',
                     ),
                 ),
-                ('name', models.CharField(blank=True, db_index=True, max_length=100)),
+                ('name', models.CharField(db_index=True, max_length=100)),
                 (
                     'subnet',
                     openwisp_ipam.base.fields.NetworkField(