1
0
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:
Jeremy Stretch
2016-09-19 16:11:37 -04:00
parent d0c92b4f8a
commit b10e29aaac
9 changed files with 45 additions and 11 deletions

View File

@ -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

View File

@ -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)

View File

@ -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">

View File

@ -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 %}

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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'