mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Closes #1001: Merged IP interface assignment into ipam.IPAddressForm
This commit is contained in:
parent
f70f0f8d62
commit
09000ad9b3
@ -1691,36 +1691,6 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
|
|||||||
device = forms.CharField(required=False, label='Device name')
|
device = forms.CharField(required=False, label='Device name')
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# IP addresses
|
|
||||||
#
|
|
||||||
|
|
||||||
class IPAddressForm(BootstrapMixin, CustomFieldForm):
|
|
||||||
set_as_primary = forms.BooleanField(label='Set as primary IP for device', required=False)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = IPAddress
|
|
||||||
fields = ['address', 'vrf', 'tenant', 'status', 'interface', 'description']
|
|
||||||
|
|
||||||
def __init__(self, device, *args, **kwargs):
|
|
||||||
|
|
||||||
super(IPAddressForm, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.fields['vrf'].empty_label = 'Global'
|
|
||||||
|
|
||||||
interfaces = device.interfaces.order_naturally(method=device.device_type.interface_ordering)
|
|
||||||
self.fields['interface'].queryset = interfaces
|
|
||||||
self.fields['interface'].required = True
|
|
||||||
|
|
||||||
# If this device has only one interface, select it by default.
|
|
||||||
if 'interface' not in self.initial and len(interfaces) == 1:
|
|
||||||
self.fields['interface'].initial = interfaces[0]
|
|
||||||
|
|
||||||
# If this device does not have any IP addresses assigned, default to setting the first IP as its primary.
|
|
||||||
if not IPAddress.objects.filter(interface__device=device).count():
|
|
||||||
self.fields['set_as_primary'].initial = True
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Modules
|
# Modules
|
||||||
#
|
#
|
||||||
|
@ -116,7 +116,6 @@ urlpatterns = [
|
|||||||
url(r'^devices/(?P<pk>\d+)/delete/$', views.DeviceDeleteView.as_view(), name='device_delete'),
|
url(r'^devices/(?P<pk>\d+)/delete/$', views.DeviceDeleteView.as_view(), name='device_delete'),
|
||||||
url(r'^devices/(?P<pk>\d+)/inventory/$', views.device_inventory, name='device_inventory'),
|
url(r'^devices/(?P<pk>\d+)/inventory/$', views.device_inventory, name='device_inventory'),
|
||||||
url(r'^devices/(?P<pk>\d+)/lldp-neighbors/$', views.device_lldp_neighbors, name='device_lldp_neighbors'),
|
url(r'^devices/(?P<pk>\d+)/lldp-neighbors/$', views.device_lldp_neighbors, name='device_lldp_neighbors'),
|
||||||
url(r'^devices/(?P<pk>\d+)/ip-addresses/assign/$', views.ipaddress_assign, name='ipaddress_assign'),
|
|
||||||
url(r'^devices/(?P<pk>\d+)/add-secret/$', secret_add, name='device_addsecret'),
|
url(r'^devices/(?P<pk>\d+)/add-secret/$', secret_add, name='device_addsecret'),
|
||||||
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceEditView.as_view(), name='service_assign'),
|
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceEditView.as_view(), name='service_assign'),
|
||||||
|
|
||||||
|
@ -1561,47 +1561,6 @@ class InterfaceConnectionsListView(ObjectListView):
|
|||||||
template_name = 'dcim/interface_connections_list.html'
|
template_name = 'dcim/interface_connections_list.html'
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# IP addresses
|
|
||||||
#
|
|
||||||
|
|
||||||
@permission_required(['dcim.change_device', 'ipam.add_ipaddress'])
|
|
||||||
def ipaddress_assign(request, pk):
|
|
||||||
|
|
||||||
device = get_object_or_404(Device, pk=pk)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = forms.IPAddressForm(device, request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
|
|
||||||
ipaddress = form.save(commit=False)
|
|
||||||
ipaddress.interface = form.cleaned_data['interface']
|
|
||||||
ipaddress.save()
|
|
||||||
form.save_custom_fields()
|
|
||||||
messages.success(request, u"Added new IP address {} to interface {}.".format(ipaddress, ipaddress.interface))
|
|
||||||
|
|
||||||
if form.cleaned_data['set_as_primary']:
|
|
||||||
if ipaddress.family == 4:
|
|
||||||
device.primary_ip4 = ipaddress
|
|
||||||
elif ipaddress.family == 6:
|
|
||||||
device.primary_ip6 = ipaddress
|
|
||||||
device.save()
|
|
||||||
|
|
||||||
if '_addanother' in request.POST:
|
|
||||||
return redirect('dcim:ipaddress_assign', pk=device.pk)
|
|
||||||
else:
|
|
||||||
return redirect('dcim:device', pk=device.pk)
|
|
||||||
|
|
||||||
else:
|
|
||||||
form = forms.IPAddressForm(device, initial=request.GET)
|
|
||||||
|
|
||||||
return render(request, 'dcim/ipaddress_assign.html', {
|
|
||||||
'device': device,
|
|
||||||
'form': form,
|
|
||||||
'return_url': reverse('dcim:device', kwargs={'pk': device.pk}),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Modules
|
# Modules
|
||||||
#
|
#
|
||||||
|
@ -6,7 +6,7 @@ from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFi
|
|||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
APISelect, BootstrapMixin, BulkImportForm, CSVDataField, ExpandableIPAddressField, FilterChoiceField, Livesearch,
|
APISelect, BootstrapMixin, BulkImportForm, CSVDataField, ExpandableIPAddressField, FilterChoiceField, Livesearch,
|
||||||
SlugField, add_blank_choice,
|
ReturnURLForm, SlugField, add_blank_choice,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
@ -307,21 +307,46 @@ class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
# IP addresses
|
# IP addresses
|
||||||
#
|
#
|
||||||
|
|
||||||
class IPAddressForm(BootstrapMixin, CustomFieldForm):
|
class IPAddressForm(BootstrapMixin, ReturnURLForm, CustomFieldForm):
|
||||||
nat_site = forms.ModelChoiceField(queryset=Site.objects.all(), required=False, label='Site',
|
interface_site = forms.ModelChoiceField(
|
||||||
widget=forms.Select(attrs={'filter-for': 'nat_device'}))
|
queryset=Site.objects.all(), required=False, label='Site', widget=forms.Select(
|
||||||
nat_device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, label='Device',
|
attrs={'filter-for': 'interface_rack'}
|
||||||
widget=APISelect(api_url='/api/dcim/devices/?site_id={{nat_site}}',
|
)
|
||||||
display_field='display_name',
|
)
|
||||||
attrs={'filter-for': 'nat_inside'}))
|
interface_rack = forms.ModelChoiceField(
|
||||||
livesearch = forms.CharField(required=False, label='IP Address', widget=Livesearch(
|
queryset=Rack.objects.all(), required=False, label='Rack', widget=APISelect(
|
||||||
query_key='q', query_url='ipam-api:ipaddress_list', field_to_update='nat_inside', obj_label='address')
|
api_url='/api/dcim/racks/?site_id={{interface_site}}', display_field='display_name',
|
||||||
|
attrs={'filter-for': 'interface_device'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
interface_device = forms.ModelChoiceField(
|
||||||
|
queryset=Device.objects.all(), required=False, label='Device', widget=APISelect(
|
||||||
|
api_url='/api/dcim/devices/?site_id={{interface_site}}&rack_id={{interface_rack}}',
|
||||||
|
display_field='display_name', attrs={'filter-for': 'interface'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
nat_site = forms.ModelChoiceField(
|
||||||
|
queryset=Site.objects.all(), required=False, label='Site', widget=forms.Select(
|
||||||
|
attrs={'filter-for': 'nat_device'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
nat_device = forms.ModelChoiceField(
|
||||||
|
queryset=Device.objects.all(), required=False, label='Device', widget=APISelect(
|
||||||
|
api_url='/api/dcim/devices/?site_id={{nat_site}}', display_field='display_name',
|
||||||
|
attrs={'filter-for': 'nat_inside'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
livesearch = forms.CharField(
|
||||||
|
required=False, label='IP Address', widget=Livesearch(
|
||||||
|
query_key='q', query_url='ipam-api:ipaddress_list', field_to_update='nat_inside', obj_label='address'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
fields = ['address', 'vrf', 'tenant', 'status', 'nat_inside', 'description']
|
fields = ['address', 'vrf', 'tenant', 'status', 'interface', 'nat_inside', 'description']
|
||||||
widgets = {
|
widgets = {
|
||||||
|
'interface': APISelect(api_url='/api/dcim/devices/{{interface_device}}/interfaces/'),
|
||||||
'nat_inside': APISelect(api_url='/api/ipam/ip-addresses/?device_id={{nat_device}}', display_field='address')
|
'nat_inside': APISelect(api_url='/api/ipam/ip-addresses/?device_id={{nat_device}}', display_field='address')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,8 +355,37 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm):
|
|||||||
|
|
||||||
self.fields['vrf'].empty_label = 'Global'
|
self.fields['vrf'].empty_label = 'Global'
|
||||||
|
|
||||||
if self.instance.nat_inside:
|
# If an interface has been assigned, initialize site, rack, and device
|
||||||
|
if self.instance.interface:
|
||||||
|
self.initial['interface_site'] = self.instance.interface.device.site
|
||||||
|
self.initial['interface_rack'] = self.instance.interface.device.rack
|
||||||
|
self.initial['interface_device'] = self.instance.interface.device
|
||||||
|
|
||||||
|
# Limit rack choices
|
||||||
|
if self.is_bound and self.data.get('interface_site'):
|
||||||
|
self.fields['interface_rack'].queryset = Rack.objects.filter(site__pk=self.data['interface_site'])
|
||||||
|
elif self.initial.get('interface_site'):
|
||||||
|
self.fields['interface_rack'].queryset = Rack.objects.filter(site=self.initial['interface_site'])
|
||||||
|
else:
|
||||||
|
self.fields['interface_rack'].choices = []
|
||||||
|
|
||||||
|
# Limit device choices
|
||||||
|
if self.is_bound and self.data.get('interface_rack'):
|
||||||
|
self.fields['interface_device'].queryset = Device.objects.filter(rack=self.data['interface_rack'])
|
||||||
|
elif self.initial.get('interface_rack'):
|
||||||
|
self.fields['interface_device'].queryset = Device.objects.filter(rack=self.initial['interface_rack'])
|
||||||
|
else:
|
||||||
|
self.fields['interface_device'].choices = []
|
||||||
|
|
||||||
|
# Limit interface choices
|
||||||
|
if self.is_bound and self.data.get('interface_device'):
|
||||||
|
self.fields['interface'].queryset = Interface.objects.filter(device=self.data['interface_device'])
|
||||||
|
elif self.initial.get('interface_device'):
|
||||||
|
self.fields['interface'].queryset = Interface.objects.filter(device=self.initial['interface_device'])
|
||||||
|
else:
|
||||||
|
self.fields['interface'].choices = []
|
||||||
|
|
||||||
|
if self.instance.nat_inside:
|
||||||
nat_inside = self.instance.nat_inside
|
nat_inside = self.instance.nat_inside
|
||||||
# If the IP is assigned to an interface, populate site/device fields accordingly
|
# If the IP is assigned to an interface, populate site/device fields accordingly
|
||||||
if self.instance.nat_inside.interface:
|
if self.instance.nat_inside.interface:
|
||||||
@ -345,9 +399,7 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.fields['nat_inside'].queryset = IPAddress.objects.filter(pk=nat_inside.pk)
|
self.fields['nat_inside'].queryset = IPAddress.objects.filter(pk=nat_inside.pk)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# Initialize nat_device choices if nat_site is set
|
# Initialize nat_device choices if nat_site is set
|
||||||
if self.is_bound and self.data.get('nat_site'):
|
if self.is_bound and self.data.get('nat_site'):
|
||||||
self.fields['nat_device'].queryset = Device.objects.filter(site__pk=self.data['nat_site'])
|
self.fields['nat_device'].queryset = Device.objects.filter(site__pk=self.data['nat_site'])
|
||||||
@ -355,7 +407,6 @@ class IPAddressForm(BootstrapMixin, CustomFieldForm):
|
|||||||
self.fields['nat_device'].queryset = Device.objects.filter(site=self.initial['nat_site'])
|
self.fields['nat_device'].queryset = Device.objects.filter(site=self.initial['nat_site'])
|
||||||
else:
|
else:
|
||||||
self.fields['nat_device'].choices = []
|
self.fields['nat_device'].choices = []
|
||||||
|
|
||||||
# Initialize nat_inside choices if nat_device is set
|
# Initialize nat_inside choices if nat_device is set
|
||||||
if self.is_bound and self.data.get('nat_device'):
|
if self.is_bound and self.data.get('nat_device'):
|
||||||
self.fields['nat_inside'].queryset = IPAddress.objects.filter(
|
self.fields['nat_inside'].queryset = IPAddress.objects.filter(
|
||||||
|
@ -57,8 +57,6 @@ urlpatterns = [
|
|||||||
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
|
||||||
url(r'^ip-addresses/(?P<pk>\d+)/$', views.ipaddress, name='ipaddress'),
|
url(r'^ip-addresses/(?P<pk>\d+)/$', views.ipaddress, name='ipaddress'),
|
||||||
url(r'^ip-addresses/(?P<pk>\d+)/edit/$', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
|
url(r'^ip-addresses/(?P<pk>\d+)/edit/$', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
|
||||||
url(r'^ip-addresses/(?P<pk>\d+)/assign/$', views.ipaddress_assign, name='ipaddress_assign'),
|
|
||||||
url(r'^ip-addresses/(?P<pk>\d+)/remove/$', views.ipaddress_remove, name='ipaddress_remove'),
|
|
||||||
url(r'^ip-addresses/(?P<pk>\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
|
url(r'^ip-addresses/(?P<pk>\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
|
||||||
|
|
||||||
# VLAN groups
|
# VLAN groups
|
||||||
|
@ -571,75 +571,6 @@ def ipaddress(request, pk):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@permission_required(['dcim.change_device', 'ipam.change_ipaddress'])
|
|
||||||
def ipaddress_assign(request, pk):
|
|
||||||
|
|
||||||
ipaddress = get_object_or_404(IPAddress, pk=pk)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = forms.IPAddressAssignForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
|
|
||||||
interface = form.cleaned_data['interface']
|
|
||||||
ipaddress.interface = interface
|
|
||||||
ipaddress.save()
|
|
||||||
messages.success(request, u"Assigned IP address {} to interface {}.".format(ipaddress, ipaddress.interface))
|
|
||||||
|
|
||||||
if form.cleaned_data['set_as_primary']:
|
|
||||||
device = interface.device
|
|
||||||
if ipaddress.family == 4:
|
|
||||||
device.primary_ip4 = ipaddress
|
|
||||||
elif ipaddress.family == 6:
|
|
||||||
device.primary_ip6 = ipaddress
|
|
||||||
device.save()
|
|
||||||
|
|
||||||
return redirect('ipam:ipaddress', pk=ipaddress.pk)
|
|
||||||
else:
|
|
||||||
assert False, form.errors
|
|
||||||
|
|
||||||
else:
|
|
||||||
form = forms.IPAddressAssignForm()
|
|
||||||
|
|
||||||
return render(request, 'ipam/ipaddress_assign.html', {
|
|
||||||
'ipaddress': ipaddress,
|
|
||||||
'form': form,
|
|
||||||
'return_url': reverse('ipam:ipaddress', kwargs={'pk': ipaddress.pk}),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@permission_required(['dcim.change_device', 'ipam.change_ipaddress'])
|
|
||||||
def ipaddress_remove(request, pk):
|
|
||||||
|
|
||||||
ipaddress = get_object_or_404(IPAddress, pk=pk)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = ConfirmationForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
|
|
||||||
device = ipaddress.interface.device
|
|
||||||
ipaddress.interface = None
|
|
||||||
ipaddress.save()
|
|
||||||
messages.success(request, u"Removed IP address {} from {}.".format(ipaddress, device))
|
|
||||||
|
|
||||||
if device.primary_ip4 == ipaddress.pk:
|
|
||||||
device.primary_ip4 = None
|
|
||||||
device.save()
|
|
||||||
elif device.primary_ip6 == ipaddress.pk:
|
|
||||||
device.primary_ip6 = None
|
|
||||||
device.save()
|
|
||||||
|
|
||||||
return redirect('ipam:ipaddress', pk=ipaddress.pk)
|
|
||||||
|
|
||||||
else:
|
|
||||||
form = ConfirmationForm()
|
|
||||||
|
|
||||||
return render(request, 'ipam/ipaddress_unassign.html', {
|
|
||||||
'ipaddress': ipaddress,
|
|
||||||
'form': form,
|
|
||||||
'return_url': reverse('ipam:ipaddress', kwargs={'pk': ipaddress.pk}),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class IPAddressEditView(PermissionRequiredMixin, ObjectEditView):
|
class IPAddressEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
permission_required = 'ipam.change_ipaddress'
|
permission_required = 'ipam.change_ipaddress'
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.ipam.add_ipaddress %}
|
{% if perms.ipam.add_ipaddress %}
|
||||||
<a href="{% url 'dcim:ipaddress_assign' pk=device.pk %}?interface={{ iface.pk }}" class="btn btn-xs btn-success" title="Assign IP address">
|
<a href="{% url 'ipam:ipaddress_add' %}?interface_site={{ device.site.pk }}&interface_rack={{ device.rack.pk }}&interface_device={{ device.pk }}&interface={{ iface.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-success" title="Add IP address">
|
||||||
<i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
|
<i class="glyphicon glyphicon-plus" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -98,14 +98,8 @@
|
|||||||
<td>
|
<td>
|
||||||
{% if ipaddress.interface %}
|
{% if ipaddress.interface %}
|
||||||
<span><a href="{% url 'dcim:device' pk=ipaddress.interface.device.pk %}">{{ ipaddress.interface.device }}</a> ({{ ipaddress.interface }})</span>
|
<span><a href="{% url 'dcim:device' pk=ipaddress.interface.device.pk %}">{{ ipaddress.interface.device }}</a> ({{ ipaddress.interface }})</span>
|
||||||
{% if perms.dcim.change_device and perms.ipam.change_ipaddress %}
|
|
||||||
<a href="{% url 'ipam:ipaddress_remove' pk=ipaddress.pk %}" class="btn btn-xs btn-danger"><i class="glyphicon glyphicon-remove"></i> Remove</a>
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-muted">None</span>
|
<span class="text-muted">None</span>
|
||||||
{% if perms.dcim.change_device and perms.ipam.change_ipaddress %}
|
|
||||||
<a href="{% url 'ipam:ipaddress_assign' pk=ipaddress.pk %}" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-plus"></i> Assign</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -16,39 +16,20 @@
|
|||||||
{% render_field form.vrf %}
|
{% render_field form.vrf %}
|
||||||
{% render_field form.tenant %}
|
{% render_field form.tenant %}
|
||||||
{% render_field form.status %}
|
{% render_field form.status %}
|
||||||
{% if obj.pk %}
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-md-3 control-label">Device</label>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<p class="form-control-static">
|
|
||||||
{% if obj.interface %}
|
|
||||||
<a href="{% url 'dcim:device' pk=obj.interface.device.pk %}">{{ obj.interface.device }}</a>
|
|
||||||
<a href="{% url 'ipam:ipaddress_remove' pk=obj.pk %}" class="btn btn-xs btn-danger"><i class="glyphicon glyphicon-remove"></i> Remove</a>
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">None</span>
|
|
||||||
{% if obj.pk %}
|
|
||||||
<a href="{% url 'ipam:ipaddress_assign' pk=obj.pk %}" class="btn btn-xs btn-primary"><i class="glyphicon glyphicon-plus"></i> Assign</a>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="col-md-3 control-label">Interface</label>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<p class="form-control-static">
|
|
||||||
{% if obj.interface %}
|
|
||||||
{{ obj.interface }}
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">None</span>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% render_field form.description %}
|
{% render_field form.description %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Interface Assignment</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% render_field form.interface_site %}
|
||||||
|
{% render_field form.interface_rack %}
|
||||||
|
{% render_field form.interface_device %}
|
||||||
|
{% render_field form.interface %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading"><strong>NAT IP (Inside)</strong></div>
|
<div class="panel-heading"><strong>NAT IP (Inside)</strong></div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
|
@ -177,7 +177,9 @@ class ObjectEditView(GetReturnURLMixin, View):
|
|||||||
|
|
||||||
obj = self.get_object(kwargs)
|
obj = self.get_object(kwargs)
|
||||||
obj = self.alter_obj(obj, request, args, kwargs)
|
obj = self.alter_obj(obj, request, args, kwargs)
|
||||||
form = self.form_class(instance=obj, initial=request.GET)
|
# Parse initial data manually to avoid setting field values as lists
|
||||||
|
initial_data = {k: request.GET[k] for k in request.GET}
|
||||||
|
form = self.form_class(instance=obj, initial=initial_data)
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
'obj': obj,
|
'obj': obj,
|
||||||
|
Loading…
Reference in New Issue
Block a user