Merge branch 'develop-2.9' into 2006-scripts-reports-background

This commit is contained in:
Jeremy Stretch
2020-07-02 11:00:59 -04:00
committed by GitHub
144 changed files with 4747 additions and 3590 deletions

View File

@@ -110,7 +110,7 @@ class ExportTemplateViewSet(ModelViewSet):
#
class TagViewSet(ModelViewSet):
queryset = Tag.restricted.annotate(
queryset = Tag.objects.annotate(
tagged_items=Count('extras_taggeditem_items', distinct=True)
)
serializer_class = serializers.TagSerializer

View File

@@ -6,6 +6,7 @@ from django import get_version
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
APPS = ['circuits', 'dcim', 'extras', 'ipam', 'secrets', 'tenancy', 'users', 'virtualization']
@@ -52,6 +53,7 @@ class Command(BaseCommand):
pass
# Additional objects to include
namespace['ContentType'] = ContentType
namespace['User'] = User
# Load convenience commands

View File

@@ -540,7 +540,7 @@ class ConfigContextModel(models.Model):
# Compile all config data, overwriting lower-weight values with higher-weight values where a collision occurs
data = OrderedDict()
for context in ConfigContext.objects.get_for_object(self):
for context in ConfigContext.objects.unrestricted().get_for_object(self):
data = deepmerge(data, context.data)
# If the object has local config context data defined, merge it last

View File

@@ -22,14 +22,10 @@ class Tag(TagBase, ChangeLoggedModel):
blank=True,
)
objects = models.Manager()
restricted = RestrictedQuerySet.as_manager()
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'color', 'description']
def get_absolute_url(self):
return reverse('extras:tag', args=[self.slug])
def slugify(self, tag, i=None):
# Allow Unicode in Tag slugs (avoids empty slugs for Tags with all-Unicode names)
slug = slugify(tag, allow_unicode=True)

View File

@@ -173,7 +173,7 @@ class ChoiceVar(ScriptVariable):
class ObjectVar(ScriptVariable):
"""
NetBox object representation. The provided QuerySet will determine the choices available.
A single object within NetBox.
"""
form_field = DynamicModelChoiceField

View File

@@ -1,21 +1,9 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
from utilities.tables import BaseTable, BooleanColumn, ButtonsColumn, ColorColumn, ToggleColumn
from .models import ConfigContext, ObjectChange, JobResult, Tag, TaggedItem
TAG_ACTIONS = """
<a href="{% url 'extras:tag_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Change log">
<i class="fa fa-history"></i>
</a>
{% if perms.taggit.change_tag %}
<a href="{% url 'extras:tag_edit' slug=record.slug %}" class="btn btn-xs btn-warning"><i class="glyphicon glyphicon-pencil" aria-hidden="true"></i></a>
{% endif %}
{% if perms.taggit.delete_tag %}
<a href="{% url 'extras:tag_delete' slug=record.slug %}" class="btn btn-xs btn-danger"><i class="glyphicon glyphicon-trash" aria-hidden="true"></i></a>
{% endif %}
"""
TAGGED_ITEM = """
{% if value.get_absolute_url %}
<a href="{{ value.get_absolute_url }}">{{ value }}</a>
@@ -64,16 +52,8 @@ OBJECTCHANGE_REQUEST_ID = """
class TagTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn(
viewname='extras:tag',
args=[Accessor('slug')]
)
actions = tables.TemplateColumn(
template_code=TAG_ACTIONS,
attrs={'td': {'class': 'text-right noprint'}},
verbose_name=''
)
color = ColorColumn()
actions = ButtonsColumn(Tag, pk_field='slug')
class Meta(BaseTable.Meta):
model = Tag

View File

@@ -10,7 +10,7 @@ from extras.models import ConfigContext, ObjectChange, Tag
from utilities.testing import ViewTestCases, TestCase
class TagTestCase(ViewTestCases.PrimaryObjectViewTestCase):
class TagTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
model = Tag
@classmethod

View File

@@ -13,7 +13,6 @@ urlpatterns = [
path('tags/import/', views.TagBulkImportView.as_view(), name='tag_import'),
path('tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'),
path('tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
path('tags/<str:slug>/', views.TagView.as_view(), name='tag'),
path('tags/<str:slug>/edit/', views.TagEditView.as_view(), name='tag_edit'),
path('tags/<str:slug>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
path('tags/<str:slug>/changelog/', views.ObjectChangeLogView.as_view(), name='tag_changelog', kwargs={'model': Tag}),

View File

@@ -4,13 +4,15 @@ from django import template
from django.conf import settings
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType
from django.db.models import Count, Q
from django.db.models import Count, Prefetch, Q
from django.http import Http404, HttpResponseForbidden
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.safestring import mark_safe
from django.views.generic import View
from django_tables2 import RequestConfig
from dcim.models import DeviceRole, Platform, Region, Site
from tenancy.models import Tenant, TenantGroup
from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator
from utilities.utils import copy_safe_request, shallow_compare_dict
@@ -18,6 +20,7 @@ from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
ContentTypePermissionRequiredMixin,
)
from virtualization.models import Cluster, ClusterGroup
from . import filters, forms, tables
from .choices import JobResultStatusChoices
from .models import ConfigContext, ImageAttachment, ObjectChange, Report, JobResult, Script, Tag, TaggedItem
@@ -30,7 +33,7 @@ from .scripts import get_scripts, run_script
#
class TagListView(ObjectListView):
queryset = Tag.restricted.annotate(
queryset = Tag.objects.annotate(
items=Count('extras_taggeditem_items', distinct=True)
).order_by(
'name'
@@ -40,71 +43,39 @@ class TagListView(ObjectListView):
table = tables.TagTable
class TagView(ObjectView):
queryset = Tag.restricted.all()
def get(self, request, slug):
tag = get_object_or_404(self.queryset, slug=slug)
tagged_items = TaggedItem.objects.filter(
tag=tag
).prefetch_related(
'content_type', 'content_object'
)
# Generate a table of all items tagged with this Tag
items_table = tables.TaggedItemTable(tagged_items)
paginate = {
'paginator_class': EnhancedPaginator,
'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
}
RequestConfig(request, paginate).configure(items_table)
return render(request, 'extras/tag.html', {
'tag': tag,
'items_count': tagged_items.count(),
'items_table': items_table,
})
class TagEditView(ObjectEditView):
queryset = Tag.restricted.all()
queryset = Tag.objects.all()
model_form = forms.TagForm
default_return_url = 'extras:tag_list'
template_name = 'extras/tag_edit.html'
class TagDeleteView(ObjectDeleteView):
queryset = Tag.restricted.all()
default_return_url = 'extras:tag_list'
queryset = Tag.objects.all()
class TagBulkImportView(BulkImportView):
queryset = Tag.restricted.all()
queryset = Tag.objects.all()
model_form = forms.TagCSVForm
table = tables.TagTable
default_return_url = 'extras:tag_list'
class TagBulkEditView(BulkEditView):
queryset = Tag.restricted.annotate(
queryset = Tag.objects.annotate(
items=Count('extras_taggeditem_items', distinct=True)
).order_by(
'name'
)
table = tables.TagTable
form = forms.TagBulkEditForm
default_return_url = 'extras:tag_list'
class TagBulkDeleteView(BulkDeleteView):
queryset = Tag.restricted.annotate(
queryset = Tag.objects.annotate(
items=Count('extras_taggeditem_items')
).order_by(
'name'
)
table = tables.TagTable
default_return_url = 'extras:tag_list'
#
@@ -123,6 +94,18 @@ class ConfigContextView(ObjectView):
queryset = ConfigContext.objects.all()
def get(self, request, pk):
# Extend queryset to prefetch related objects
self.queryset = self.queryset.prefetch_related(
Prefetch('regions', queryset=Region.objects.restrict(request.user)),
Prefetch('sites', queryset=Site.objects.restrict(request.user)),
Prefetch('roles', queryset=DeviceRole.objects.restrict(request.user)),
Prefetch('platforms', queryset=Platform.objects.restrict(request.user)),
Prefetch('clusters', queryset=Cluster.objects.restrict(request.user)),
Prefetch('cluster_groups', queryset=ClusterGroup.objects.restrict(request.user)),
Prefetch('tenants', queryset=Tenant.objects.restrict(request.user)),
Prefetch('tenant_groups', queryset=TenantGroup.objects.restrict(request.user)),
)
configcontext = get_object_or_404(self.queryset, pk=pk)
# Determine user's preferred output format
@@ -144,7 +127,6 @@ class ConfigContextView(ObjectView):
class ConfigContextEditView(ObjectEditView):
queryset = ConfigContext.objects.all()
model_form = forms.ConfigContextForm
default_return_url = 'extras:configcontext_list'
template_name = 'extras/configcontext_edit.html'
@@ -153,18 +135,15 @@ class ConfigContextBulkEditView(BulkEditView):
filterset = filters.ConfigContextFilterSet
table = tables.ConfigContextTable
form = forms.ConfigContextBulkEditForm
default_return_url = 'extras:configcontext_list'
class ConfigContextDeleteView(ObjectDeleteView):
queryset = ConfigContext.objects.all()
default_return_url = 'extras:configcontext_list'
class ConfigContextBulkDeleteView(BulkDeleteView):
queryset = ConfigContext.objects.all()
table = tables.ConfigContextTable
default_return_url = 'extras:configcontext_list'
class ObjectConfigContextView(ObjectView):
@@ -264,9 +243,11 @@ class ObjectChangeLogView(View):
def get(self, request, model, **kwargs):
# Get object my model and kwargs (e.g. slug='foo')
queryset = model.objects.restrict(request.user, 'view')
obj = get_object_or_404(queryset, **kwargs)
# Handle QuerySet restriction of parent object if needed
if hasattr(model.objects, 'restrict'):
obj = get_object_or_404(model.objects.restrict(request.user, 'view'), **kwargs)
else:
obj = get_object_or_404(model, **kwargs)
# Gather all changes for this object (and its related objects)
content_type = ContentType.objects.get_for_model(model)
@@ -299,6 +280,7 @@ class ObjectChangeLogView(View):
return render(request, 'extras/object_changelog.html', {
object_var: obj,
'instance': obj, # We'll eventually standardize on 'instance` for the object variable name
'table': objectchanges_table,
'base_template': base_template,
'active_tab': 'changelog',