mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Rename CustomLink.content_types to object_types & use ObjectType proxy
This commit is contained in:
parent
54b9d1b3f2
commit
ba514aceac
@ -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')
|
||||||
|
@ -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):
|
||||||
|
@ -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',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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'),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -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.')
|
||||||
)
|
)
|
||||||
|
@ -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):
|
||||||
|
@ -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 ''
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
])
|
])
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user