Explorar el Código

Rename CustomField.content_types to object_types & use ObjectType proxy

Jeremy Stretch hace 2 años
padre
commit
aeeec284a5

+ 3 - 3
netbox/dcim/tests/test_models.py

@@ -1,8 +1,8 @@
-from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.test import TestCase
 from django.test import TestCase
 
 
 from circuits.models import *
 from circuits.models import *
+from core.models import ObjectType
 from dcim.choices import *
 from dcim.choices import *
 from dcim.models import *
 from dcim.models import *
 from extras.models import CustomField
 from extras.models import CustomField
@@ -293,8 +293,8 @@ class DeviceTestCase(TestCase):
 
 
         # Create a CustomField with a default value & assign it to all component models
         # Create a CustomField with a default value & assign it to all component models
         cf1 = CustomField.objects.create(name='cf1', default='foo')
         cf1 = CustomField.objects.create(name='cf1', default='foo')
-        cf1.content_types.set(
-            ContentType.objects.filter(app_label='dcim', model__in=[
+        cf1.object_types.set(
+            ObjectType.objects.filter(app_label='dcim', model__in=[
                 'consoleport',
                 'consoleport',
                 'consoleserverport',
                 'consoleserverport',
                 'powerport',
                 'powerport',

+ 2 - 2
netbox/extras/api/customfields.py

@@ -26,7 +26,7 @@ class CustomFieldDefaultValues:
 
 
         # Retrieve the CustomFields for the parent model
         # Retrieve the CustomFields for the parent model
         content_type = ContentType.objects.get_for_model(self.model)
         content_type = ContentType.objects.get_for_model(self.model)
-        fields = CustomField.objects.filter(content_types=content_type)
+        fields = CustomField.objects.filter(object_types=content_type)
 
 
         # Populate the default value for each CustomField
         # Populate the default value for each CustomField
         value = {}
         value = {}
@@ -48,7 +48,7 @@ class CustomFieldsDataField(Field):
         """
         """
         if not hasattr(self, '_custom_fields'):
         if not hasattr(self, '_custom_fields'):
             content_type = ContentType.objects.get_for_model(self.parent.Meta.model)
             content_type = ContentType.objects.get_for_model(self.parent.Meta.model)
-            self._custom_fields = CustomField.objects.filter(content_types=content_type)
+            self._custom_fields = CustomField.objects.filter(object_types=content_type)
         return self._custom_fields
         return self._custom_fields
 
 
     def to_representation(self, obj):
     def to_representation(self, obj):

+ 2 - 2
netbox/extras/api/serializers.py

@@ -117,7 +117,7 @@ class WebhookSerializer(NetBoxModelSerializer):
 
 
 class CustomFieldSerializer(ValidatedModelSerializer):
 class CustomFieldSerializer(ValidatedModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
     url = serializers.HyperlinkedIdentityField(view_name='extras-api:customfield-detail')
-    content_types = ContentTypeField(
+    object_types = ContentTypeField(
         queryset=ObjectType.objects.with_feature('custom_fields'),
         queryset=ObjectType.objects.with_feature('custom_fields'),
         many=True
         many=True
     )
     )
@@ -139,7 +139,7 @@ class CustomFieldSerializer(ValidatedModelSerializer):
     class Meta:
     class Meta:
         model = CustomField
         model = CustomField
         fields = [
         fields = [
-            'id', 'url', 'display', 'content_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
+            'id', 'url', 'display', 'object_types', 'type', 'object_type', 'data_type', 'name', 'label', 'group_name',
             'description', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable',
             'description', 'required', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable',
             'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set',
             'default', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', 'choice_set',
             'created', 'last_updated',
             'created', 'last_updated',

+ 6 - 4
netbox/extras/filtersets.py

@@ -124,10 +124,12 @@ class CustomFieldFilterSet(BaseFilterSet):
     type = django_filters.MultipleChoiceFilter(
     type = django_filters.MultipleChoiceFilter(
         choices=CustomFieldTypeChoices
         choices=CustomFieldTypeChoices
     )
     )
-    content_type_id = MultiValueNumberFilter(
-        field_name='content_types__id'
+    object_types_id = MultiValueNumberFilter(
+        field_name='object_types__id'
+    )
+    object_types = ContentTypeFilter(
+        field_name='object_types'
     )
     )
-    content_types = ContentTypeFilter()
     choice_set_id = django_filters.ModelMultipleChoiceFilter(
     choice_set_id = django_filters.ModelMultipleChoiceFilter(
         queryset=CustomFieldChoiceSet.objects.all()
         queryset=CustomFieldChoiceSet.objects.all()
     )
     )
@@ -140,7 +142,7 @@ class CustomFieldFilterSet(BaseFilterSet):
     class Meta:
     class Meta:
         model = CustomField
         model = CustomField
         fields = [
         fields = [
-            'id', 'content_types', 'name', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible',
+            'id', 'object_types', 'name', 'group_name', 'required', 'search_weight', 'filter_logic', 'ui_visible',
             'ui_editable', 'weight', 'is_cloneable', 'description',
             'ui_editable', 'weight', 'is_cloneable', 'description',
         ]
         ]
 
 

+ 3 - 3
netbox/extras/forms/bulk_import.py

@@ -30,8 +30,8 @@ __all__ = (
 
 
 
 
 class CustomFieldImportForm(CSVModelForm):
 class CustomFieldImportForm(CSVModelForm):
-    content_types = CSVMultipleContentTypeField(
-        label=_('Content types'),
+    object_types = CSVMultipleContentTypeField(
+        label=_('Object types'),
         queryset=ObjectType.objects.with_feature('custom_fields'),
         queryset=ObjectType.objects.with_feature('custom_fields'),
         help_text=_("One or more assigned object types")
         help_text=_("One or more assigned object types")
     )
     )
@@ -69,7 +69,7 @@ class CustomFieldImportForm(CSVModelForm):
     class Meta:
     class Meta:
         model = CustomField
         model = CustomField
         fields = (
         fields = (
-            'name', 'label', 'group_name', 'type', 'content_types', 'object_type', 'required', 'description',
+            'name', 'label', 'group_name', 'type', 'object_types', 'object_type', 'required', 'description',
             'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
             'search_weight', 'filter_logic', 'default', 'choice_set', 'weight', 'validation_minimum',
             'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable',
             'validation_maximum', 'validation_regex', 'ui_visible', 'ui_editable', 'is_cloneable',
         )
         )

+ 2 - 2
netbox/extras/forms/filtersets.py

@@ -38,11 +38,11 @@ class CustomFieldFilterForm(SavedFiltersMixin, FilterForm):
     fieldsets = (
     fieldsets = (
         (None, ('q', 'filter_id')),
         (None, ('q', 'filter_id')),
         (_('Attributes'), (
         (_('Attributes'), (
-            'type', 'content_type_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible', 'ui_editable',
+            'type', 'object_types_id', 'group_name', 'weight', 'required', 'choice_set_id', 'ui_visible', 'ui_editable',
             'is_cloneable',
             'is_cloneable',
         )),
         )),
     )
     )
-    content_type_id = ContentTypeMultipleChoiceField(
+    object_types_id = ContentTypeMultipleChoiceField(
         queryset=ObjectType.objects.with_feature('custom_fields'),
         queryset=ObjectType.objects.with_feature('custom_fields'),
         required=False,
         required=False,
         label=_('Object type')
         label=_('Object type')

+ 3 - 4
netbox/extras/forms/model_forms.py

@@ -2,7 +2,6 @@ import json
 import re
 import re
 
 
 from django import forms
 from django import forms
-from django.contrib.contenttypes.models import ContentType
 from django.utils.safestring import mark_safe
 from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 
 
@@ -39,8 +38,8 @@ __all__ = (
 
 
 
 
 class CustomFieldForm(forms.ModelForm):
 class CustomFieldForm(forms.ModelForm):
-    content_types = ContentTypeMultipleChoiceField(
-        label=_('Content types'),
+    object_types = ContentTypeMultipleChoiceField(
+        label=_('Object types'),
         queryset=ObjectType.objects.with_feature('custom_fields')
         queryset=ObjectType.objects.with_feature('custom_fields')
     )
     )
     object_type = ContentTypeChoiceField(
     object_type = ContentTypeChoiceField(
@@ -56,7 +55,7 @@ class CustomFieldForm(forms.ModelForm):
 
 
     fieldsets = (
     fieldsets = (
         (_('Custom Field'), (
         (_('Custom Field'), (
-            'content_types', 'name', 'label', 'group_name', 'type', 'object_type', 'required', 'description',
+            'object_types', 'name', 'label', 'group_name', 'type', 'object_type', 'required', 'description',
         )),
         )),
         (_('Behavior'), ('search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable')),
         (_('Behavior'), ('search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'weight', 'is_cloneable')),
         (_('Values'), ('default', 'choice_set')),
         (_('Values'), ('default', 'choice_set')),

+ 1 - 1
netbox/extras/graphql/types.py

@@ -39,7 +39,7 @@ class CustomFieldType(ObjectType):
 
 
     class Meta:
     class Meta:
         model = models.CustomField
         model = models.CustomField
-        exclude = ('content_types', )
+        exclude = ('object_types', 'object_type')
         filterset_class = filtersets.CustomFieldFilterSet
         filterset_class = filtersets.CustomFieldFilterSet
 
 
 
 

+ 28 - 0
netbox/extras/migrations/0111_rename_content_types.py

@@ -0,0 +1,28 @@
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0010_gfk_indexes'),
+        ('extras', '0110_remove_eventrule_action_parameters'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='customfield',
+            old_name='content_types',
+            new_name='object_types',
+        ),
+        migrations.AlterField(
+            model_name='customfield',
+            name='object_types',
+            field=models.ManyToManyField(related_name='custom_fields', to='core.objecttype'),
+        ),
+        migrations.AlterField(
+            model_name='customfield',
+            name='object_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype'),
+        ),
+    ]

+ 5 - 5
netbox/extras/models/customfields.py

@@ -53,7 +53,7 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
         Return all CustomFields assigned to the given model.
         Return all CustomFields assigned to the given model.
         """
         """
         content_type = ObjectType.objects.get_for_model(model._meta.concrete_model)
         content_type = ObjectType.objects.get_for_model(model._meta.concrete_model)
-        return self.get_queryset().filter(content_types=content_type)
+        return self.get_queryset().filter(object_types=content_type)
 
 
     def get_defaults_for_model(self, model):
     def get_defaults_for_model(self, model):
         """
         """
@@ -66,8 +66,8 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
 
 
 
 
 class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
 class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
-    content_types = models.ManyToManyField(
-        to='contenttypes.ContentType',
+    object_types = models.ManyToManyField(
+        to='core.ObjectType',
         related_name='custom_fields',
         related_name='custom_fields',
         help_text=_('The object(s) to which this field applies.')
         help_text=_('The object(s) to which this field applies.')
     )
     )
@@ -79,7 +79,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
         help_text=_('The type of data this custom field holds')
         help_text=_('The type of data this custom field holds')
     )
     )
     object_type = models.ForeignKey(
     object_type = models.ForeignKey(
-        to='contenttypes.ContentType',
+        to='core.ObjectType',
         on_delete=models.PROTECT,
         on_delete=models.PROTECT,
         blank=True,
         blank=True,
         null=True,
         null=True,
@@ -284,7 +284,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
         """
         """
         Called when a CustomField has been renamed. Updates all assigned object data.
         Called when a CustomField has been renamed. Updates all assigned object data.
         """
         """
-        for ct in self.content_types.all():
+        for ct in self.object_types.all():
             model = ct.model_class()
             model = ct.model_class()
             params = {f'custom_field_data__{old_name}__isnull': False}
             params = {f'custom_field_data__{old_name}__isnull': False}
             instances = model.objects.filter(**params)
             instances = model.objects.filter(**params)

+ 3 - 3
netbox/extras/signals.py

@@ -205,13 +205,13 @@ def handle_cf_deleted(instance, **kwargs):
     """
     """
     Handle the cleanup of old custom field data when a CustomField is deleted.
     Handle the cleanup of old custom field data when a CustomField is deleted.
     """
     """
-    instance.remove_stale_data(instance.content_types.all())
+    instance.remove_stale_data(instance.object_types.all())
 
 
 
 
 post_save.connect(handle_cf_renamed, sender=CustomField)
 post_save.connect(handle_cf_renamed, sender=CustomField)
 pre_delete.connect(handle_cf_deleted, sender=CustomField)
 pre_delete.connect(handle_cf_deleted, sender=CustomField)
-m2m_changed.connect(handle_cf_added_obj_types, sender=CustomField.content_types.through)
-m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.content_types.through)
+m2m_changed.connect(handle_cf_added_obj_types, sender=CustomField.object_types.through)
+m2m_changed.connect(handle_cf_removed_obj_types, sender=CustomField.object_types.through)
 
 
 
 
 #
 #

+ 7 - 7
netbox/extras/tests/test_api.py

@@ -7,10 +7,10 @@ from django.utils.timezone import make_aware
 from rest_framework import status
 from rest_framework import status
 
 
 from core.choices import ManagedFileRootPathChoices
 from core.choices import ManagedFileRootPathChoices
+from core.models import ObjectType
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
 from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, Location, RackRole, Site
 from extras.choices import *
 from extras.choices import *
 from extras.models import *
 from extras.models import *
-from extras.reports import Report
 from extras.scripts import BooleanVar, IntegerVar, Script as PythonClass, StringVar
 from extras.scripts import BooleanVar, IntegerVar, Script as PythonClass, StringVar
 from utilities.testing import APITestCase, APIViewTestCases
 from utilities.testing import APITestCase, APIViewTestCases
 
 
@@ -152,17 +152,17 @@ class CustomFieldTest(APIViewTestCases.APIViewTestCase):
     brief_fields = ['description', 'display', 'id', 'name', 'url']
     brief_fields = ['description', 'display', 'id', 'name', 'url']
     create_data = [
     create_data = [
         {
         {
-            'content_types': ['dcim.site'],
+            'object_types': ['dcim.site'],
             'name': 'cf4',
             'name': 'cf4',
             'type': 'date',
             'type': 'date',
         },
         },
         {
         {
-            'content_types': ['dcim.site'],
+            'object_types': ['dcim.site'],
             'name': 'cf5',
             'name': 'cf5',
             'type': 'url',
             'type': 'url',
         },
         },
         {
         {
-            'content_types': ['dcim.site'],
+            'object_types': ['dcim.site'],
             'name': 'cf6',
             'name': 'cf6',
             'type': 'text',
             'type': 'text',
         },
         },
@@ -171,14 +171,14 @@ class CustomFieldTest(APIViewTestCases.APIViewTestCase):
         'description': 'New description',
         'description': 'New description',
     }
     }
     update_data = {
     update_data = {
-        'content_types': ['dcim.device'],
+        'object_types': ['dcim.device'],
         'name': 'New_Name',
         'name': 'New_Name',
         'description': 'New description',
         'description': 'New description',
     }
     }
 
 
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
-        site_ct = ContentType.objects.get_for_model(Site)
+        site_ct = ObjectType.objects.get_for_model(Site)
 
 
         custom_fields = (
         custom_fields = (
             CustomField(
             CustomField(
@@ -196,7 +196,7 @@ class CustomFieldTest(APIViewTestCases.APIViewTestCase):
         )
         )
         CustomField.objects.bulk_create(custom_fields)
         CustomField.objects.bulk_create(custom_fields)
         for cf in custom_fields:
         for cf in custom_fields:
-            cf.content_types.add(site_ct)
+            cf.object_types.add(site_ct)
 
 
 
 
 class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase):
 class CustomFieldChoiceSetTest(APIViewTestCases.APIViewTestCase):

+ 7 - 6
netbox/extras/tests/test_changelog.py

@@ -3,6 +3,7 @@ from django.test import override_settings
 from django.urls import reverse
 from django.urls import reverse
 from rest_framework import status
 from rest_framework import status
 
 
+from core.models import ObjectType
 from dcim.choices import SiteStatusChoices
 from dcim.choices import SiteStatusChoices
 from dcim.models import Site
 from dcim.models import Site
 from extras.choices import *
 from extras.choices import *
@@ -23,14 +24,14 @@ class ChangeLogViewTest(ModelViewTestCase):
         )
         )
 
 
         # Create a custom field on the Site model
         # Create a custom field on the Site model
-        ct = ContentType.objects.get_for_model(Site)
+        site_type = ObjectType.objects.get_for_model(Site)
         cf = CustomField(
         cf = CustomField(
             type=CustomFieldTypeChoices.TYPE_TEXT,
             type=CustomFieldTypeChoices.TYPE_TEXT,
             name='cf1',
             name='cf1',
             required=False
             required=False
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([ct])
+        cf.object_types.set([site_type])
 
 
         # Create a select custom field on the Site model
         # Create a select custom field on the Site model
         cf_select = CustomField(
         cf_select = CustomField(
@@ -40,7 +41,7 @@ class ChangeLogViewTest(ModelViewTestCase):
             choice_set=choice_set
             choice_set=choice_set
         )
         )
         cf_select.save()
         cf_select.save()
-        cf_select.content_types.set([ct])
+        cf_select.object_types.set([site_type])
 
 
     def test_create_object(self):
     def test_create_object(self):
         tags = create_tags('Tag 1', 'Tag 2')
         tags = create_tags('Tag 1', 'Tag 2')
@@ -275,14 +276,14 @@ class ChangeLogAPITest(APITestCase):
     def setUpTestData(cls):
     def setUpTestData(cls):
 
 
         # Create a custom field on the Site model
         # Create a custom field on the Site model
-        ct = ContentType.objects.get_for_model(Site)
+        site_type = ObjectType.objects.get_for_model(Site)
         cf = CustomField(
         cf = CustomField(
             type=CustomFieldTypeChoices.TYPE_TEXT,
             type=CustomFieldTypeChoices.TYPE_TEXT,
             name='cf1',
             name='cf1',
             required=False
             required=False
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([ct])
+        cf.object_types.set([site_type])
 
 
         # Create a select custom field on the Site model
         # Create a select custom field on the Site model
         choice_set = CustomFieldChoiceSet.objects.create(
         choice_set = CustomFieldChoiceSet.objects.create(
@@ -296,7 +297,7 @@ class ChangeLogAPITest(APITestCase):
             choice_set=choice_set
             choice_set=choice_set
         )
         )
         cf_select.save()
         cf_select.save()
-        cf_select.content_types.set([ct])
+        cf_select.object_types.set([site_type])
 
 
         # Create some tags
         # Create some tags
         tags = (
         tags = (

+ 45 - 45
netbox/extras/tests/test_customfields.py

@@ -1,11 +1,11 @@
 import datetime
 import datetime
 from decimal import Decimal
 from decimal import Decimal
 
 
-from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from django.urls import reverse
 from django.urls import reverse
 from rest_framework import status
 from rest_framework import status
 
 
+from core.models import ObjectType
 from dcim.filtersets import SiteFilterSet
 from dcim.filtersets import SiteFilterSet
 from dcim.forms import SiteImportForm
 from dcim.forms import SiteImportForm
 from dcim.models import Manufacturer, Rack, Site
 from dcim.models import Manufacturer, Rack, Site
@@ -28,7 +28,7 @@ class CustomFieldTest(TestCase):
             Site(name='Site C', slug='site-c'),
             Site(name='Site C', slug='site-c'),
         ])
         ])
 
 
-        cls.object_type = ContentType.objects.get_for_model(Site)
+        cls.object_type = ObjectType.objects.get_for_model(Site)
 
 
     def test_invalid_name(self):
     def test_invalid_name(self):
         """
         """
@@ -50,7 +50,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_TEXT,
             type=CustomFieldTypeChoices.TYPE_TEXT,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -75,7 +75,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_LONGTEXT,
             type=CustomFieldTypeChoices.TYPE_LONGTEXT,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -99,7 +99,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_INTEGER,
             type=CustomFieldTypeChoices.TYPE_INTEGER,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -125,7 +125,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_DECIMAL,
             type=CustomFieldTypeChoices.TYPE_DECIMAL,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -151,7 +151,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_INTEGER,
             type=CustomFieldTypeChoices.TYPE_INTEGER,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -178,7 +178,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_DATE,
             type=CustomFieldTypeChoices.TYPE_DATE,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -203,7 +203,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_DATETIME,
             type=CustomFieldTypeChoices.TYPE_DATETIME,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -228,7 +228,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_URL,
             type=CustomFieldTypeChoices.TYPE_URL,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -253,7 +253,7 @@ class CustomFieldTest(TestCase):
             type=CustomFieldTypeChoices.TYPE_JSON,
             type=CustomFieldTypeChoices.TYPE_JSON,
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -290,7 +290,7 @@ class CustomFieldTest(TestCase):
             required=False,
             required=False,
             choice_set=choice_set
             choice_set=choice_set
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -327,7 +327,7 @@ class CustomFieldTest(TestCase):
             required=False,
             required=False,
             choice_set=choice_set
             choice_set=choice_set
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -350,10 +350,10 @@ class CustomFieldTest(TestCase):
         cf = CustomField.objects.create(
         cf = CustomField.objects.create(
             name='object_field',
             name='object_field',
             type=CustomFieldTypeChoices.TYPE_OBJECT,
             type=CustomFieldTypeChoices.TYPE_OBJECT,
-            object_type=ContentType.objects.get_for_model(VLAN),
+            object_type=ObjectType.objects.get_for_model(VLAN),
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -382,10 +382,10 @@ class CustomFieldTest(TestCase):
         cf = CustomField.objects.create(
         cf = CustomField.objects.create(
             name='object_field',
             name='object_field',
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
-            object_type=ContentType.objects.get_for_model(VLAN),
+            object_type=ObjectType.objects.get_for_model(VLAN),
             required=False
             required=False
         )
         )
-        cf.content_types.set([self.object_type])
+        cf.object_types.set([self.object_type])
         instance = Site.objects.first()
         instance = Site.objects.first()
         self.assertIsNone(instance.custom_field_data[cf.name])
         self.assertIsNone(instance.custom_field_data[cf.name])
 
 
@@ -402,13 +402,13 @@ class CustomFieldTest(TestCase):
         self.assertIsNone(instance.custom_field_data.get(cf.name))
         self.assertIsNone(instance.custom_field_data.get(cf.name))
 
 
     def test_rename_customfield(self):
     def test_rename_customfield(self):
-        obj_type = ContentType.objects.get_for_model(Site)
+        obj_type = ObjectType.objects.get_for_model(Site)
         FIELD_DATA = 'abc'
         FIELD_DATA = 'abc'
 
 
         # Create a custom field
         # Create a custom field
         cf = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='field1')
         cf = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='field1')
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([obj_type])
 
 
         # Assign custom field data to an object
         # Assign custom field data to an object
         site = Site.objects.create(
         site = Site.objects.create(
@@ -437,7 +437,7 @@ class CustomFieldTest(TestCase):
             )
             )
         )
         )
         site = Site.objects.create(name='Site 1', slug='site-1')
         site = Site.objects.create(name='Site 1', slug='site-1')
-        object_type = ContentType.objects.get_for_model(Site)
+        object_type = ObjectType.objects.get_for_model(Site)
 
 
         # Text
         # Text
         CustomField(name='test', type='text', required=True, default="Default text").full_clean()
         CustomField(name='test', type='text', required=True, default="Default text").full_clean()
@@ -524,10 +524,10 @@ class CustomFieldManagerTest(TestCase):
 
 
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
-        content_type = ContentType.objects.get_for_model(Site)
+        object_type = ObjectType.objects.get_for_model(Site)
         custom_field = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo')
         custom_field = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='text_field', default='foo')
         custom_field.save()
         custom_field.save()
-        custom_field.content_types.set([content_type])
+        custom_field.object_types.set([object_type])
 
 
     def test_get_for_model(self):
     def test_get_for_model(self):
         self.assertEqual(CustomField.objects.get_for_model(Site).count(), 1)
         self.assertEqual(CustomField.objects.get_for_model(Site).count(), 1)
@@ -538,7 +538,7 @@ class CustomFieldAPITest(APITestCase):
 
 
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
-        content_type = ContentType.objects.get_for_model(Site)
+        object_type = ObjectType.objects.get_for_model(Site)
 
 
         # Create some VLANs
         # Create some VLANs
         vlans = (
         vlans = (
@@ -581,19 +581,19 @@ class CustomFieldAPITest(APITestCase):
             CustomField(
             CustomField(
                 type=CustomFieldTypeChoices.TYPE_OBJECT,
                 type=CustomFieldTypeChoices.TYPE_OBJECT,
                 name='object_field',
                 name='object_field',
-                object_type=ContentType.objects.get_for_model(VLAN),
+                object_type=ObjectType.objects.get_for_model(VLAN),
                 default=vlans[0].pk,
                 default=vlans[0].pk,
             ),
             ),
             CustomField(
             CustomField(
                 type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
                 type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
                 name='multiobject_field',
                 name='multiobject_field',
-                object_type=ContentType.objects.get_for_model(VLAN),
+                object_type=ObjectType.objects.get_for_model(VLAN),
                 default=[vlans[0].pk, vlans[1].pk],
                 default=[vlans[0].pk, vlans[1].pk],
             ),
             ),
         )
         )
         for cf in custom_fields:
         for cf in custom_fields:
             cf.save()
             cf.save()
-            cf.content_types.set([content_type])
+            cf.object_types.set([object_type])
 
 
         # Create some sites *after* creating the custom fields. This ensures that
         # Create some sites *after* creating the custom fields. This ensures that
         # default values are not set for the assigned objects.
         # default values are not set for the assigned objects.
@@ -1163,7 +1163,7 @@ class CustomFieldImportTest(TestCase):
         )
         )
         for cf in custom_fields:
         for cf in custom_fields:
             cf.save()
             cf.save()
-            cf.content_types.set([ContentType.objects.get_for_model(Site)])
+            cf.object_types.set([ObjectType.objects.get_for_model(Site)])
 
 
     def test_import(self):
     def test_import(self):
         """
         """
@@ -1256,11 +1256,11 @@ class CustomFieldModelTest(TestCase):
     def setUpTestData(cls):
     def setUpTestData(cls):
         cf1 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='foo')
         cf1 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='foo')
         cf1.save()
         cf1.save()
-        cf1.content_types.set([ContentType.objects.get_for_model(Site)])
+        cf1.object_types.set([ObjectType.objects.get_for_model(Site)])
 
 
         cf2 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='bar')
         cf2 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='bar')
         cf2.save()
         cf2.save()
-        cf2.content_types.set([ContentType.objects.get_for_model(Rack)])
+        cf2.object_types.set([ObjectType.objects.get_for_model(Rack)])
 
 
     def test_cf_data(self):
     def test_cf_data(self):
         """
         """
@@ -1299,7 +1299,7 @@ class CustomFieldModelTest(TestCase):
         """
         """
         cf3 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='baz', required=True)
         cf3 = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='baz', required=True)
         cf3.save()
         cf3.save()
-        cf3.content_types.set([ContentType.objects.get_for_model(Site)])
+        cf3.object_types.set([ObjectType.objects.get_for_model(Site)])
 
 
         site = Site(name='Test Site', slug='test-site')
         site = Site(name='Test Site', slug='test-site')
 
 
@@ -1318,7 +1318,7 @@ class CustomFieldModelFilterTest(TestCase):
 
 
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
-        obj_type = ContentType.objects.get_for_model(Site)
+        object_type = ObjectType.objects.get_for_model(Site)
 
 
         manufacturers = Manufacturer.objects.bulk_create((
         manufacturers = Manufacturer.objects.bulk_create((
             Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
             Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
@@ -1335,17 +1335,17 @@ class CustomFieldModelFilterTest(TestCase):
         # Integer filtering
         # Integer filtering
         cf = CustomField(name='cf1', type=CustomFieldTypeChoices.TYPE_INTEGER)
         cf = CustomField(name='cf1', type=CustomFieldTypeChoices.TYPE_INTEGER)
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Decimal filtering
         # Decimal filtering
         cf = CustomField(name='cf2', type=CustomFieldTypeChoices.TYPE_DECIMAL)
         cf = CustomField(name='cf2', type=CustomFieldTypeChoices.TYPE_DECIMAL)
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Boolean filtering
         # Boolean filtering
         cf = CustomField(name='cf3', type=CustomFieldTypeChoices.TYPE_BOOLEAN)
         cf = CustomField(name='cf3', type=CustomFieldTypeChoices.TYPE_BOOLEAN)
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Exact text filtering
         # Exact text filtering
         cf = CustomField(
         cf = CustomField(
@@ -1354,7 +1354,7 @@ class CustomFieldModelFilterTest(TestCase):
             filter_logic=CustomFieldFilterLogicChoices.FILTER_EXACT
             filter_logic=CustomFieldFilterLogicChoices.FILTER_EXACT
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Loose text filtering
         # Loose text filtering
         cf = CustomField(
         cf = CustomField(
@@ -1363,12 +1363,12 @@ class CustomFieldModelFilterTest(TestCase):
             filter_logic=CustomFieldFilterLogicChoices.FILTER_LOOSE
             filter_logic=CustomFieldFilterLogicChoices.FILTER_LOOSE
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Date filtering
         # Date filtering
         cf = CustomField(name='cf6', type=CustomFieldTypeChoices.TYPE_DATE)
         cf = CustomField(name='cf6', type=CustomFieldTypeChoices.TYPE_DATE)
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Exact URL filtering
         # Exact URL filtering
         cf = CustomField(
         cf = CustomField(
@@ -1377,7 +1377,7 @@ class CustomFieldModelFilterTest(TestCase):
             filter_logic=CustomFieldFilterLogicChoices.FILTER_EXACT
             filter_logic=CustomFieldFilterLogicChoices.FILTER_EXACT
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Loose URL filtering
         # Loose URL filtering
         cf = CustomField(
         cf = CustomField(
@@ -1386,7 +1386,7 @@ class CustomFieldModelFilterTest(TestCase):
             filter_logic=CustomFieldFilterLogicChoices.FILTER_LOOSE
             filter_logic=CustomFieldFilterLogicChoices.FILTER_LOOSE
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Selection filtering
         # Selection filtering
         cf = CustomField(
         cf = CustomField(
@@ -1395,7 +1395,7 @@ class CustomFieldModelFilterTest(TestCase):
             choice_set=choice_set
             choice_set=choice_set
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Multiselect filtering
         # Multiselect filtering
         cf = CustomField(
         cf = CustomField(
@@ -1404,25 +1404,25 @@ class CustomFieldModelFilterTest(TestCase):
             choice_set=choice_set
             choice_set=choice_set
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Object filtering
         # Object filtering
         cf = CustomField(
         cf = CustomField(
             name='cf11',
             name='cf11',
             type=CustomFieldTypeChoices.TYPE_OBJECT,
             type=CustomFieldTypeChoices.TYPE_OBJECT,
-            object_type=ContentType.objects.get_for_model(Manufacturer)
+            object_type=ObjectType.objects.get_for_model(Manufacturer)
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         # Multi-object filtering
         # Multi-object filtering
         cf = CustomField(
         cf = CustomField(
             name='cf12',
             name='cf12',
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
-            object_type=ContentType.objects.get_for_model(Manufacturer)
+            object_type=ObjectType.objects.get_for_model(Manufacturer)
         )
         )
         cf.save()
         cf.save()
-        cf.content_types.set([obj_type])
+        cf.object_types.set([object_type])
 
 
         Site.objects.bulk_create([
         Site.objects.bulk_create([
             Site(name='Site 1', slug='site-1', custom_field_data={
             Site(name='Site 1', slug='site-1', custom_field_data={

+ 9 - 8
netbox/extras/tests/test_filtersets.py

@@ -7,6 +7,7 @@ from django.test import TestCase
 
 
 from circuits.models import Provider
 from circuits.models import Provider
 from core.choices import ManagedFileRootPathChoices
 from core.choices import ManagedFileRootPathChoices
+from core.models import ObjectType
 from dcim.filtersets import SiteFilterSet
 from dcim.filtersets import SiteFilterSet
 from dcim.models import DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup
 from dcim.models import DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup
 from dcim.models import Location
 from dcim.models import Location
@@ -87,11 +88,11 @@ class CustomFieldTestCase(TestCase, BaseFilterSetTests):
             ),
             ),
         )
         )
         CustomField.objects.bulk_create(custom_fields)
         CustomField.objects.bulk_create(custom_fields)
-        custom_fields[0].content_types.add(ContentType.objects.get_by_natural_key('dcim', 'site'))
-        custom_fields[1].content_types.add(ContentType.objects.get_by_natural_key('dcim', 'rack'))
-        custom_fields[2].content_types.add(ContentType.objects.get_by_natural_key('dcim', 'device'))
-        custom_fields[3].content_types.add(ContentType.objects.get_by_natural_key('dcim', 'device'))
-        custom_fields[4].content_types.add(ContentType.objects.get_by_natural_key('dcim', 'device'))
+        custom_fields[0].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'site'))
+        custom_fields[1].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'rack'))
+        custom_fields[2].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'device'))
+        custom_fields[3].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'device'))
+        custom_fields[4].object_types.add(ObjectType.objects.get_by_natural_key('dcim', 'device'))
 
 
     def test_q(self):
     def test_q(self):
         params = {'q': 'foobar1'}
         params = {'q': 'foobar1'}
@@ -101,10 +102,10 @@ class CustomFieldTestCase(TestCase, BaseFilterSetTests):
         params = {'name': ['Custom Field 1', 'Custom Field 2']}
         params = {'name': ['Custom Field 1', 'Custom Field 2']}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
 
-    def test_content_types(self):
-        params = {'content_types': 'dcim.site'}
+    def test_object_types(self):
+        params = {'object_types': 'dcim.site'}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
-        params = {'content_type_id': [ContentType.objects.get_by_natural_key('dcim', 'site').pk]}
+        params = {'object_types_id': [ObjectType.objects.get_by_natural_key('dcim', 'site').pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
 
     def test_required(self):
     def test_required(self):

+ 15 - 14
netbox/extras/tests/test_forms.py

@@ -1,6 +1,7 @@
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
 from django.test import TestCase
 
 
+from core.models import ObjectType
 from dcim.forms import SiteForm
 from dcim.forms import SiteForm
 from dcim.models import Site
 from dcim.models import Site
 from extras.choices import CustomFieldTypeChoices
 from extras.choices import CustomFieldTypeChoices
@@ -12,66 +13,66 @@ class CustomFieldModelFormTest(TestCase):
 
 
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
-        obj_type = ContentType.objects.get_for_model(Site)
+        object_type = ObjectType.objects.get_for_model(Site)
         choice_set = CustomFieldChoiceSet.objects.create(
         choice_set = CustomFieldChoiceSet.objects.create(
             name='Choice Set 1',
             name='Choice Set 1',
             extra_choices=(('a', 'A'), ('b', 'B'), ('c', 'C'))
             extra_choices=(('a', 'A'), ('b', 'B'), ('c', 'C'))
         )
         )
 
 
         cf_text = CustomField.objects.create(name='text', type=CustomFieldTypeChoices.TYPE_TEXT)
         cf_text = CustomField.objects.create(name='text', type=CustomFieldTypeChoices.TYPE_TEXT)
-        cf_text.content_types.set([obj_type])
+        cf_text.object_types.set([object_type])
 
 
         cf_longtext = CustomField.objects.create(name='longtext', type=CustomFieldTypeChoices.TYPE_LONGTEXT)
         cf_longtext = CustomField.objects.create(name='longtext', type=CustomFieldTypeChoices.TYPE_LONGTEXT)
-        cf_longtext.content_types.set([obj_type])
+        cf_longtext.object_types.set([object_type])
 
 
         cf_integer = CustomField.objects.create(name='integer', type=CustomFieldTypeChoices.TYPE_INTEGER)
         cf_integer = CustomField.objects.create(name='integer', type=CustomFieldTypeChoices.TYPE_INTEGER)
-        cf_integer.content_types.set([obj_type])
+        cf_integer.object_types.set([object_type])
 
 
         cf_integer = CustomField.objects.create(name='decimal', type=CustomFieldTypeChoices.TYPE_DECIMAL)
         cf_integer = CustomField.objects.create(name='decimal', type=CustomFieldTypeChoices.TYPE_DECIMAL)
-        cf_integer.content_types.set([obj_type])
+        cf_integer.object_types.set([object_type])
 
 
         cf_boolean = CustomField.objects.create(name='boolean', type=CustomFieldTypeChoices.TYPE_BOOLEAN)
         cf_boolean = CustomField.objects.create(name='boolean', type=CustomFieldTypeChoices.TYPE_BOOLEAN)
-        cf_boolean.content_types.set([obj_type])
+        cf_boolean.object_types.set([object_type])
 
 
         cf_date = CustomField.objects.create(name='date', type=CustomFieldTypeChoices.TYPE_DATE)
         cf_date = CustomField.objects.create(name='date', type=CustomFieldTypeChoices.TYPE_DATE)
-        cf_date.content_types.set([obj_type])
+        cf_date.object_types.set([object_type])
 
 
         cf_datetime = CustomField.objects.create(name='datetime', type=CustomFieldTypeChoices.TYPE_DATETIME)
         cf_datetime = CustomField.objects.create(name='datetime', type=CustomFieldTypeChoices.TYPE_DATETIME)
-        cf_datetime.content_types.set([obj_type])
+        cf_datetime.object_types.set([object_type])
 
 
         cf_url = CustomField.objects.create(name='url', type=CustomFieldTypeChoices.TYPE_URL)
         cf_url = CustomField.objects.create(name='url', type=CustomFieldTypeChoices.TYPE_URL)
-        cf_url.content_types.set([obj_type])
+        cf_url.object_types.set([object_type])
 
 
         cf_json = CustomField.objects.create(name='json', type=CustomFieldTypeChoices.TYPE_JSON)
         cf_json = CustomField.objects.create(name='json', type=CustomFieldTypeChoices.TYPE_JSON)
-        cf_json.content_types.set([obj_type])
+        cf_json.object_types.set([object_type])
 
 
         cf_select = CustomField.objects.create(
         cf_select = CustomField.objects.create(
             name='select',
             name='select',
             type=CustomFieldTypeChoices.TYPE_SELECT,
             type=CustomFieldTypeChoices.TYPE_SELECT,
             choice_set=choice_set
             choice_set=choice_set
         )
         )
-        cf_select.content_types.set([obj_type])
+        cf_select.object_types.set([object_type])
 
 
         cf_multiselect = CustomField.objects.create(
         cf_multiselect = CustomField.objects.create(
             name='multiselect',
             name='multiselect',
             type=CustomFieldTypeChoices.TYPE_MULTISELECT,
             type=CustomFieldTypeChoices.TYPE_MULTISELECT,
             choice_set=choice_set
             choice_set=choice_set
         )
         )
-        cf_multiselect.content_types.set([obj_type])
+        cf_multiselect.object_types.set([object_type])
 
 
         cf_object = CustomField.objects.create(
         cf_object = CustomField.objects.create(
             name='object',
             name='object',
             type=CustomFieldTypeChoices.TYPE_OBJECT,
             type=CustomFieldTypeChoices.TYPE_OBJECT,
             object_type=ContentType.objects.get_for_model(Site)
             object_type=ContentType.objects.get_for_model(Site)
         )
         )
-        cf_object.content_types.set([obj_type])
+        cf_object.object_types.set([object_type])
 
 
         cf_multiobject = CustomField.objects.create(
         cf_multiobject = CustomField.objects.create(
             name='multiobject',
             name='multiobject',
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
             type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
             object_type=ContentType.objects.get_for_model(Site)
             object_type=ContentType.objects.get_for_model(Site)
         )
         )
-        cf_multiobject.content_types.set([obj_type])
+        cf_multiobject.object_types.set([object_type])
 
 
     def test_empty_values(self):
     def test_empty_values(self):
         """
         """

+ 5 - 4
netbox/extras/tests/test_views.py

@@ -5,6 +5,7 @@ from django.contrib.auth import get_user_model
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.urls import reverse
 from django.urls import reverse
 
 
+from core.models import ObjectType
 from dcim.models import DeviceType, Manufacturer, Site
 from dcim.models import DeviceType, Manufacturer, Site
 from extras.choices import *
 from extras.choices import *
 from extras.models import *
 from extras.models import *
@@ -19,7 +20,7 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
     @classmethod
     @classmethod
     def setUpTestData(cls):
     def setUpTestData(cls):
 
 
-        site_ct = ContentType.objects.get_for_model(Site)
+        site_type = ObjectType.objects.get_for_model(Site)
         CustomFieldChoiceSet.objects.create(
         CustomFieldChoiceSet.objects.create(
             name='Choice Set 1',
             name='Choice Set 1',
             extra_choices=(
             extra_choices=(
@@ -36,13 +37,13 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         )
         )
         for customfield in custom_fields:
         for customfield in custom_fields:
             customfield.save()
             customfield.save()
-            customfield.content_types.add(site_ct)
+            customfield.object_types.add(site_type)
 
 
         cls.form_data = {
         cls.form_data = {
             'name': 'field_x',
             'name': 'field_x',
             'label': 'Field X',
             'label': 'Field X',
             'type': 'text',
             'type': 'text',
-            'content_types': [site_ct.pk],
+            'object_types': [site_type.pk],
             'search_weight': 2000,
             'search_weight': 2000,
             'filter_logic': CustomFieldFilterLogicChoices.FILTER_EXACT,
             'filter_logic': CustomFieldFilterLogicChoices.FILTER_EXACT,
             'default': None,
             'default': None,
@@ -53,7 +54,7 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         }
         }
 
 
         cls.csv_data = (
         cls.csv_data = (
-            'name,label,type,content_types,object_type,weight,search_weight,filter_logic,choice_set,validation_minimum,validation_maximum,validation_regex,ui_visible,ui_editable',
+            'name,label,type,object_types,object_type,weight,search_weight,filter_logic,choice_set,validation_minimum,validation_maximum,validation_regex,ui_visible,ui_editable',
             'field4,Field 4,text,dcim.site,,100,1000,exact,,,,[a-z]{3},always,yes',
             'field4,Field 4,text,dcim.site,,100,1000,exact,,,,[a-z]{3},always,yes',
             'field5,Field 5,integer,dcim.site,,100,2000,exact,,1,100,,always,yes',
             'field5,Field 5,integer,dcim.site,,100,2000,exact,,1,100,,always,yes',
             'field6,Field 6,select,dcim.site,,100,3000,exact,Choice Set 1,,,,always,yes',
             'field6,Field 6,select,dcim.site,,100,3000,exact,Choice Set 1,,,,always,yes',

+ 2 - 2
netbox/extras/views.py

@@ -46,9 +46,9 @@ class CustomFieldView(generic.ObjectView):
     def get_extra_context(self, request, instance):
     def get_extra_context(self, request, instance):
         related_models = ()
         related_models = ()
 
 
-        for content_type in instance.content_types.all():
+        for object_type in instance.object_types.all():
             related_models += (
             related_models += (
-                content_type.model_class().objects.restrict(request.user, 'view').exclude(
+                object_type.model_class().objects.restrict(request.user, 'view').exclude(
                     Q(**{f'custom_field_data__{instance.name}': ''}) |
                     Q(**{f'custom_field_data__{instance.name}': ''}) |
                     Q(**{f'custom_field_data__{instance.name}': None})
                     Q(**{f'custom_field_data__{instance.name}': None})
                 ),
                 ),

+ 3 - 2
netbox/netbox/api/viewsets/mixins.py

@@ -5,6 +5,7 @@ from django.http import Http404
 from rest_framework import status
 from rest_framework import status
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
+from core.models import ObjectType
 from extras.models import ExportTemplate
 from extras.models import ExportTemplate
 from netbox.api.serializers import BulkOperationSerializer
 from netbox.api.serializers import BulkOperationSerializer
 
 
@@ -26,9 +27,9 @@ class CustomFieldsMixin:
         context = super().get_serializer_context()
         context = super().get_serializer_context()
 
 
         if hasattr(self.queryset.model, 'custom_fields'):
         if hasattr(self.queryset.model, 'custom_fields'):
-            content_type = ContentType.objects.get_for_model(self.queryset.model)
+            object_type = ObjectType.objects.get_for_model(self.queryset.model)
             context.update({
             context.update({
-                'custom_fields': content_type.custom_fields.all(),
+                'custom_fields': object_type.custom_fields.all(),
             })
             })
 
 
         return context
         return context

+ 1 - 1
netbox/netbox/filtersets.py

@@ -281,7 +281,7 @@ class NetBoxModelFilterSet(ChangeLoggedModelFilterSet):
 
 
         # Dynamically add a Filter for each CustomField applicable to the parent model
         # Dynamically add a Filter for each CustomField applicable to the parent model
         custom_fields = CustomField.objects.filter(
         custom_fields = CustomField.objects.filter(
-            content_types=ContentType.objects.get_for_model(self._meta.model)
+            object_types=ContentType.objects.get_for_model(self._meta.model)
         ).exclude(
         ).exclude(
             filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
             filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED
         )
         )

+ 1 - 1
netbox/netbox/forms/base.py

@@ -88,7 +88,7 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm):
 
 
     def _get_custom_fields(self, content_type):
     def _get_custom_fields(self, content_type):
         return CustomField.objects.filter(
         return CustomField.objects.filter(
-            content_types=content_type,
+            object_types=content_type,
             ui_editable=CustomFieldUIEditableChoices.YES
             ui_editable=CustomFieldUIEditableChoices.YES
         )
         )
 
 

+ 4 - 3
netbox/netbox/forms/mixins.py

@@ -2,6 +2,7 @@ from django import forms
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 
 
+from core.models import ObjectType
 from extras.choices import *
 from extras.choices import *
 from extras.models import *
 from extras.models import *
 from utilities.forms.fields import DynamicModelMultipleChoiceField
 from utilities.forms.fields import DynamicModelMultipleChoiceField
@@ -32,16 +33,16 @@ class CustomFieldsMixin:
 
 
     def _get_content_type(self):
     def _get_content_type(self):
         """
         """
-        Return the ContentType of the form's model.
+        Return the ObjectType of the form's model.
         """
         """
         if not getattr(self, 'model', None):
         if not getattr(self, 'model', None):
             raise NotImplementedError(_("{class_name} must specify a model class.").format(
             raise NotImplementedError(_("{class_name} must specify a model class.").format(
                 class_name=self.__class__.__name__
                 class_name=self.__class__.__name__
             ))
             ))
-        return ContentType.objects.get_for_model(self.model)
+        return ObjectType.objects.get_for_model(self.model)
 
 
     def _get_custom_fields(self, content_type):
     def _get_custom_fields(self, content_type):
-        return CustomField.objects.filter(content_types=content_type).exclude(
+        return CustomField.objects.filter(object_types=content_type).exclude(
             ui_editable=CustomFieldUIEditableChoices.HIDDEN
             ui_editable=CustomFieldUIEditableChoices.HIDDEN
         )
         )
 
 

+ 2 - 2
netbox/netbox/middleware.py

@@ -70,8 +70,8 @@ class CoreMiddleware:
             return
             return
 
 
         # Cleanly handle exceptions that occur from REST API requests
         # Cleanly handle exceptions that occur from REST API requests
-        if is_api_request(request):
-            return rest_api_server_error(request)
+        # if is_api_request(request):
+        #     return rest_api_server_error(request)
 
 
         # Ignore Http404s (defer to Django's built-in 404 handling)
         # Ignore Http404s (defer to Django's built-in 404 handling)
         if isinstance(exception, Http404):
         if isinstance(exception, Http404):

+ 7 - 6
netbox/netbox/search/backends.py

@@ -11,6 +11,7 @@ from django.utils.module_loading import import_string
 import netaddr
 import netaddr
 from netaddr.core import AddrFormatError
 from netaddr.core import AddrFormatError
 
 
+from core.models import ObjectType
 from extras.models import CachedValue, CustomField
 from extras.models import CachedValue, CustomField
 from netbox.registry import registry
 from netbox.registry import registry
 from utilities.querysets import RestrictedPrefetch
 from utilities.querysets import RestrictedPrefetch
@@ -134,7 +135,7 @@ class CachedValueSearchBackend(SearchBackend):
         # objects). This must be done before generating the final results list, which returns
         # objects). This must be done before generating the final results list, which returns
         # a RawQuerySet.
         # a RawQuerySet.
         content_type_ids = set(queryset.values_list('object_type', flat=True))
         content_type_ids = set(queryset.values_list('object_type', flat=True))
-        content_types = ContentType.objects.filter(pk__in=content_type_ids)
+        object_types = ObjectType.objects.filter(pk__in=content_type_ids)
 
 
         # Construct a Prefetch to pre-fetch only those related objects for which the
         # Construct a Prefetch to pre-fetch only those related objects for which the
         # user has permission to view.
         # user has permission to view.
@@ -153,7 +154,7 @@ class CachedValueSearchBackend(SearchBackend):
 
 
         # Iterate through each ContentType represented in the search results and prefetch any
         # Iterate through each ContentType represented in the search results and prefetch any
         # related objects necessary to render the prescribed display attributes (display_attrs).
         # related objects necessary to render the prescribed display attributes (display_attrs).
-        for ct in content_types:
+        for ct in object_types:
             model = ct.model_class()
             model = ct.model_class()
             indexer = registry['search'].get(content_type_identifier(ct))
             indexer = registry['search'].get(content_type_identifier(ct))
             if not (display_attrs := getattr(indexer, 'display_attrs', None)):
             if not (display_attrs := getattr(indexer, 'display_attrs', None)):
@@ -182,7 +183,7 @@ class CachedValueSearchBackend(SearchBackend):
         return ret
         return ret
 
 
     def cache(self, instances, indexer=None, remove_existing=True):
     def cache(self, instances, indexer=None, remove_existing=True):
-        content_type = None
+        object_type = None
         custom_fields = None
         custom_fields = None
 
 
         # Convert a single instance to an iterable
         # Convert a single instance to an iterable
@@ -204,8 +205,8 @@ class CachedValueSearchBackend(SearchBackend):
                         break
                         break
 
 
                 # Prefetch any associated custom fields
                 # Prefetch any associated custom fields
-                content_type = ContentType.objects.get_for_model(indexer.model)
-                custom_fields = CustomField.objects.filter(content_types=content_type).exclude(search_weight=0)
+                object_type = ObjectType.objects.get_for_model(indexer.model)
+                custom_fields = CustomField.objects.filter(object_types=object_type).exclude(search_weight=0)
 
 
             # Wipe out any previously cached values for the object
             # Wipe out any previously cached values for the object
             if remove_existing:
             if remove_existing:
@@ -215,7 +216,7 @@ class CachedValueSearchBackend(SearchBackend):
             for field in indexer.to_cache(instance, custom_fields=custom_fields):
             for field in indexer.to_cache(instance, custom_fields=custom_fields):
                 buffer.append(
                 buffer.append(
                     CachedValue(
                     CachedValue(
-                        object_type=content_type,
+                        object_type=object_type,
                         object_id=instance.pk,
                         object_id=instance.pk,
                         field=field.name,
                         field=field.name,
                         type=field.type,
                         type=field.type,

+ 4 - 4
netbox/netbox/tables/tables.py

@@ -3,7 +3,6 @@ from copy import deepcopy
 import django_tables2 as tables
 import django_tables2 as tables
 from django.contrib.auth.models import AnonymousUser
 from django.contrib.auth.models import AnonymousUser
 from django.contrib.contenttypes.fields import GenericForeignKey
 from django.contrib.contenttypes.fields import GenericForeignKey
-from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import FieldDoesNotExist
 from django.core.exceptions import FieldDoesNotExist
 from django.db.models.fields.related import RelatedField
 from django.db.models.fields.related import RelatedField
 from django.urls import reverse
 from django.urls import reverse
@@ -12,6 +11,7 @@ from django.utils.safestring import mark_safe
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
 from django_tables2.data import TableQuerysetData
 from django_tables2.data import TableQuerysetData
 
 
+from core.models import ObjectType
 from extras.choices import *
 from extras.choices import *
 from extras.models import CustomField, CustomLink
 from extras.models import CustomField, CustomLink
 from netbox.registry import registry
 from netbox.registry import registry
@@ -201,14 +201,14 @@ class NetBoxTable(BaseTable):
             ])
             ])
 
 
         # Add custom field & custom link columns
         # Add custom field & custom link columns
-        content_type = ContentType.objects.get_for_model(self._meta.model)
+        object_type = ObjectType.objects.get_for_model(self._meta.model)
         custom_fields = CustomField.objects.filter(
         custom_fields = CustomField.objects.filter(
-            content_types=content_type
+            object_types=object_type
         ).exclude(ui_visible=CustomFieldUIVisibleChoices.HIDDEN)
         ).exclude(ui_visible=CustomFieldUIVisibleChoices.HIDDEN)
         extra_columns.extend([
         extra_columns.extend([
             (f'cf_{cf.name}', columns.CustomFieldColumn(cf)) for cf in custom_fields
             (f'cf_{cf.name}', columns.CustomFieldColumn(cf)) for cf in custom_fields
         ])
         ])
-        custom_links = CustomLink.objects.filter(content_types=content_type, enabled=True)
+        custom_links = CustomLink.objects.filter(content_types=object_type, enabled=True)
         extra_columns.extend([
         extra_columns.extend([
             (f'cl_{cl.name}', columns.CustomLinkColumn(cl)) for cl in custom_links
             (f'cl_{cl.name}', columns.CustomLinkColumn(cl)) for cl in custom_links
         ])
         ])

+ 4 - 3
netbox/utilities/testing/base.py

@@ -10,6 +10,7 @@ from django.test import Client, TestCase as _TestCase
 from netaddr import IPNetwork
 from netaddr import IPNetwork
 from taggit.managers import TaggableManager
 from taggit.managers import TaggableManager
 
 
+from core.models import ObjectType
 from users.models import ObjectPermission
 from users.models import ObjectPermission
 from utilities.permissions import resolve_permission_ct
 from utilities.permissions import resolve_permission_ct
 from utilities.utils import content_type_identifier
 from utilities.utils import content_type_identifier
@@ -112,7 +113,7 @@ class ModelTestCase(TestCase):
             # Handle ManyToManyFields
             # Handle ManyToManyFields
             if value and type(field) in (ManyToManyField, TaggableManager):
             if value and type(field) in (ManyToManyField, TaggableManager):
 
 
-                if field.related_model is ContentType and api:
+                if field.related_model in (ContentType, ObjectType) and api:
                     model_dict[key] = sorted([content_type_identifier(ct) for ct in value])
                     model_dict[key] = sorted([content_type_identifier(ct) for ct in value])
                 else:
                 else:
                     model_dict[key] = sorted([obj.pk for obj in value])
                     model_dict[key] = sorted([obj.pk for obj in value])
@@ -120,8 +121,8 @@ class ModelTestCase(TestCase):
             elif api:
             elif api:
 
 
                 # Replace ContentType numeric IDs with <app_label>.<model>
                 # Replace ContentType numeric IDs with <app_label>.<model>
-                if type(getattr(instance, key)) is ContentType:
-                    ct = ContentType.objects.get(pk=value)
+                if type(getattr(instance, key)) in (ContentType, ObjectType):
+                    ct = ObjectType.objects.get(pk=value)
                     model_dict[key] = content_type_identifier(ct)
                     model_dict[key] = content_type_identifier(ct)
 
 
                 # Convert IPNetwork instances to strings
                 # Convert IPNetwork instances to strings

+ 3 - 5
netbox/utilities/tests/test_api.py

@@ -1,10 +1,8 @@
-import urllib.parse
-
-from django.contrib.contenttypes.models import ContentType
 from django.test import Client, TestCase, override_settings
 from django.test import Client, TestCase, override_settings
 from django.urls import reverse
 from django.urls import reverse
 from rest_framework import status
 from rest_framework import status
 
 
+from core.models import ObjectType
 from dcim.models import Region, Site
 from dcim.models import Region, Site
 from extras.choices import CustomFieldTypeChoices
 from extras.choices import CustomFieldTypeChoices
 from extras.models import CustomField
 from extras.models import CustomField
@@ -240,10 +238,10 @@ class APIDocsTestCase(TestCase):
         self.client = Client()
         self.client = Client()
 
 
         # Populate a CustomField to activate CustomFieldSerializer
         # Populate a CustomField to activate CustomFieldSerializer
-        content_type = ContentType.objects.get_for_model(Site)
+        object_type = ObjectType.objects.get_for_model(Site)
         self.cf_text = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='test')
         self.cf_text = CustomField(type=CustomFieldTypeChoices.TYPE_TEXT, name='test')
         self.cf_text.save()
         self.cf_text.save()
-        self.cf_text.content_types.set([content_type])
+        self.cf_text.object_types.set([object_type])
         self.cf_text.save()
         self.cf_text.save()
 
 
     def test_api_docs(self):
     def test_api_docs(self):