mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #10761: Enable associating an export template with multiple object types
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
* Device and virtual machine names are no longer case-sensitive. Attempting to create e.g. "device1" and "DEVICE1" will raise a validation error.
|
* Device and virtual machine names are no longer case-sensitive. Attempting to create e.g. "device1" and "DEVICE1" will raise a validation error.
|
||||||
* The `asn` field has been removed from the provider model. Please replicate any provider ASN assignments to the ASN model introduced in NetBox v3.1 prior to upgrading.
|
* The `asn` field has been removed from the provider model. Please replicate any provider ASN assignments to the ASN model introduced in NetBox v3.1 prior to upgrading.
|
||||||
* The `noc_contact`, `admin_contact`, and `portal_url` fields have been removed from the provider model. Please replicate any data remaining in these fields to the contact model introduced in NetBox v3.1 prior to upgrading.
|
* The `noc_contact`, `admin_contact`, and `portal_url` fields have been removed from the provider model. Please replicate any data remaining in these fields to the contact model introduced in NetBox v3.1 prior to upgrading.
|
||||||
* The `content_type` field on the CustomLink model has been renamed to `content_types` and now supports the assignment of multiple content types.
|
* The `content_type` field on the CustomLink and ExportTemplate models have been renamed to `content_types` and now supports the assignment of multiple content types.
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ A new `PluginMenu` class has been introduced, which enables a plugin to inject a
|
|||||||
* [#10348](https://github.com/netbox-community/netbox/issues/10348) - Add decimal custom field type
|
* [#10348](https://github.com/netbox-community/netbox/issues/10348) - Add decimal custom field type
|
||||||
* [#10556](https://github.com/netbox-community/netbox/issues/10556) - Include a `display` field in all GraphQL object types
|
* [#10556](https://github.com/netbox-community/netbox/issues/10556) - Include a `display` field in all GraphQL object types
|
||||||
* [#10595](https://github.com/netbox-community/netbox/issues/10595) - Add GraphQL relationships for additional generic foreign key fields
|
* [#10595](https://github.com/netbox-community/netbox/issues/10595) - Add GraphQL relationships for additional generic foreign key fields
|
||||||
|
* [#10761](https://github.com/netbox-community/netbox/issues/10761) - Enable associating an export template with multiple object types
|
||||||
|
|
||||||
### Plugins API
|
### Plugins API
|
||||||
|
|
||||||
@ -61,6 +62,8 @@ A new `PluginMenu` class has been introduced, which enables a plugin to inject a
|
|||||||
* Added optional `weight` and `weight_unit` fields
|
* Added optional `weight` and `weight_unit` fields
|
||||||
* extras.CustomLink
|
* extras.CustomLink
|
||||||
* Renamed `content_type` field to `content_types`
|
* Renamed `content_type` field to `content_types`
|
||||||
|
* extras.ExportTemplate
|
||||||
|
* Renamed `content_type` field to `content_types`
|
||||||
* ipam.FHRPGroup
|
* ipam.FHRPGroup
|
||||||
* Added optional `name` field
|
* Added optional `name` field
|
||||||
|
|
||||||
|
@ -136,14 +136,15 @@ class CustomLinkSerializer(ValidatedModelSerializer):
|
|||||||
|
|
||||||
class ExportTemplateSerializer(ValidatedModelSerializer):
|
class ExportTemplateSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='extras-api:exporttemplate-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='extras-api:exporttemplate-detail')
|
||||||
content_type = ContentTypeField(
|
content_types = ContentTypeField(
|
||||||
queryset=ContentType.objects.filter(FeatureQuery('export_templates').get_query()),
|
queryset=ContentType.objects.filter(FeatureQuery('export_templates').get_query()),
|
||||||
|
many=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExportTemplate
|
model = ExportTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'content_type', 'name', 'description', 'template_code', 'mime_type',
|
'id', 'url', 'display', 'content_types', 'name', 'description', 'template_code', 'mime_type',
|
||||||
'file_extension', 'as_attachment', 'created', 'last_updated',
|
'file_extension', 'as_attachment', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -120,10 +120,14 @@ class ExportTemplateFilterSet(BaseFilterSet):
|
|||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label='Search',
|
||||||
)
|
)
|
||||||
|
content_type_id = MultiValueNumberFilter(
|
||||||
|
field_name='content_types__id'
|
||||||
|
)
|
||||||
|
content_types = ContentTypeFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExportTemplate
|
model = ExportTemplate
|
||||||
fields = ['id', 'content_type', 'name', 'description']
|
fields = ['id', 'content_types', 'name', 'description']
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
if not value.strip():
|
if not value.strip():
|
||||||
|
@ -76,11 +76,6 @@ class ExportTemplateBulkEditForm(BulkEditForm):
|
|||||||
queryset=ExportTemplate.objects.all(),
|
queryset=ExportTemplate.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
)
|
)
|
||||||
content_type = ContentTypeChoiceField(
|
|
||||||
queryset=ContentType.objects.all(),
|
|
||||||
limit_choices_to=FeatureQuery('export_templates'),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
required=False
|
required=False
|
||||||
|
@ -68,16 +68,16 @@ class CustomLinkCSVForm(CSVModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class ExportTemplateCSVForm(CSVModelForm):
|
class ExportTemplateCSVForm(CSVModelForm):
|
||||||
content_type = CSVContentTypeField(
|
content_types = CSVMultipleContentTypeField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('export_templates'),
|
limit_choices_to=FeatureQuery('export_templates'),
|
||||||
help_text="Assigned object type"
|
help_text="One or more assigned object types"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExportTemplate
|
model = ExportTemplate
|
||||||
fields = (
|
fields = (
|
||||||
'name', 'content_type', 'description', 'mime_type', 'file_extension', 'as_attachment', 'template_code',
|
'name', 'content_types', 'description', 'mime_type', 'file_extension', 'as_attachment', 'template_code',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,9 +148,9 @@ class CustomLinkFilterForm(FilterForm):
|
|||||||
class ExportTemplateFilterForm(FilterForm):
|
class ExportTemplateFilterForm(FilterForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q',)),
|
(None, ('q',)),
|
||||||
('Attributes', ('content_type', 'mime_type', 'file_extension', 'as_attachment')),
|
('Attributes', ('content_types', 'mime_type', 'file_extension', 'as_attachment')),
|
||||||
)
|
)
|
||||||
content_type = ContentTypeChoiceField(
|
content_types = ContentTypeMultipleChoiceField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('export_templates'),
|
limit_choices_to=FeatureQuery('export_templates'),
|
||||||
required=False
|
required=False
|
||||||
|
@ -89,13 +89,13 @@ class CustomLinkForm(BootstrapMixin, forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class ExportTemplateForm(BootstrapMixin, forms.ModelForm):
|
class ExportTemplateForm(BootstrapMixin, forms.ModelForm):
|
||||||
content_type = ContentTypeChoiceField(
|
content_types = ContentTypeMultipleChoiceField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('export_templates')
|
limit_choices_to=FeatureQuery('export_templates')
|
||||||
)
|
)
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Export Template', ('name', 'content_type', 'description')),
|
('Export Template', ('name', 'content_types', 'description')),
|
||||||
('Template', ('template_code',)),
|
('Template', ('template_code',)),
|
||||||
('Rendering', ('mime_type', 'file_extension', 'as_attachment')),
|
('Rendering', ('mime_type', 'file_extension', 'as_attachment')),
|
||||||
)
|
)
|
||||||
|
@ -43,7 +43,7 @@ class ExportTemplateType(ObjectType):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.ExportTemplate
|
model = models.ExportTemplate
|
||||||
fields = '__all__'
|
exclude = ('content_types', )
|
||||||
filterset_class = filtersets.ExportTemplateFilterSet
|
filterset_class = filtersets.ExportTemplateFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
def copy_content_types(apps, schema_editor):
|
||||||
|
ExportTemplate = apps.get_model('extras', 'ExportTemplate')
|
||||||
|
|
||||||
|
for et in ExportTemplate.objects.all():
|
||||||
|
et.content_types.set([et.content_type])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('contenttypes', '0002_remove_content_type_name'),
|
||||||
|
('extras', '0081_customlink_content_types'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='exporttemplate',
|
||||||
|
name='content_types',
|
||||||
|
field=models.ManyToManyField(related_name='export_templates', to='contenttypes.contenttype'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(
|
||||||
|
code=copy_content_types,
|
||||||
|
reverse_code=migrations.RunPython.noop
|
||||||
|
),
|
||||||
|
migrations.RemoveConstraint(
|
||||||
|
model_name='exporttemplate',
|
||||||
|
name='extras_exporttemplate_unique_content_type_name',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='exporttemplate',
|
||||||
|
name='content_type',
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='exporttemplate',
|
||||||
|
options={'ordering': ('name',)},
|
||||||
|
),
|
||||||
|
]
|
@ -268,10 +268,10 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, WebhooksMixin, ChangeLogged
|
|||||||
|
|
||||||
|
|
||||||
class ExportTemplate(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel):
|
class ExportTemplate(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel):
|
||||||
content_type = models.ForeignKey(
|
content_types = models.ManyToManyField(
|
||||||
to=ContentType,
|
to=ContentType,
|
||||||
on_delete=models.CASCADE,
|
related_name='export_templates',
|
||||||
limit_choices_to=FeatureQuery('export_templates')
|
help_text='The object type(s) to which this template applies.'
|
||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100
|
max_length=100
|
||||||
@ -301,16 +301,10 @@ class ExportTemplate(ExportTemplatesMixin, WebhooksMixin, ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['content_type', 'name']
|
ordering = ('name',)
|
||||||
constraints = (
|
|
||||||
models.UniqueConstraint(
|
|
||||||
fields=('content_type', 'name'),
|
|
||||||
name='%(app_label)s_%(class)s_unique_content_type_name'
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.content_type}: {self.name}"
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('extras:exporttemplate', args=[self.pk])
|
return reverse('extras:exporttemplate', args=[self.pk])
|
||||||
|
@ -197,17 +197,17 @@ class ExportTemplateTest(APIViewTestCases.APIViewTestCase):
|
|||||||
brief_fields = ['display', 'id', 'name', 'url']
|
brief_fields = ['display', 'id', 'name', 'url']
|
||||||
create_data = [
|
create_data = [
|
||||||
{
|
{
|
||||||
'content_type': 'dcim.device',
|
'content_types': ['dcim.device'],
|
||||||
'name': 'Test Export Template 4',
|
'name': 'Test Export Template 4',
|
||||||
'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
|
'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'content_type': 'dcim.device',
|
'content_types': ['dcim.device'],
|
||||||
'name': 'Test Export Template 5',
|
'name': 'Test Export Template 5',
|
||||||
'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
|
'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'content_type': 'dcim.device',
|
'content_types': ['dcim.device'],
|
||||||
'name': 'Test Export Template 6',
|
'name': 'Test Export Template 6',
|
||||||
'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
|
'template_code': '{% for obj in queryset %}{{ obj.name }}\n{% endfor %}',
|
||||||
},
|
},
|
||||||
@ -218,26 +218,23 @@ class ExportTemplateTest(APIViewTestCases.APIViewTestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
ct = ContentType.objects.get_for_model(Device)
|
|
||||||
|
|
||||||
export_templates = (
|
export_templates = (
|
||||||
ExportTemplate(
|
ExportTemplate(
|
||||||
content_type=ct,
|
|
||||||
name='Export Template 1',
|
name='Export Template 1',
|
||||||
template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
|
template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
|
||||||
),
|
),
|
||||||
ExportTemplate(
|
ExportTemplate(
|
||||||
content_type=ct,
|
|
||||||
name='Export Template 2',
|
name='Export Template 2',
|
||||||
template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
|
template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
|
||||||
),
|
),
|
||||||
ExportTemplate(
|
ExportTemplate(
|
||||||
content_type=ct,
|
|
||||||
name='Export Template 3',
|
name='Export Template 3',
|
||||||
template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
|
template_code='{% for obj in queryset %}{{ obj.name }}\n{% endfor %}'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
ExportTemplate.objects.bulk_create(export_templates)
|
ExportTemplate.objects.bulk_create(export_templates)
|
||||||
|
for et in export_templates:
|
||||||
|
et.content_types.set([ContentType.objects.get_for_model(Device)])
|
||||||
|
|
||||||
|
|
||||||
class TagTest(APIViewTestCases.APIViewTestCase):
|
class TagTest(APIViewTestCases.APIViewTestCase):
|
||||||
|
@ -228,22 +228,25 @@ class ExportTemplateTestCase(TestCase, BaseFilterSetTests):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
content_types = ContentType.objects.filter(model__in=['site', 'rack', 'device'])
|
content_types = ContentType.objects.filter(model__in=['site', 'rack', 'device'])
|
||||||
|
|
||||||
export_templates = (
|
export_templates = (
|
||||||
ExportTemplate(name='Export Template 1', content_type=content_types[0], template_code='TESTING', description='foobar1'),
|
ExportTemplate(name='Export Template 1', template_code='TESTING', description='foobar1'),
|
||||||
ExportTemplate(name='Export Template 2', content_type=content_types[1], template_code='TESTING', description='foobar2'),
|
ExportTemplate(name='Export Template 2', template_code='TESTING', description='foobar2'),
|
||||||
ExportTemplate(name='Export Template 3', content_type=content_types[2], template_code='TESTING'),
|
ExportTemplate(name='Export Template 3', template_code='TESTING'),
|
||||||
)
|
)
|
||||||
ExportTemplate.objects.bulk_create(export_templates)
|
ExportTemplate.objects.bulk_create(export_templates)
|
||||||
|
for i, et in enumerate(export_templates):
|
||||||
|
et.content_types.set([content_types[i]])
|
||||||
|
|
||||||
def test_name(self):
|
def test_name(self):
|
||||||
params = {'name': ['Export Template 1', 'Export Template 2']}
|
params = {'name': ['Export Template 1', 'Export Template 2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_content_type(self):
|
def test_content_types(self):
|
||||||
params = {'content_type': ContentType.objects.get(model='site').pk}
|
params = {'content_types': 'dcim.site'}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||||
|
params = {'content_type_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_description(self):
|
def test_description(self):
|
||||||
|
@ -98,23 +98,26 @@ class ExportTemplateTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
site_ct = ContentType.objects.get_for_model(Site)
|
site_ct = ContentType.objects.get_for_model(Site)
|
||||||
TEMPLATE_CODE = """{% for object in queryset %}{{ object }}{% endfor %}"""
|
TEMPLATE_CODE = """{% for object in queryset %}{{ object }}{% endfor %}"""
|
||||||
ExportTemplate.objects.bulk_create((
|
|
||||||
ExportTemplate(name='Export Template 1', content_type=site_ct, template_code=TEMPLATE_CODE),
|
export_templates = (
|
||||||
ExportTemplate(name='Export Template 2', content_type=site_ct, template_code=TEMPLATE_CODE),
|
ExportTemplate(name='Export Template 1', template_code=TEMPLATE_CODE),
|
||||||
ExportTemplate(name='Export Template 3', content_type=site_ct, template_code=TEMPLATE_CODE),
|
ExportTemplate(name='Export Template 2', template_code=TEMPLATE_CODE),
|
||||||
))
|
ExportTemplate(name='Export Template 3', template_code=TEMPLATE_CODE),
|
||||||
|
)
|
||||||
|
ExportTemplate.objects.bulk_create(export_templates)
|
||||||
|
for et in export_templates:
|
||||||
|
et.content_types.set([site_ct])
|
||||||
|
|
||||||
cls.form_data = {
|
cls.form_data = {
|
||||||
'name': 'Export Template X',
|
'name': 'Export Template X',
|
||||||
'content_type': site_ct.pk,
|
'content_types': [site_ct.pk],
|
||||||
'template_code': TEMPLATE_CODE,
|
'template_code': TEMPLATE_CODE,
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"name,content_type,template_code",
|
"name,content_types,template_code",
|
||||||
f"Export Template 4,dcim.site,{TEMPLATE_CODE}",
|
f"Export Template 4,dcim.site,{TEMPLATE_CODE}",
|
||||||
f"Export Template 5,dcim.site,{TEMPLATE_CODE}",
|
f"Export Template 5,dcim.site,{TEMPLATE_CODE}",
|
||||||
f"Export Template 6,dcim.site,{TEMPLATE_CODE}",
|
f"Export Template 6,dcim.site,{TEMPLATE_CODE}",
|
||||||
|
@ -142,7 +142,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
|
|
||||||
# Render an ExportTemplate
|
# Render an ExportTemplate
|
||||||
elif request.GET['export']:
|
elif request.GET['export']:
|
||||||
template = get_object_or_404(ExportTemplate, content_type=content_type, name=request.GET['export'])
|
template = get_object_or_404(ExportTemplate, content_types=content_type, name=request.GET['export'])
|
||||||
return self.export_template(template, request)
|
return self.export_template(template, request)
|
||||||
|
|
||||||
# Check for YAML export support on the model
|
# Check for YAML export support on the model
|
||||||
|
@ -18,10 +18,6 @@
|
|||||||
</h5>
|
</h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover attr-table">
|
<table class="table table-hover attr-table">
|
||||||
<tr>
|
|
||||||
<th scope="row">Content Type</th>
|
|
||||||
<td>{{ object.content_type }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Name</th>
|
<th scope="row">Name</th>
|
||||||
<td>{{ object.name }}</td>
|
<td>{{ object.name }}</td>
|
||||||
@ -45,6 +41,18 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">Assigned Models</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-hover attr-table">
|
||||||
|
{% for ct in object.content_types.all %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ ct }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% plugin_left_page object %}
|
{% plugin_left_page object %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col col-md-7">
|
<div class="col col-md-7">
|
||||||
|
@ -83,7 +83,7 @@ def export_button(context, model):
|
|||||||
data_format = 'YAML' if hasattr(content_type.model_class(), 'to_yaml') else 'CSV'
|
data_format = 'YAML' if hasattr(content_type.model_class(), 'to_yaml') else 'CSV'
|
||||||
|
|
||||||
# Retrieve all export templates for this model
|
# Retrieve all export templates for this model
|
||||||
export_templates = ExportTemplate.objects.restrict(user, 'view').filter(content_type=content_type)
|
export_templates = ExportTemplate.objects.restrict(user, 'view').filter(content_types=content_type)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'perms': context['perms'],
|
'perms': context['perms'],
|
||||||
|
Reference in New Issue
Block a user