Rename CustomLink.content_types to object_types & use ObjectType proxy

This commit is contained in:
Jeremy Stretch 2024-03-01 15:04:02 -05:00
parent 54b9d1b3f2
commit ba514aceac
16 changed files with 60 additions and 47 deletions

View File

@ -196,7 +196,7 @@ class CustomFieldChoiceSetSerializer(ValidatedModelSerializer):
class CustomLinkSerializer(ValidatedModelSerializer): class CustomLinkSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:customlink-detail') url = serializers.HyperlinkedIdentityField(view_name='extras-api:customlink-detail')
content_types = ContentTypeField( object_types = ContentTypeField(
queryset=ObjectType.objects.with_feature('custom_links'), queryset=ObjectType.objects.with_feature('custom_links'),
many=True many=True
) )
@ -204,7 +204,7 @@ class CustomLinkSerializer(ValidatedModelSerializer):
class Meta: class Meta:
model = CustomLink model = CustomLink
fields = [ fields = [
'id', 'url', 'display', 'content_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'id', 'url', 'display', 'object_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
'button_class', 'new_window', 'created', 'last_updated', 'button_class', 'new_window', 'created', 'last_updated',
] ]
brief_fields = ('id', 'url', 'display', 'name') brief_fields = ('id', 'url', 'display', 'name')

View File

@ -190,15 +190,15 @@ class CustomLinkFilterSet(BaseFilterSet):
method='search', method='search',
label=_('Search'), label=_('Search'),
) )
content_type_id = MultiValueNumberFilter( object_types_id = MultiValueNumberFilter(
field_name='content_types__id' field_name='object_types__id'
) )
content_types = ContentTypeFilter() object_types = ContentTypeFilter()
class Meta: class Meta:
model = CustomLink model = CustomLink
fields = [ fields = [
'id', 'content_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'new_window', 'id', 'object_types', 'name', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'new_window',
] ]
def search(self, queryset, name, value): def search(self, queryset, name, value):

View File

@ -111,8 +111,8 @@ class CustomFieldChoiceSetImportForm(CSVModelForm):
class CustomLinkImportForm(CSVModelForm): class CustomLinkImportForm(CSVModelForm):
content_types = CSVMultipleContentTypeField( object_types = CSVMultipleContentTypeField(
label=_('Content types'), label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links'), queryset=ObjectType.objects.with_feature('custom_links'),
help_text=_("One or more assigned object types") help_text=_("One or more assigned object types")
) )
@ -120,7 +120,7 @@ class CustomLinkImportForm(CSVModelForm):
class Meta: class Meta:
model = CustomLink model = CustomLink
fields = ( fields = (
'name', 'content_types', 'enabled', 'weight', 'group_name', 'button_class', 'new_window', 'link_text', 'name', 'object_types', 'enabled', 'weight', 'group_name', 'button_class', 'new_window', 'link_text',
'link_url', 'link_url',
) )

View File

@ -108,10 +108,10 @@ class CustomFieldChoiceSetFilterForm(SavedFiltersMixin, FilterForm):
class CustomLinkFilterForm(SavedFiltersMixin, FilterForm): class CustomLinkFilterForm(SavedFiltersMixin, FilterForm):
fieldsets = ( fieldsets = (
(None, ('q', 'filter_id')), (None, ('q', 'filter_id')),
(_('Attributes'), ('content_types', 'enabled', 'new_window', 'weight')), (_('Attributes'), ('object_types', 'enabled', 'new_window', 'weight')),
) )
content_types = ContentTypeMultipleChoiceField( object_types = ContentTypeMultipleChoiceField(
label=_('Content types'), label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links'), queryset=ObjectType.objects.with_feature('custom_links'),
required=False required=False
) )

View File

@ -122,13 +122,13 @@ class CustomFieldChoiceSetForm(forms.ModelForm):
class CustomLinkForm(forms.ModelForm): class CustomLinkForm(forms.ModelForm):
content_types = ContentTypeMultipleChoiceField( object_types = ContentTypeMultipleChoiceField(
label=_('Content types'), label=_('Object types'),
queryset=ObjectType.objects.with_feature('custom_links') queryset=ObjectType.objects.with_feature('custom_links')
) )
fieldsets = ( fieldsets = (
(_('Custom Link'), ('name', 'content_types', 'weight', 'group_name', 'button_class', 'enabled', 'new_window')), (_('Custom Link'), ('name', 'object_types', 'weight', 'group_name', 'button_class', 'enabled', 'new_window')),
(_('Templates'), ('link_text', 'link_url')), (_('Templates'), ('link_text', 'link_url')),
) )

View File

@ -55,7 +55,7 @@ class CustomLinkType(ObjectType):
class Meta: class Meta:
model = models.CustomLink model = models.CustomLink
exclude = ('content_types', ) exclude = ('object_types', )
filterset_class = filtersets.CustomLinkFilterSet filterset_class = filtersets.CustomLinkFilterSet

View File

@ -10,6 +10,7 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
# Custom fields
migrations.RenameField( migrations.RenameField(
model_name='customfield', model_name='customfield',
old_name='content_types', old_name='content_types',
@ -25,4 +26,16 @@ class Migration(migrations.Migration):
name='object_type', name='object_type',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype'), field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype'),
), ),
# Custom links
migrations.RenameField(
model_name='customlink',
old_name='content_types',
new_name='object_types',
),
migrations.AlterField(
model_name='customlink',
name='object_types',
field=models.ManyToManyField(related_name='custom_links', to='core.objecttype'),
),
] ]

View File

@ -313,8 +313,8 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template A custom link to an external representation of a NetBox object. The link text and URL fields accept Jinja2 template
code to be rendered with an object as context. code to be rendered with an object as context.
""" """
content_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='contenttypes.ContentType', to='core.ObjectType',
related_name='custom_links', related_name='custom_links',
help_text=_('The object type(s) to which this link applies.') help_text=_('The object type(s) to which this link applies.')
) )

View File

@ -40,8 +40,8 @@ class CustomFieldTable(NetBoxTable):
verbose_name=_('Name'), verbose_name=_('Name'),
linkify=True linkify=True
) )
content_types = columns.ContentTypesColumn( object_types = columns.ContentTypesColumn(
verbose_name=_('Content Types') verbose_name=_('Object Types')
) )
required = columns.BooleanColumn( required = columns.BooleanColumn(
verbose_name=_('Required') verbose_name=_('Required')
@ -71,11 +71,11 @@ class CustomFieldTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = CustomField model = CustomField
fields = ( fields = (
'pk', 'id', 'name', 'content_types', 'label', 'type', 'group_name', 'required', 'default', 'description', 'pk', 'id', 'name', 'object_types', 'label', 'type', 'group_name', 'required', 'default', 'description',
'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable', 'weight', 'choice_set', 'search_weight', 'filter_logic', 'ui_visible', 'ui_editable', 'is_cloneable', 'weight', 'choice_set',
'choices', 'created', 'last_updated', 'choices', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'content_types', 'label', 'group_name', 'type', 'required', 'description') default_columns = ('pk', 'name', 'object_types', 'label', 'group_name', 'type', 'required', 'description')
class CustomFieldChoiceSetTable(NetBoxTable): class CustomFieldChoiceSetTable(NetBoxTable):
@ -115,8 +115,8 @@ class CustomLinkTable(NetBoxTable):
verbose_name=_('Name'), verbose_name=_('Name'),
linkify=True linkify=True
) )
content_types = columns.ContentTypesColumn( object_types = columns.ContentTypesColumn(
verbose_name=_('Content Types'), verbose_name=_('Object Types'),
) )
enabled = columns.BooleanColumn( enabled = columns.BooleanColumn(
verbose_name=_('Enabled'), verbose_name=_('Enabled'),
@ -128,10 +128,10 @@ class CustomLinkTable(NetBoxTable):
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = CustomLink model = CustomLink
fields = ( fields = (
'pk', 'id', 'name', 'content_types', 'enabled', 'link_text', 'link_url', 'weight', 'group_name', 'pk', 'id', 'name', 'object_types', 'enabled', 'link_text', 'link_url', 'weight', 'group_name',
'button_class', 'new_window', 'created', 'last_updated', 'button_class', 'new_window', 'created', 'last_updated',
) )
default_columns = ('pk', 'name', 'content_types', 'enabled', 'group_name', 'button_class', 'new_window') default_columns = ('pk', 'name', 'object_types', 'enabled', 'group_name', 'button_class', 'new_window')
class ExportTemplateTable(NetBoxTable): class ExportTemplateTable(NetBoxTable):

View File

@ -1,7 +1,7 @@
from django import template from django import template
from django.contrib.contenttypes.models import ContentType
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from core.models import ObjectType
from extras.models import CustomLink from extras.models import CustomLink
@ -32,8 +32,8 @@ def custom_links(context, obj):
""" """
Render all applicable links for the given object. Render all applicable links for the given object.
""" """
content_type = ContentType.objects.get_for_model(obj) object_type = ObjectType.objects.get_for_model(obj)
custom_links = CustomLink.objects.filter(content_types=content_type, enabled=True) custom_links = CustomLink.objects.filter(object_types=object_type, enabled=True)
if not custom_links: if not custom_links:
return '' return ''

View File

@ -273,21 +273,21 @@ class CustomLinkTest(APIViewTestCases.APIViewTestCase):
brief_fields = ['display', 'id', 'name', 'url'] brief_fields = ['display', 'id', 'name', 'url']
create_data = [ create_data = [
{ {
'content_types': ['dcim.site'], 'object_types': ['dcim.site'],
'name': 'Custom Link 4', 'name': 'Custom Link 4',
'enabled': True, 'enabled': True,
'link_text': 'Link 4', 'link_text': 'Link 4',
'link_url': 'http://example.com/?4', 'link_url': 'http://example.com/?4',
}, },
{ {
'content_types': ['dcim.site'], 'object_types': ['dcim.site'],
'name': 'Custom Link 5', 'name': 'Custom Link 5',
'enabled': True, 'enabled': True,
'link_text': 'Link 5', 'link_text': 'Link 5',
'link_url': 'http://example.com/?5', 'link_url': 'http://example.com/?5',
}, },
{ {
'content_types': ['dcim.site'], 'object_types': ['dcim.site'],
'name': 'Custom Link 6', 'name': 'Custom Link 6',
'enabled': False, 'enabled': False,
'link_text': 'Link 6', 'link_text': 'Link 6',
@ -301,7 +301,7 @@ class CustomLinkTest(APIViewTestCases.APIViewTestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
site_ct = ContentType.objects.get_for_model(Site) site_type = ObjectType.objects.get_for_model(Site)
custom_links = ( custom_links = (
CustomLink( CustomLink(
@ -325,7 +325,7 @@ class CustomLinkTest(APIViewTestCases.APIViewTestCase):
) )
CustomLink.objects.bulk_create(custom_links) CustomLink.objects.bulk_create(custom_links)
for i, custom_link in enumerate(custom_links): for i, custom_link in enumerate(custom_links):
custom_link.content_types.set([site_ct]) custom_link.object_types.set([site_type])
class SavedFilterTest(APIViewTestCases.APIViewTestCase): class SavedFilterTest(APIViewTestCases.APIViewTestCase):

View File

@ -397,7 +397,7 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
content_types = ContentType.objects.filter(model__in=['site', 'rack', 'device']) object_types = ObjectType.objects.filter(model__in=['site', 'rack', 'device'])
custom_links = ( custom_links = (
CustomLink( CustomLink(
@ -427,7 +427,7 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
) )
CustomLink.objects.bulk_create(custom_links) CustomLink.objects.bulk_create(custom_links)
for i, custom_link in enumerate(custom_links): for i, custom_link in enumerate(custom_links):
custom_link.content_types.set([content_types[i]]) custom_link.object_types.set([object_types[i]])
def test_q(self): def test_q(self):
params = {'q': 'Custom Link 1'} params = {'q': 'Custom Link 1'}
@ -437,10 +437,10 @@ class CustomLinkTestCase(TestCase, BaseFilterSetTests):
params = {'name': ['Custom Link 1', 'Custom Link 2']} params = {'name': ['Custom Link 1', 'Custom Link 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): def test_object_types(self):
params = {'content_types': 'dcim.site'} 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_for_model(Site).pk]} params = {'object_types_id': [ContentType.objects.get_for_model(Site).pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_weight(self): def test_weight(self):

View File

@ -138,7 +138,7 @@ class CustomLinkTestCase(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)
custom_links = ( custom_links = (
CustomLink(name='Custom Link 1', enabled=True, link_text='Link 1', link_url='http://example.com/?1'), CustomLink(name='Custom Link 1', enabled=True, link_text='Link 1', link_url='http://example.com/?1'),
CustomLink(name='Custom Link 2', enabled=True, link_text='Link 2', link_url='http://example.com/?2'), CustomLink(name='Custom Link 2', enabled=True, link_text='Link 2', link_url='http://example.com/?2'),
@ -146,11 +146,11 @@ class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
) )
CustomLink.objects.bulk_create(custom_links) CustomLink.objects.bulk_create(custom_links)
for i, custom_link in enumerate(custom_links): for i, custom_link in enumerate(custom_links):
custom_link.content_types.set([site_ct]) custom_link.object_types.set([site_type])
cls.form_data = { cls.form_data = {
'name': 'Custom Link X', 'name': 'Custom Link X',
'content_types': [site_ct.pk], 'object_types': [site_type.pk],
'enabled': False, 'enabled': False,
'weight': 100, 'weight': 100,
'button_class': CustomLinkButtonClassChoices.DEFAULT, 'button_class': CustomLinkButtonClassChoices.DEFAULT,
@ -159,7 +159,7 @@ class CustomLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
} }
cls.csv_data = ( cls.csv_data = (
"name,content_types,enabled,weight,button_class,link_text,link_url", "name,object_types,enabled,weight,button_class,link_text,link_url",
"Custom Link 4,dcim.site,True,100,blue,Link 4,http://exmaple.com/?4", "Custom Link 4,dcim.site,True,100,blue,Link 4,http://exmaple.com/?4",
"Custom Link 5,dcim.site,True,100,blue,Link 5,http://exmaple.com/?5", "Custom Link 5,dcim.site,True,100,blue,Link 5,http://exmaple.com/?5",
"Custom Link 6,dcim.site,False,100,blue,Link 6,http://exmaple.com/?6", "Custom Link 6,dcim.site,False,100,blue,Link 6,http://exmaple.com/?6",
@ -652,7 +652,7 @@ class CustomLinkTest(TestCase):
new_window=False new_window=False
) )
customlink.save() customlink.save()
customlink.content_types.set([ContentType.objects.get_for_model(Site)]) customlink.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')
site.save() site.save()

View File

@ -208,7 +208,7 @@ class NetBoxTable(BaseTable):
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=object_type, enabled=True) custom_links = CustomLink.objects.filter(object_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
]) ])

View File

@ -89,7 +89,7 @@
<div class="card"> <div class="card">
<h5 class="card-header">{% trans "Object Types" %}</h5> <h5 class="card-header">{% trans "Object Types" %}</h5>
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
{% for ct in object.content_types.all %} {% for ct in object.object_types.all %}
<tr> <tr>
<td>{{ ct }}</td> <td>{{ ct }}</td>
</tr> </tr>

View File

@ -38,7 +38,7 @@
<div class="card"> <div class="card">
<h5 class="card-header">{% trans "Assigned Models" %}</h5> <h5 class="card-header">{% trans "Assigned Models" %}</h5>
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
{% for ct in object.content_types.all %} {% for ct in object.object_types.all %}
<tr> <tr>
<td>{{ ct }}</td> <td>{{ ct }}</td>
</tr> </tr>