mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #561: Make custom fields accessible from within export templates
This commit is contained in:
@ -35,6 +35,8 @@ Each export template is associated with a certain type of object. For instance,
|
|||||||
|
|
||||||
Export templates are written in [Django's template language](https://docs.djangoproject.com/en/1.9/ref/templates/language/), which is very similar to Jinja2. The list of objects returned from the database is stored in the `queryset` variable. Typically, you'll want to iterate through this list using a for loop.
|
Export templates are written in [Django's template language](https://docs.djangoproject.com/en/1.9/ref/templates/language/), which is very similar to Jinja2. The list of objects returned from the database is stored in the `queryset` variable. Typically, you'll want to iterate through this list using a for loop.
|
||||||
|
|
||||||
|
To access custom fields of an object within a template, use the `cf` attribute. For example, `{{ obj.cf.color }}` will return the value (if any) for a custom field named `color` on `obj`.
|
||||||
|
|
||||||
A MIME type and file extension can optionally be defined for each export template. The default MIME type is `text/plain`.
|
A MIME type and file extension can optionally be defined for each export template. The default MIME type is `text/plain`.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@ -67,7 +67,18 @@ ACTION_CHOICES = (
|
|||||||
|
|
||||||
class CustomFieldModel(object):
|
class CustomFieldModel(object):
|
||||||
|
|
||||||
def custom_fields(self):
|
def cf(self):
|
||||||
|
"""
|
||||||
|
Name-based CustomFieldValue accessor for use in templates
|
||||||
|
"""
|
||||||
|
if not hasattr(self, 'custom_fields'):
|
||||||
|
return dict()
|
||||||
|
return {field.name: value for field, value in self.custom_fields.items()}
|
||||||
|
|
||||||
|
def get_custom_fields(self):
|
||||||
|
"""
|
||||||
|
Return a dictionary of custom fields for a single object in the form {<field>: value}.
|
||||||
|
"""
|
||||||
|
|
||||||
# Find all custom fields applicable to this type of object
|
# Find all custom fields applicable to this type of object
|
||||||
content_type = ContentType.objects.get_for_model(self)
|
content_type = ContentType.objects.get_for_model(self)
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% with provider.custom_fields as custom_fields %}
|
{% with provider.get_custom_fields as custom_fields %}
|
||||||
{% include 'inc/custom_fields_panel.html' %}
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -144,7 +144,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% with device.custom_fields as custom_fields %}
|
{% with device.get_custom_fields as custom_fields %}
|
||||||
{% include 'inc/custom_fields_panel.html' %}
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% with rack.custom_fields as custom_fields %}
|
{% with rack.get_custom_fields as custom_fields %}
|
||||||
{% include 'inc/custom_fields_panel.html' %}
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% with site.custom_fields as custom_fields %}
|
{% with site.get_custom_fields as custom_fields %}
|
||||||
{% include 'inc/custom_fields_panel.html' %}
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
{% include 'inc/created_updated.html' with obj=aggregate %}
|
{% include 'inc/created_updated.html' with obj=aggregate %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% with aggregate.custom_fields as custom_fields %}
|
{% with aggregate.get_custom_fields as custom_fields %}
|
||||||
{% include 'inc/custom_fields_panel.html' %}
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% with tenant.custom_fields as custom_fields %}
|
{% with tenant.get_custom_fields as custom_fields %}
|
||||||
{% include 'inc/custom_fields_panel.html' %}
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from collections import OrderedDict
|
||||||
from django_tables2 import RequestConfig
|
from django_tables2 import RequestConfig
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@ -14,13 +15,26 @@ from django.utils.http import is_safe_url
|
|||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from extras.forms import CustomFieldForm
|
from extras.forms import CustomFieldForm
|
||||||
from extras.models import CustomFieldValue, ExportTemplate, UserAction
|
from extras.models import CustomField, CustomFieldValue, ExportTemplate, UserAction
|
||||||
|
|
||||||
from .error_handlers import handle_protectederror
|
from .error_handlers import handle_protectederror
|
||||||
from .forms import ConfirmationForm
|
from .forms import ConfirmationForm
|
||||||
from .paginator import EnhancedPaginator
|
from .paginator import EnhancedPaginator
|
||||||
|
|
||||||
|
|
||||||
|
class annotate_custom_fields:
|
||||||
|
|
||||||
|
def __init__(self, queryset, custom_fields):
|
||||||
|
self.queryset = queryset
|
||||||
|
self.custom_fields = custom_fields
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for obj in self.queryset:
|
||||||
|
values_dict = {cfv.field_id: cfv.value for cfv in obj.custom_field_values.all()}
|
||||||
|
obj.custom_fields = OrderedDict([(field, values_dict.get(field.pk)) for field in self.custom_fields])
|
||||||
|
yield obj
|
||||||
|
|
||||||
|
|
||||||
class ObjectListView(View):
|
class ObjectListView(View):
|
||||||
queryset = None
|
queryset = None
|
||||||
filter = None
|
filter = None
|
||||||
@ -38,19 +52,26 @@ class ObjectListView(View):
|
|||||||
if self.filter:
|
if self.filter:
|
||||||
self.queryset = self.filter(request.GET, self.queryset).qs
|
self.queryset = self.filter(request.GET, self.queryset).qs
|
||||||
|
|
||||||
|
# If this type of object has one or more custom fields, prefetch any relevant custom field values
|
||||||
|
custom_fields = CustomField.objects.filter(obj_type=ContentType.objects.get_for_model(model))\
|
||||||
|
.prefetch_related('choices')
|
||||||
|
if custom_fields:
|
||||||
|
self.queryset = self.queryset.prefetch_related('custom_field_values')
|
||||||
|
|
||||||
# Check for export template rendering
|
# Check for export template rendering
|
||||||
if request.GET.get('export'):
|
if request.GET.get('export'):
|
||||||
et = get_object_or_404(ExportTemplate, content_type=object_ct, name=request.GET.get('export'))
|
et = get_object_or_404(ExportTemplate, content_type=object_ct, name=request.GET.get('export'))
|
||||||
|
queryset = annotate_custom_fields(self.queryset, custom_fields) if custom_fields else self.queryset
|
||||||
try:
|
try:
|
||||||
response = et.to_response(context_dict={'queryset': self.queryset.all()},
|
response = et.to_response(context_dict={'queryset': queryset},
|
||||||
filename='netbox_{}'.format(self.queryset.model._meta.verbose_name_plural))
|
filename='netbox_{}'.format(model._meta.verbose_name_plural))
|
||||||
return response
|
return response
|
||||||
except TemplateSyntaxError:
|
except TemplateSyntaxError:
|
||||||
messages.error(request, "There was an error rendering the selected export template ({})."
|
messages.error(request, "There was an error rendering the selected export template ({})."
|
||||||
.format(et.name))
|
.format(et.name))
|
||||||
# Fall back to built-in CSV export
|
# Fall back to built-in CSV export
|
||||||
elif 'export' in request.GET and hasattr(model, 'to_csv'):
|
elif 'export' in request.GET and hasattr(model, 'to_csv'):
|
||||||
output = '\n'.join([obj.to_csv() for obj in self.queryset.all()])
|
output = '\n'.join([obj.to_csv() for obj in self.queryset])
|
||||||
response = HttpResponse(
|
response = HttpResponse(
|
||||||
output,
|
output,
|
||||||
content_type='text/csv'
|
content_type='text/csv'
|
||||||
|
Reference in New Issue
Block a user