mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-19 03:42:25 -06:00
Merge branch 'virtualization' into develop-2.2
This commit is contained in:
@@ -188,6 +188,10 @@ class APISelect(SelectWithDisabled):
|
||||
self.attrs['disabled-indicator'] = disabled_indicator
|
||||
|
||||
|
||||
class APISelectMultiple(APISelect):
|
||||
allow_multiple_selected = True
|
||||
|
||||
|
||||
class Livesearch(forms.TextInput):
|
||||
"""
|
||||
A text widget that carries a few extra bits of data for use in AJAX-powered autocomplete search
|
||||
@@ -386,6 +390,15 @@ class ChainedModelChoiceField(forms.ModelChoiceField):
|
||||
super(ChainedModelChoiceField, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class ChainedModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
||||
"""
|
||||
See ChainedModelChoiceField
|
||||
"""
|
||||
def __init__(self, chains=None, *args, **kwargs):
|
||||
self.chains = chains
|
||||
super(ChainedModelMultipleChoiceField, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class SlugField(forms.SlugField):
|
||||
|
||||
def __init__(self, slug_source='name', *args, **kwargs):
|
||||
@@ -508,6 +521,15 @@ class ConfirmationForm(BootstrapMixin, ReturnURLForm):
|
||||
confirm = forms.BooleanField(required=True, widget=forms.HiddenInput(), initial=True)
|
||||
|
||||
|
||||
class ComponentForm(BootstrapMixin, forms.Form):
|
||||
"""
|
||||
Allow inclusion of the parent Device/VirtualMachine as context for limiting field choices.
|
||||
"""
|
||||
def __init__(self, parent, *args, **kwargs):
|
||||
self.parent = parent
|
||||
super(ComponentForm, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class BulkEditForm(forms.Form):
|
||||
|
||||
def __init__(self, model, *args, **kwargs):
|
||||
|
||||
@@ -46,6 +46,22 @@ def gfm(value):
|
||||
return mark_safe(html)
|
||||
|
||||
|
||||
@register.filter()
|
||||
def model_name(obj):
|
||||
"""
|
||||
Return the name of the model of the given object
|
||||
"""
|
||||
return obj._meta.verbose_name
|
||||
|
||||
|
||||
@register.filter()
|
||||
def model_name_plural(obj):
|
||||
"""
|
||||
Return the plural name of the model of the given object
|
||||
"""
|
||||
return obj._meta.verbose_name_plural
|
||||
|
||||
|
||||
@register.filter()
|
||||
def contains(value, arg):
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
|
||||
from django_tables2 import RequestConfig
|
||||
|
||||
@@ -697,3 +698,91 @@ class BulkDeleteView(View):
|
||||
if self.form:
|
||||
return self.form
|
||||
return BulkDeleteForm
|
||||
|
||||
|
||||
#
|
||||
# Device/VirtualMachine components
|
||||
#
|
||||
|
||||
class ComponentCreateView(View):
|
||||
parent_model = None
|
||||
parent_field = None
|
||||
model = None
|
||||
form = None
|
||||
model_form = None
|
||||
template_name = None
|
||||
|
||||
def get(self, request, pk):
|
||||
|
||||
parent = get_object_or_404(self.parent_model, pk=pk)
|
||||
form = self.form(parent, initial=request.GET)
|
||||
|
||||
return render(request, self.template_name, {
|
||||
'parent': parent,
|
||||
'component_type': self.model._meta.verbose_name,
|
||||
'form': form,
|
||||
'return_url': parent.get_absolute_url(),
|
||||
})
|
||||
|
||||
def post(self, request, pk):
|
||||
|
||||
parent = get_object_or_404(self.parent_model, pk=pk)
|
||||
|
||||
form = self.form(parent, request.POST)
|
||||
if form.is_valid():
|
||||
|
||||
new_components = []
|
||||
data = deepcopy(form.cleaned_data)
|
||||
|
||||
for name in form.cleaned_data['name_pattern']:
|
||||
component_data = {
|
||||
self.parent_field: parent.pk,
|
||||
'name': name,
|
||||
}
|
||||
# Replace objects with their primary key to keep component_form.clean() happy
|
||||
for k, v in data.items():
|
||||
if hasattr(v, 'pk'):
|
||||
component_data[k] = v.pk
|
||||
else:
|
||||
component_data[k] = v
|
||||
component_form = self.model_form(component_data)
|
||||
if component_form.is_valid():
|
||||
new_components.append(component_form.save(commit=False))
|
||||
else:
|
||||
for field, errors in component_form.errors.as_data().items():
|
||||
# Assign errors on the child form's name field to name_pattern on the parent form
|
||||
if field == 'name':
|
||||
field = 'name_pattern'
|
||||
for e in errors:
|
||||
form.add_error(field, '{}: {}'.format(name, ', '.join(e)))
|
||||
|
||||
if not form.errors:
|
||||
self.model.objects.bulk_create(new_components)
|
||||
messages.success(request, "Added {} {} to {}.".format(
|
||||
len(new_components), self.model._meta.verbose_name_plural, parent
|
||||
))
|
||||
if '_addanother' in request.POST:
|
||||
return redirect(request.path)
|
||||
else:
|
||||
return redirect(parent.get_absolute_url())
|
||||
|
||||
return render(request, self.template_name, {
|
||||
'parent': parent,
|
||||
'component_type': self.model._meta.verbose_name,
|
||||
'form': form,
|
||||
'return_url': parent.get_absolute_url(),
|
||||
})
|
||||
|
||||
|
||||
class ComponentEditView(ObjectEditView):
|
||||
parent_field = None
|
||||
|
||||
def get_return_url(self, request, obj):
|
||||
return getattr(obj, self.parent_field).get_absolute_url()
|
||||
|
||||
|
||||
class ComponentDeleteView(ObjectDeleteView):
|
||||
parent_field = None
|
||||
|
||||
def get_return_url(self, request, obj):
|
||||
return getattr(obj, self.parent_field).get_absolute_url()
|
||||
|
||||
Reference in New Issue
Block a user