From 0969c458b377b07396af6d3a8d6c6eb6ee9f79f9 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 30 Mar 2018 10:39:22 -0400 Subject: [PATCH] Closes #1842: Implement support for Django 2.0 --- netbox/dcim/migrations/0056_django2.py | 24 +++++++++++++++ netbox/dcim/models.py | 2 ++ netbox/extras/migrations/0011_django2.py | 29 +++++++++++++++++++ netbox/extras/tests/test_customfields.py | 16 +++++----- netbox/netbox/settings.py | 1 - netbox/secrets/api/views.py | 2 +- netbox/utilities/api.py | 2 +- netbox/utilities/forms.py | 2 +- netbox/utilities/middleware.py | 2 +- .../virtualization/migrations/0005_django2.py | 19 ++++++++++++ requirements.txt | 2 +- 11 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 netbox/dcim/migrations/0056_django2.py create mode 100644 netbox/extras/migrations/0011_django2.py create mode 100644 netbox/virtualization/migrations/0005_django2.py diff --git a/netbox/dcim/migrations/0056_django2.py b/netbox/dcim/migrations/0056_django2.py new file mode 100644 index 000000000..bb7af920e --- /dev/null +++ b/netbox/dcim/migrations/0056_django2.py @@ -0,0 +1,24 @@ +# Generated by Django 2.0.3 on 2018-03-30 14:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('dcim', '0055_virtualchassis_ordering'), + ] + + operations = [ + migrations.AlterField( + model_name='interface', + name='untagged_vlan', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='interfaces_as_untagged', to='ipam.VLAN', verbose_name='Untagged VLAN'), + ), + migrations.AlterField( + model_name='platform', + name='manufacturer', + field=models.ForeignKey(blank=True, help_text='Optionally limit this platform to devices of a certain manufacturer', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='platforms', to='dcim.Manufacturer'), + ), + ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index ac1affdef..1bf15a411 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -804,6 +804,7 @@ class Platform(models.Model): slug = models.SlugField(unique=True) manufacturer = models.ForeignKey( to='Manufacturer', + on_delete=models.PROTECT, related_name='platforms', blank=True, null=True, @@ -1373,6 +1374,7 @@ class Interface(models.Model): ) untagged_vlan = models.ForeignKey( to='ipam.VLAN', + on_delete=models.SET_NULL, null=True, blank=True, verbose_name='Untagged VLAN', diff --git a/netbox/extras/migrations/0011_django2.py b/netbox/extras/migrations/0011_django2.py new file mode 100644 index 000000000..f8e0954d6 --- /dev/null +++ b/netbox/extras/migrations/0011_django2.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.3 on 2018-03-30 14:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0010_customfield_filter_logic'), + ] + + operations = [ + migrations.AlterField( + model_name='customfield', + name='obj_type', + field=models.ManyToManyField(help_text='The object(s) to which this field applies.', limit_choices_to={'model__in': ('provider', 'circuit', 'site', 'rack', 'devicetype', 'device', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'tenant', 'cluster', 'virtualmachine')}, related_name='custom_fields', to='contenttypes.ContentType', verbose_name='Object(s)'), + ), + migrations.AlterField( + model_name='customfieldchoice', + name='field', + field=models.ForeignKey(limit_choices_to={'type': 600}, on_delete=django.db.models.deletion.CASCADE, related_name='choices', to='extras.CustomField'), + ), + migrations.AlterField( + model_name='exporttemplate', + name='content_type', + field=models.ForeignKey(limit_choices_to={'model__in': ['provider', 'circuit', 'site', 'region', 'rack', 'rackgroup', 'manufacturer', 'devicetype', 'device', 'consoleport', 'powerport', 'interfaceconnection', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'tenant', 'cluster', 'virtualmachine']}, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'), + ), + ] diff --git a/netbox/extras/tests/test_customfields.py b/netbox/extras/tests/test_customfields.py index 84aaa76b2..b10db514e 100644 --- a/netbox/extras/tests/test_customfields.py +++ b/netbox/extras/tests/test_customfields.py @@ -45,7 +45,7 @@ class CustomFieldTest(TestCase): # Create a custom field cf = CustomField(type=data['field_type'], name='my_field', required=False) cf.save() - cf.obj_type = [obj_type] + cf.obj_type.set([obj_type]) cf.save() # Assign a value to the first Site @@ -73,7 +73,7 @@ class CustomFieldTest(TestCase): # Create a custom field cf = CustomField(type=CF_TYPE_SELECT, name='my_field', required=False) cf.save() - cf.obj_type = [obj_type] + cf.obj_type.set([obj_type]) cf.save() # Create some choices for the field @@ -115,37 +115,37 @@ class CustomFieldAPITest(HttpStatusMixin, APITestCase): # Text custom field self.cf_text = CustomField(type=CF_TYPE_TEXT, name='magic_word') self.cf_text.save() - self.cf_text.obj_type = [content_type] + self.cf_text.obj_type.set([content_type]) self.cf_text.save() # Integer custom field self.cf_integer = CustomField(type=CF_TYPE_INTEGER, name='magic_number') self.cf_integer.save() - self.cf_integer.obj_type = [content_type] + self.cf_integer.obj_type.set([content_type]) self.cf_integer.save() # Boolean custom field self.cf_boolean = CustomField(type=CF_TYPE_BOOLEAN, name='is_magic') self.cf_boolean.save() - self.cf_boolean.obj_type = [content_type] + self.cf_boolean.obj_type.set([content_type]) self.cf_boolean.save() # Date custom field self.cf_date = CustomField(type=CF_TYPE_DATE, name='magic_date') self.cf_date.save() - self.cf_date.obj_type = [content_type] + self.cf_date.obj_type.set([content_type]) self.cf_date.save() # URL custom field self.cf_url = CustomField(type=CF_TYPE_URL, name='magic_url') self.cf_url.save() - self.cf_url.obj_type = [content_type] + self.cf_url.obj_type.set([content_type]) self.cf_url.save() # Select custom field self.cf_select = CustomField(type=CF_TYPE_SELECT, name='magic_choice') self.cf_select.save() - self.cf_select.obj_type = [content_type] + self.cf_select.obj_type.set([content_type]) self.cf_select.save() self.cf_select_choice1 = CustomFieldChoice(field=self.cf_select, value='Foo') self.cf_select_choice1.save() diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index e40106a21..8abc92e23 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -154,7 +154,6 @@ MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', diff --git a/netbox/secrets/api/views.py b/netbox/secrets/api/views.py index d2fb2ef00..807a87b42 100644 --- a/netbox/secrets/api/views.py +++ b/netbox/secrets/api/views.py @@ -68,7 +68,7 @@ class SecretViewSet(ModelViewSet): super(SecretViewSet, self).initial(request, *args, **kwargs) - if request.user.is_authenticated(): + if request.user.is_authenticated: # Read session key from HTTP cookie or header if it has been provided. The session key must be provided in # order to encrypt/decrypt secrets. diff --git a/netbox/utilities/api.py b/netbox/utilities/api.py index 5c78dacc4..c54379dff 100644 --- a/netbox/utilities/api.py +++ b/netbox/utilities/api.py @@ -33,7 +33,7 @@ class IsAuthenticatedOrLoginNotRequired(BasePermission): def has_permission(self, request, view): if not settings.LOGIN_REQUIRED: return True - return request.user.is_authenticated() + return request.user.is_authenticated # diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index 15fb69f7f..d64af0105 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -325,7 +325,7 @@ class CSVChoiceField(forms.ChoiceField): """ def __init__(self, choices, *args, **kwargs): - super(CSVChoiceField, self).__init__(choices, *args, **kwargs) + super(CSVChoiceField, self).__init__(choices=choices, *args, **kwargs) self.choices = [(label, label) for value, label in choices] self.choice_values = {label: value for value, label in choices} diff --git a/netbox/utilities/middleware.py b/netbox/utilities/middleware.py index 64fb70a07..47fa48c90 100644 --- a/netbox/utilities/middleware.py +++ b/netbox/utilities/middleware.py @@ -20,7 +20,7 @@ class LoginRequiredMiddleware(object): self.get_response = get_response def __call__(self, request): - if LOGIN_REQUIRED and not request.user.is_authenticated(): + if LOGIN_REQUIRED and not request.user.is_authenticated: # Redirect unauthenticated requests to the login page. API requests are exempt from redirection as the API # performs its own authentication. api_path = reverse('api-root') diff --git a/netbox/virtualization/migrations/0005_django2.py b/netbox/virtualization/migrations/0005_django2.py new file mode 100644 index 000000000..e79a55350 --- /dev/null +++ b/netbox/virtualization/migrations/0005_django2.py @@ -0,0 +1,19 @@ +# Generated by Django 2.0.3 on 2018-03-30 14:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('virtualization', '0004_virtualmachine_add_role'), + ] + + operations = [ + migrations.AlterField( + model_name='virtualmachine', + name='role', + field=models.ForeignKey(blank=True, limit_choices_to={'vm_role': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='dcim.DeviceRole'), + ), + ] diff --git a/requirements.txt b/requirements.txt index 5b7b3e73e..d6b63b1bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django>=1.11,<2.0 +Django>=2.0.3 django-cors-headers>=2.1.0 django-debug-toolbar>=1.9.0 django-filter>=1.1.0