Beispiel #1
0
def serialize_object(obj, extra=None, exclude=None):
    """
    Return a generic JSON representation of an object using Django's built-in serializer. (This is used for things like
    change logging, not the REST API.) Optionally include a dictionary to supplement the object data. A list of keys
    can be provided to exclude them from the returned dictionary. Private fields (prefaced with an underscore) are
    implicitly excluded.
    """
    json_str = serialize('json', [obj])
    data = json.loads(json_str)[0]['fields']

    # Include custom_field_data as "custom_fields"
    if hasattr(obj, 'custom_field_data'):
        data['custom_fields'] = data.pop('custom_field_data')

    # Include any tags. Check for tags cached on the instance; fall back to using the manager.
    if is_taggable(obj):
        tags = getattr(obj, '_tags', obj.tags.all())
        data['tags'] = [tag.name for tag in tags]

    # Append any extra data
    if extra is not None:
        data.update(extra)

    # Copy keys to list to avoid 'dictionary changed size during iteration' exception
    for key in list(data):
        # Private fields shouldn't be logged in the object change
        if isinstance(key, str) and key.startswith('_'):
            data.pop(key)

        # Explicitly excluded keys
        if isinstance(exclude, (list, tuple)) and key in exclude:
            data.pop(key)

    return data
Beispiel #2
0
def prepare_cloned_fields(instance):
    """
    Compile an object's `clone_fields` list into a string of URL query parameters. Tags are automatically cloned where
    applicable.
    """
    params = {}
    for field_name in getattr(instance, 'clone_fields', []):
        field = instance._meta.get_field(field_name)
        field_value = field.value_from_object(instance)

        # Swap out False with URL-friendly value
        if field_value is False:
            field_value = ''

        # Omit empty values
        if field_value not in (None, ''):
            params[field_name] = field_value

        # Copy tags
        if is_taggable(instance):
            params['tags'] = ','.join([t.name for t in instance.tags.all()])

    # Concatenate parameters into a URL query string
    param_string = '&'.join(['{}={}'.format(k, v) for k, v in params.items()])

    return param_string
Beispiel #3
0
def prepare_cloned_fields(instance):
    """
    Compile an object's `clone_fields` list into a string of URL query parameters. Tags are automatically cloned where
    applicable.
    """
    params = []
    for field_name in getattr(instance, 'clone_fields', []):
        field = instance._meta.get_field(field_name)
        field_value = field.value_from_object(instance)

        # Swap out False with URL-friendly value
        if field_value is False:
            field_value = ''

        # Omit empty values
        if field_value not in (None, ''):
            params.append((field_name, field_value))

    # Copy tags
    if is_taggable(instance):
        for tag in instance.tags.all():
            params.append(('tags', tag.pk))

    # Concatenate parameters into a URL query string
    param_string = '&'.join([f'{k}={v}' for k, v in params])

    return param_string
Beispiel #4
0
def prepare_cloned_fields(instance):
    """
    Compile an object's `clone_fields` list into a string of URL query parameters. Tags are automatically cloned where
    applicable.
    """
    params = []
    for field_name in getattr(instance, 'clone_fields', []):
        field = instance._meta.get_field(field_name)
        field_value = field.value_from_object(instance)

        # Pass False as null for boolean fields
        if field_value is False:
            params.append((field_name, ''))

        # Omit empty values
        elif field_value not in (None, ''):
            params.append((field_name, field_value))

    # Copy tags
    if is_taggable(instance):
        for tag in instance.tags.all():
            params.append(('tags', tag.pk))

    # Return a QueryDict with the parameters
    return QueryDict('&'.join([f'{k}={v}' for k, v in params]), mutable=True)
Beispiel #5
0
def handle_deleted_object(sender, instance, **kwargs):
    """
    Fires when an object is deleted.
    """
    # Cache custom fields prior to copying the instance
    if hasattr(instance, 'cache_custom_fields'):
        instance.cache_custom_fields()

    # Create a copy of the object being deleted
    copy = deepcopy(instance)

    # Preserve tags
    if is_taggable(instance):
        copy.tags = DummyQuerySet(instance.tags.all())

    # Queue the copy of the object for processing once the request completes
    _thread_locals.changed_objects.append(
        (copy, ObjectChangeActionChoices.ACTION_DELETE))
Beispiel #6
0
def serialize_object(obj, extra=None):
    """
    Return a generic JSON representation of an object using Django's built-in serializer. (This is used for things like
    change logging, not the REST API.) Optionally include a dictionary to supplement the object data.
    """
    json_str = serialize('json', [obj])
    data = json.loads(json_str)[0]['fields']

    # Include any custom fields
    if hasattr(obj, 'get_custom_fields'):
        data['custom_fields'] = {
            field: str(value) for field, value in obj.cf.items()
        }

    # Include any tags
    if is_taggable(obj):
        data['tags'] = [tag.name for tag in obj.tags.all()]

    # Append any extra data
    if extra is not None:
        data.update(extra)

    return data
Beispiel #7
0
    def get(self, request):

        model = self.queryset.model
        content_type = ContentType.objects.get_for_model(model)

        if self.filterset:
            self.queryset = self.filterset(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=content_type, name=request.GET.get('export'))
            queryset = CustomFieldQueryset(self.queryset, custom_fields) if custom_fields else self.queryset
            try:
                return et.render_to_response(queryset)
            except Exception as e:
                messages.error(
                    request,
                    "There was an error rendering the selected export template ({}): {}".format(
                        et.name, e
                    )
                )

        # Check for YAML export support
        elif 'export' in request.GET and hasattr(model, 'to_yaml'):
            response = HttpResponse(self.queryset_to_yaml(), content_type='text/yaml')
            filename = 'netbox_{}.yaml'.format(self.queryset.model._meta.verbose_name_plural)
            response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
            return response

        # Fall back to built-in CSV formatting if export requested but no template specified
        elif 'export' in request.GET and hasattr(model, 'to_csv'):
            response = HttpResponse(self.queryset_to_csv(), content_type='text/csv')
            filename = 'netbox_{}.csv'.format(self.queryset.model._meta.verbose_name_plural)
            response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
            return response

        # Provide a hook to tweak the queryset based on the request immediately prior to rendering the object list
        self.queryset = self.alter_queryset(request)

        # Compile user model permissions for access from within the template
        perm_base_name = '{}.{{}}_{}'.format(model._meta.app_label, model._meta.model_name)
        permissions = {p: request.user.has_perm(perm_base_name.format(p)) for p in ['add', 'change', 'delete']}

        # Construct the table based on the user's permissions
        table = self.table(self.queryset)
        if 'pk' in table.base_columns and (permissions['change'] or permissions['delete']):
            table.columns.show('pk')

        # Construct queryset for tags list
        if is_taggable(model):
            tags = model.tags.annotate(count=Count('extras_taggeditem_items')).order_by('name')
        else:
            tags = None

        # Apply the request context
        paginate = {
            'paginator_class': EnhancedPaginator,
            'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
        }
        RequestConfig(request, paginate).configure(table)

        context = {
            'content_type': content_type,
            'table': table,
            'permissions': permissions,
            'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
            'tags': tags,
        }
        context.update(self.extra_context())

        return render(request, self.template_name, context)