Skip to content

Commit

Permalink
Closes #561: Make custom fields accessible from within export templates
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Sep 19, 2016
1 parent d0c92b4 commit b10e29a
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 11 deletions.
2 changes: 2 additions & 0 deletions docs/data-model/extras.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

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`.

## Example
Expand Down
13 changes: 12 additions & 1 deletion netbox/extras/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,18 @@

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
content_type = ContentType.objects.get_for_model(self)
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/circuits/provider.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ <h1>{{ provider }}</h1>
</tr>
</table>
</div>
{% with provider.custom_fields as custom_fields %}
{% with provider.get_custom_fields as custom_fields %}
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
<div class="panel panel-default">
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/dcim/device.html
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
</tr>
</table>
</div>
{% with device.custom_fields as custom_fields %}
{% with device.get_custom_fields as custom_fields %}
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
{% if request.user.is_authenticated %}
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/dcim/rack.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ <h1>Rack {{ rack.name }}</h1>
</tr>
</table>
</div>
{% with rack.custom_fields as custom_fields %}
{% with rack.get_custom_fields as custom_fields %}
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
<div class="panel panel-default">
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/dcim/site.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ <h1>{{ site.name }}</h1>
</tr>
</table>
</div>
{% with site.custom_fields as custom_fields %}
{% with site.get_custom_fields as custom_fields %}
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
<div class="panel panel-default">
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/ipam/aggregate.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ <h1>{{ aggregate }}</h1>
{% include 'inc/created_updated.html' with obj=aggregate %}
</div>
<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' %}
{% endwith %}
</div>
Expand Down
2 changes: 1 addition & 1 deletion netbox/templates/tenancy/tenant.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ <h1>{{ tenant }}</h1>
</tr>
</table>
</div>
{% with tenant.custom_fields as custom_fields %}
{% with tenant.get_custom_fields as custom_fields %}
{% include 'inc/custom_fields_panel.html' %}
{% endwith %}
<div class="panel panel-default">
Expand Down
29 changes: 25 additions & 4 deletions netbox/utilities/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections import OrderedDict
from django_tables2 import RequestConfig

from django.contrib import messages
Expand All @@ -14,13 +15,26 @@
from django.views.generic import View

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 .forms import ConfirmationForm
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):
queryset = None
filter = None
Expand All @@ -38,19 +52,26 @@ def get(self, request, *args, **kwargs):
if self.filter:
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
if 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:
response = et.to_response(context_dict={'queryset': self.queryset.all()},
filename='netbox_{}'.format(self.queryset.model._meta.verbose_name_plural))
response = et.to_response(context_dict={'queryset': queryset},
filename='netbox_{}'.format(model._meta.verbose_name_plural))
return response
except TemplateSyntaxError:
messages.error(request, "There was an error rendering the selected export template ({})."
.format(et.name))
# Fall back to built-in CSV export
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(
output,
content_type='text/csv'
Expand Down

0 comments on commit b10e29a

Please sign in to comment.