mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-18 04:56:29 -06:00
commit
ad95b86fdd
@ -33,3 +33,4 @@ Please see [the documentation](http://netbox.readthedocs.io/en/stable/) for inst
|
||||
|
||||
* [Docker container](https://github.com/digitalocean/netbox-docker)
|
||||
* [Heroku deployment](https://heroku.com/deploy?template=https://github.com/BILDQUADRAT/netbox/tree/heroku) (via [@mraerino](https://github.com/BILDQUADRAT/netbox/tree/heroku))
|
||||
* [Vagrant deployment](https://github.com/ryanmerolle/netbox-vagrant)
|
||||
|
@ -5,14 +5,14 @@
|
||||
Python 3:
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y python3 python3-dev python3-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev
|
||||
# apt-get install -y python3 python3-dev python3-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev zlib1g-dev
|
||||
# update-alternatives --install /usr/bin/python python /usr/bin/python3 1
|
||||
```
|
||||
|
||||
Python 2:
|
||||
|
||||
```no-highlight
|
||||
# apt-get install -y python2.7 python-dev python-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev
|
||||
# apt-get install -y python2.7 python-dev python-pip libxml2-dev libxslt1-dev libffi-dev graphviz libpq-dev libssl-dev zlib1g-dev
|
||||
```
|
||||
|
||||
**CentOS/RHEL**
|
||||
|
@ -73,6 +73,9 @@ Once Apache is installed, proceed with the following configuration (Be sure to m
|
||||
|
||||
Alias /static /opt/netbox/netbox/static
|
||||
|
||||
# Needed to allow token-based API authentication
|
||||
WSGIPassAuthorization on
|
||||
|
||||
<Directory /opt/netbox/netbox/static>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride None
|
||||
|
@ -581,9 +581,18 @@ class WritablePowerPortSerializer(serializers.ModelSerializer):
|
||||
# Interfaces
|
||||
#
|
||||
|
||||
class NestedInterfaceSerializer(serializers.ModelSerializer):
|
||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:interface-detail')
|
||||
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ['id', 'url', 'name']
|
||||
|
||||
|
||||
class InterfaceSerializer(serializers.ModelSerializer):
|
||||
device = NestedDeviceSerializer()
|
||||
form_factor = ChoiceFieldSerializer(choices=IFACE_FF_CHOICES)
|
||||
lag = NestedInterfaceSerializer()
|
||||
connection = serializers.SerializerMethodField(read_only=True)
|
||||
connected_interface = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
|
@ -477,6 +477,11 @@ class InterfaceFilter(DeviceComponentFilterSet):
|
||||
method='filter_type',
|
||||
label='Interface type',
|
||||
)
|
||||
lag_id = django_filters.ModelMultipleChoiceFilter(
|
||||
name='lag',
|
||||
queryset=Interface.objects.all(),
|
||||
label='LAG interface (ID)',
|
||||
)
|
||||
mac_address = django_filters.CharFilter(
|
||||
method='_mac_address',
|
||||
label='MAC address',
|
||||
|
@ -674,7 +674,7 @@ class BaseDeviceFromCSVForm(forms.ModelForm):
|
||||
queryset=Platform.objects.all(), required=False, to_field_name='name',
|
||||
error_messages={'invalid_choice': 'Invalid platform.'}
|
||||
)
|
||||
status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in STATUS_CHOICES])
|
||||
status = forms.CharField()
|
||||
|
||||
class Meta:
|
||||
fields = []
|
||||
@ -692,8 +692,12 @@ class BaseDeviceFromCSVForm(forms.ModelForm):
|
||||
except DeviceType.DoesNotExist:
|
||||
self.add_error('model_name', "Invalid device type ({} {})".format(manufacturer, model_name))
|
||||
|
||||
def clean_status_name(self):
|
||||
return dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']]
|
||||
def clean_status(self):
|
||||
status_choices = {s[1].lower(): s[0] for s in STATUS_CHOICES}
|
||||
try:
|
||||
return status_choices[self.cleaned_data['status'].lower()]
|
||||
except KeyError:
|
||||
raise ValidationError("Invalid status: {}".format(self.cleaned_data['status']))
|
||||
|
||||
|
||||
class DeviceFromCSVForm(BaseDeviceFromCSVForm):
|
||||
@ -707,8 +711,8 @@ class DeviceFromCSVForm(BaseDeviceFromCSVForm):
|
||||
|
||||
class Meta(BaseDeviceFromCSVForm.Meta):
|
||||
fields = [
|
||||
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag',
|
||||
'status_name', 'site', 'rack_name', 'position', 'face',
|
||||
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
|
||||
'site', 'rack_name', 'position', 'face',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
@ -751,8 +755,8 @@ class ChildDeviceFromCSVForm(BaseDeviceFromCSVForm):
|
||||
|
||||
class Meta(BaseDeviceFromCSVForm.Meta):
|
||||
fields = [
|
||||
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag',
|
||||
'status_name', 'parent', 'device_bay_name',
|
||||
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
|
||||
'parent', 'device_bay_name',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
@ -817,13 +821,15 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||
rack_id = FilterChoiceField(
|
||||
queryset=Rack.objects.annotate(filter_count=Count('devices')),
|
||||
label='Rack',
|
||||
null_option=(0, 'None'),
|
||||
)
|
||||
role = FilterChoiceField(
|
||||
queryset=DeviceRole.objects.annotate(filter_count=Count('devices')),
|
||||
to_field_name='slug',
|
||||
)
|
||||
tenant = FilterChoiceField(
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('devices')), to_field_name='slug',
|
||||
queryset=Tenant.objects.annotate(filter_count=Count('devices')),
|
||||
to_field_name='slug',
|
||||
null_option=(0, 'None'),
|
||||
)
|
||||
manufacturer_id = FilterChoiceField(queryset=Manufacturer.objects.all(), label='Manufacturer')
|
||||
@ -1207,7 +1213,7 @@ class PowerPortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFor
|
||||
)
|
||||
power_outlet = ChainedModelChoiceField(
|
||||
queryset=PowerOutlet.objects.all(),
|
||||
chains={'device': 'device'},
|
||||
chains={'device': 'pdu'},
|
||||
label='Outlet',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/power-outlets/?device_id={{pdu}}',
|
||||
@ -1441,7 +1447,7 @@ class InterfaceConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelFor
|
||||
label='Interface',
|
||||
widget=APISelect(
|
||||
api_url='/api/dcim/interfaces/?device_id={{device_b}}&type=physical',
|
||||
disabled_indicator='is_connected'
|
||||
disabled_indicator='connection'
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Count
|
||||
|
||||
from dcim.models import Site, Rack, Device, Interface
|
||||
@ -195,14 +196,16 @@ class PrefixFromCSVForm(forms.ModelForm):
|
||||
error_messages={'invalid_choice': 'Site not found.'})
|
||||
vlan_group_name = forms.CharField(required=False)
|
||||
vlan_vid = forms.IntegerField(required=False)
|
||||
status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in PREFIX_STATUS_CHOICES])
|
||||
status = forms.CharField()
|
||||
role = forms.ModelChoiceField(queryset=Role.objects.all(), required=False, to_field_name='name',
|
||||
error_messages={'invalid_choice': 'Invalid role.'})
|
||||
|
||||
class Meta:
|
||||
model = Prefix
|
||||
fields = ['prefix', 'vrf', 'tenant', 'site', 'vlan_group_name', 'vlan_vid', 'status_name', 'role', 'is_pool',
|
||||
'description']
|
||||
fields = [
|
||||
'prefix', 'vrf', 'tenant', 'site', 'vlan_group_name', 'vlan_vid', 'status', 'role', 'is_pool',
|
||||
'description',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
|
||||
@ -237,12 +240,12 @@ class PrefixFromCSVForm(forms.ModelForm):
|
||||
except VLAN.MultipleObjectsReturned:
|
||||
self.add_error('vlan_vid', "Multiple VLANs found ({} - VID {})".format(site, vlan_vid))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Assign Prefix status by name
|
||||
self.instance.status = dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']]
|
||||
|
||||
return super(PrefixFromCSVForm, self).save(*args, **kwargs)
|
||||
def clean_status(self):
|
||||
status_choices = {s[1].lower(): s[0] for s in PREFIX_STATUS_CHOICES}
|
||||
try:
|
||||
return status_choices[self.cleaned_data['status'].lower()]
|
||||
except KeyError:
|
||||
raise ValidationError("Invalid status: {}".format(self.cleaned_data['status']))
|
||||
|
||||
|
||||
class PrefixImportForm(BootstrapMixin, BulkImportForm):
|
||||
@ -491,7 +494,7 @@ class IPAddressFromCSVForm(forms.ModelForm):
|
||||
error_messages={'invalid_choice': 'VRF not found.'})
|
||||
tenant = forms.ModelChoiceField(Tenant.objects.all(), to_field_name='name', required=False,
|
||||
error_messages={'invalid_choice': 'Tenant not found.'})
|
||||
status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in IPADDRESS_STATUS_CHOICES])
|
||||
status = forms.CharField()
|
||||
device = forms.ModelChoiceField(queryset=Device.objects.all(), required=False, to_field_name='name',
|
||||
error_messages={'invalid_choice': 'Device not found.'})
|
||||
interface_name = forms.CharField(required=False)
|
||||
@ -499,7 +502,7 @@ class IPAddressFromCSVForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = IPAddress
|
||||
fields = ['address', 'vrf', 'tenant', 'status_name', 'device', 'interface_name', 'is_primary', 'description']
|
||||
fields = ['address', 'vrf', 'tenant', 'status', 'device', 'interface_name', 'is_primary', 'description']
|
||||
|
||||
def clean(self):
|
||||
|
||||
@ -522,10 +525,14 @@ class IPAddressFromCSVForm(forms.ModelForm):
|
||||
if is_primary and not device:
|
||||
self.add_error('is_primary', "No device specified; cannot set as primary IP")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
def clean_status(self):
|
||||
status_choices = {s[1].lower(): s[0] for s in IPADDRESS_STATUS_CHOICES}
|
||||
try:
|
||||
return status_choices[self.cleaned_data['status'].lower()]
|
||||
except KeyError:
|
||||
raise ValidationError("Invalid status: {}".format(self.cleaned_data['status']))
|
||||
|
||||
# Assign status by name
|
||||
self.instance.status = dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']]
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
# Set interface
|
||||
if self.cleaned_data['device'] and self.cleaned_data['interface_name']:
|
||||
@ -612,6 +619,7 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
||||
class VLANForm(BootstrapMixin, TenancyForm, CustomFieldForm):
|
||||
site = forms.ModelChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False,
|
||||
widget=forms.Select(
|
||||
attrs={'filter-for': 'group', 'nullable': 'true'}
|
||||
)
|
||||
@ -649,7 +657,7 @@ class VLANFromCSVForm(forms.ModelForm):
|
||||
Tenant.objects.all(), to_field_name='name', required=False,
|
||||
error_messages={'invalid_choice': 'Tenant not found.'}
|
||||
)
|
||||
status_name = forms.ChoiceField(choices=[(s[1], s[0]) for s in VLAN_STATUS_CHOICES])
|
||||
status = forms.CharField()
|
||||
role = forms.ModelChoiceField(
|
||||
queryset=Role.objects.all(), required=False, to_field_name='name',
|
||||
error_messages={'invalid_choice': 'Invalid role.'}
|
||||
@ -657,7 +665,7 @@ class VLANFromCSVForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = VLAN
|
||||
fields = ['site', 'group_name', 'vid', 'name', 'tenant', 'status_name', 'role', 'description']
|
||||
fields = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
||||
|
||||
def clean(self):
|
||||
|
||||
@ -671,6 +679,13 @@ class VLANFromCSVForm(forms.ModelForm):
|
||||
except VLANGroup.DoesNotExist:
|
||||
self.add_error('group_name', "Invalid VLAN group {}.".format(group_name))
|
||||
|
||||
def clean_status(self):
|
||||
status_choices = {s[1].lower(): s[0] for s in VLAN_STATUS_CHOICES}
|
||||
try:
|
||||
return status_choices[self.cleaned_data['status'].lower()]
|
||||
except KeyError:
|
||||
raise ValidationError("Invalid status: {}".format(self.cleaned_data['status']))
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
vlan = super(VLANFromCSVForm, self).save(commit=False)
|
||||
@ -679,9 +694,6 @@ class VLANFromCSVForm(forms.ModelForm):
|
||||
if self.cleaned_data['group_name']:
|
||||
vlan.group = VLANGroup.objects.get(site=self.cleaned_data['site'], name=self.cleaned_data['group_name'])
|
||||
|
||||
# Assign VLAN status by name
|
||||
vlan.status = dict(self.fields['status_name'].choices)[self.cleaned_data['status_name']]
|
||||
|
||||
if kwargs.get('commit'):
|
||||
vlan.save()
|
||||
return vlan
|
||||
|
@ -70,9 +70,9 @@ IPADDRESS_LINK = """
|
||||
{% if record.pk %}
|
||||
<a href="{{ record.get_absolute_url }}">{{ record.address }}</a>
|
||||
{% elif perms.ipam.add_ipaddress %}
|
||||
<a href="{% url 'ipam:ipaddress_add' %}?address={{ record.1 }}{% if prefix.vrf %}&vrf={{ prefix.vrf.pk }}{% endif %}" class="btn btn-xs btn-success">{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Lots of{% endif %} free IP{{ record.0|pluralize }}</a>
|
||||
<a href="{% url 'ipam:ipaddress_add' %}?address={{ record.1 }}{% if prefix.vrf %}&vrf={{ prefix.vrf.pk }}{% endif %}" class="btn btn-xs btn-success">{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available</a>
|
||||
{% else %}
|
||||
{{ record.0 }}
|
||||
{% if record.0 <= 65536 %}{{ record.0 }}{% else %}Many{% endif %} IP{{ record.0|pluralize }} available
|
||||
{% endif %}
|
||||
"""
|
||||
|
||||
|
@ -525,6 +525,7 @@ def prefix_ipaddresses(request, pk):
|
||||
'prefix': prefix,
|
||||
'ip_table': ip_table,
|
||||
'permissions': permissions,
|
||||
'bulk_querystring': 'vrf_id={}&parent={}'.format(prefix.vrf or '0', prefix.prefix),
|
||||
})
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@ except ImportError:
|
||||
)
|
||||
|
||||
|
||||
VERSION = '2.0.2'
|
||||
VERSION = '2.0.3'
|
||||
|
||||
# Import local configuration
|
||||
ALLOWED_HOSTS = DATABASE = SECRET_KEY = None
|
||||
|
@ -1,3 +1,4 @@
|
||||
from collections import OrderedDict
|
||||
import sys
|
||||
|
||||
from rest_framework.views import APIView
|
||||
@ -27,91 +28,91 @@ from .forms import SearchForm
|
||||
|
||||
|
||||
SEARCH_MAX_RESULTS = 15
|
||||
SEARCH_TYPES = {
|
||||
SEARCH_TYPES = OrderedDict((
|
||||
# Circuits
|
||||
'provider': {
|
||||
('provider', {
|
||||
'queryset': Provider.objects.all(),
|
||||
'filter': ProviderFilter,
|
||||
'table': ProviderSearchTable,
|
||||
'url': 'circuits:provider_list',
|
||||
},
|
||||
'circuit': {
|
||||
}),
|
||||
('circuit', {
|
||||
'queryset': Circuit.objects.select_related('type', 'provider', 'tenant').prefetch_related('terminations__site'),
|
||||
'filter': CircuitFilter,
|
||||
'table': CircuitSearchTable,
|
||||
'url': 'circuits:circuit_list',
|
||||
},
|
||||
}),
|
||||
# DCIM
|
||||
'site': {
|
||||
('site', {
|
||||
'queryset': Site.objects.select_related('region', 'tenant'),
|
||||
'filter': SiteFilter,
|
||||
'table': SiteSearchTable,
|
||||
'url': 'dcim:site_list',
|
||||
},
|
||||
'rack': {
|
||||
}),
|
||||
('rack', {
|
||||
'queryset': Rack.objects.select_related('site', 'group', 'tenant', 'role'),
|
||||
'filter': RackFilter,
|
||||
'table': RackSearchTable,
|
||||
'url': 'dcim:rack_list',
|
||||
},
|
||||
'devicetype': {
|
||||
}),
|
||||
('devicetype', {
|
||||
'queryset': DeviceType.objects.select_related('manufacturer'),
|
||||
'filter': DeviceTypeFilter,
|
||||
'table': DeviceTypeSearchTable,
|
||||
'url': 'dcim:devicetype_list',
|
||||
},
|
||||
'device': {
|
||||
}),
|
||||
('device', {
|
||||
'queryset': Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack'),
|
||||
'filter': DeviceFilter,
|
||||
'table': DeviceSearchTable,
|
||||
'url': 'dcim:device_list',
|
||||
},
|
||||
}),
|
||||
# IPAM
|
||||
'vrf': {
|
||||
('vrf', {
|
||||
'queryset': VRF.objects.select_related('tenant'),
|
||||
'filter': VRFFilter,
|
||||
'table': VRFSearchTable,
|
||||
'url': 'ipam:vrf_list',
|
||||
},
|
||||
'aggregate': {
|
||||
}),
|
||||
('aggregate', {
|
||||
'queryset': Aggregate.objects.select_related('rir'),
|
||||
'filter': AggregateFilter,
|
||||
'table': AggregateSearchTable,
|
||||
'url': 'ipam:aggregate_list',
|
||||
},
|
||||
'prefix': {
|
||||
}),
|
||||
('prefix', {
|
||||
'queryset': Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'),
|
||||
'filter': PrefixFilter,
|
||||
'table': PrefixSearchTable,
|
||||
'url': 'ipam:prefix_list',
|
||||
},
|
||||
'ipaddress': {
|
||||
}),
|
||||
('ipaddress', {
|
||||
'queryset': IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device'),
|
||||
'filter': IPAddressFilter,
|
||||
'table': IPAddressSearchTable,
|
||||
'url': 'ipam:ipaddress_list',
|
||||
},
|
||||
'vlan': {
|
||||
}),
|
||||
('vlan', {
|
||||
'queryset': VLAN.objects.select_related('site', 'group', 'tenant', 'role'),
|
||||
'filter': VLANFilter,
|
||||
'table': VLANSearchTable,
|
||||
'url': 'ipam:vlan_list',
|
||||
},
|
||||
}),
|
||||
# Secrets
|
||||
'secret': {
|
||||
('secret', {
|
||||
'queryset': Secret.objects.select_related('role', 'device'),
|
||||
'filter': SecretFilter,
|
||||
'table': SecretSearchTable,
|
||||
'url': 'secrets:secret_list',
|
||||
},
|
||||
}),
|
||||
# Tenancy
|
||||
'tenant': {
|
||||
('tenant', {
|
||||
'queryset': Tenant.objects.select_related('group'),
|
||||
'filter': TenantFilter,
|
||||
'table': TenantSearchTable,
|
||||
'url': 'tenancy:tenant_list',
|
||||
},
|
||||
}
|
||||
}),
|
||||
))
|
||||
|
||||
|
||||
def home(request):
|
||||
|
@ -74,6 +74,13 @@ footer p {
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide the nav search bar on displays less than 1600px wide */
|
||||
@media (max-width: 1599px) {
|
||||
#navbar_search {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
label {
|
||||
font-weight: normal;
|
||||
|
@ -16,7 +16,7 @@ $(document).ready(function() {
|
||||
|
||||
// Adding/editing a secret
|
||||
$('form').submit(function(event) {
|
||||
$(this).find('input.requires-session-key').each(function() {
|
||||
$(this).find('.requires-session-key').each(function() {
|
||||
if (this.value && document.cookie.indexOf('session_key') == -1) {
|
||||
console.log('Field ' + this.value + ' requires a session key');
|
||||
$('#privkey_modal').modal('show');
|
||||
|
@ -246,8 +246,8 @@
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if request.user.is_authenticated %}
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
{{ request.user }} <span class="caret"></span>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" title="{{ request.user }}" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
{{ request.user|truncatechars:"30" }} <span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url 'user:profile' %}"><i class="fa fa-user" aria-hidden="true"></i> Profile</a></li>
|
||||
@ -262,7 +262,7 @@
|
||||
<li><a href="{% url 'login' %}?next={{ request.path }}"><i class="fa fa-sign-in" aria-hidden="true"></i> Log in</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<form action="{% url 'search' %}" method="get" class="navbar-form navbar-right" role="search">
|
||||
<form action="{% url 'search' %}" method="get" class="navbar-form navbar-right" id="navbar_search" role="search">
|
||||
<div class="input-group">
|
||||
<input type="text" name="q" class="form-control" placeholder="Search">
|
||||
<span class="input-group-btn">
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Circuit Import{% endblock %}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Provider Import{% endblock %}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Console Connections Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}Console Connections{% endblock %}
|
||||
|
||||
@ -16,7 +15,7 @@
|
||||
<h1>Console Connections</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% render_table table 'table.html' %}
|
||||
{% include 'responsive_table.html' %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load static from staticfiles %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block title %}{{ device }}{% endblock %}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Device Import{% endblock %}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Device Import{% endblock %}
|
||||
|
@ -76,6 +76,7 @@ $(document).ready(function() {
|
||||
|
||||
// Update rack options
|
||||
rack_list.empty();
|
||||
rack_list.append($("<option></option>").attr("value", "0").text("None"));
|
||||
$.ajax({
|
||||
url: netbox_api_path + 'dcim/racks/?limit=500&site=' + selected_sites.join('&site='),
|
||||
dataType: 'json',
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{{ devicetype.manufacturer }} {{ devicetype.model }}{% endblock %}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
{% load render_table from django_tables2 %}
|
||||
{% if perms.dcim.change_devicetype %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
@ -19,7 +18,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% render_table table 'table.html' %}
|
||||
{% include 'responsive_table.html' %}
|
||||
<div class="panel-footer">
|
||||
{% if table.rows %}
|
||||
{% if edit_url %}
|
||||
@ -48,6 +47,6 @@
|
||||
<div class="panel-heading">
|
||||
<strong>{{ title }}</strong>
|
||||
</div>
|
||||
{% render_table table 'table.html' %}
|
||||
{% include 'responsive_table.html' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Interface Connections Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}Interface Connections{% endblock %}
|
||||
|
||||
@ -16,7 +15,7 @@
|
||||
<h1>Interface Connections</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% render_table table 'table.html' %}
|
||||
{% include 'responsive_table.html' %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Power Connections Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}Power Connections{% endblock %}
|
||||
|
||||
@ -16,7 +15,7 @@
|
||||
<h1>Power Connections</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
{% render_table table 'table.html' %}
|
||||
{% include 'responsive_table.html' %}
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
{% include 'inc/search_panel.html' %}
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load helpers %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{{ rack.site }} - Rack {{ rack.name }}{% endblock %}
|
||||
|
||||
|
@ -11,24 +11,25 @@
|
||||
{% if page %}
|
||||
<div class="col-md-9">
|
||||
<div style="white-space: nowrap; overflow-x: scroll;">
|
||||
{% for rack in page %}
|
||||
<div style="display: inline-block; width: 266px">
|
||||
<div class="rack_header">
|
||||
<h4><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></h4>
|
||||
{% for rack in page %}
|
||||
<div style="display: inline-block; width: 266px">
|
||||
<div class="rack_header">
|
||||
<h4><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></h4>
|
||||
</div>
|
||||
{% if face_id %}
|
||||
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_rear_elevation secondary_face=rack.get_front_elevation face_id=1 %}
|
||||
{% else %}
|
||||
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_front_elevation secondary_face=rack.get_rear_elevation face_id=0 %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
<div class="rack_header">
|
||||
<h4><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></h4>
|
||||
</div>
|
||||
</div>
|
||||
{% if face_id %}
|
||||
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_rear_elevation secondary_face=rack.get_front_elevation face_id=1 %}
|
||||
{% else %}
|
||||
{% include 'dcim/inc/rack_elevation.html' with primary_face=rack.get_front_elevation secondary_face=rack.get_rear_elevation face_id=0 %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
<div class="rack_header">
|
||||
<h4><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></h4>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% include 'paginator.html' %}
|
||||
<br />
|
||||
{% include 'inc/paginator.html' %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-md-9">
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Rack Import{% endblock %}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load static from staticfiles %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block title %}{{ site }}{% endblock %}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Site Import{% endblock %}
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'search_form.html' %}
|
||||
|
@ -1,9 +1,8 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}Import Completed{% endblock %}</h1>
|
||||
{% render_table table %}
|
||||
{% include 'responsive_table.html' %}
|
||||
<a href="{{ request.path }}" class="btn btn-primary">
|
||||
<span class="fa fa-download" aria-hidden="true"></span>
|
||||
Import more
|
||||
|
@ -1,11 +1,11 @@
|
||||
{% load helpers %}
|
||||
|
||||
<div class="paginator pull-right" style="margin-top: 20px">
|
||||
<div class="paginator pull-right">
|
||||
{% if paginator.num_pages > 1 %}
|
||||
<nav>
|
||||
<ul class="pagination pull-right">
|
||||
{% if page.has_previous %}
|
||||
<li><a href="{% querystring request page=page.previous_page_number %}">«</a></li>
|
||||
<li><a href="{% querystring request page=page.previous_page_number %}"><i class="fa fa-angle-double-left"></i></a></li>
|
||||
{% endif %}
|
||||
{% for p in page.smart_pages %}
|
||||
{% if p %}
|
||||
@ -15,13 +15,14 @@
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if page.has_next %}
|
||||
<li><a href="{% querystring request page=page.next_page_number %}">»</a></li>
|
||||
<li><a href="{% querystring request page=page.next_page_number %}"><i class="fa fa-angle-double-right"></i></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
<div class="text-right text-muted">
|
||||
Showing {{ page.start_index }}-{{ page.end_index }} of {{ total_count }}
|
||||
</div>
|
||||
{% if page %}
|
||||
<div class="text-right text-muted">
|
||||
Showing {{ page.start_index }}-{{ page.end_index }} of {{ page.paginator.count }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
41
netbox/templates/inc/table.html
Normal file
41
netbox/templates/inc/table.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% load django_tables2 %}
|
||||
|
||||
<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %}>
|
||||
{% if table.show_header %}
|
||||
<thead>
|
||||
<tr>
|
||||
{% for column in table.columns %}
|
||||
{% if column.orderable %}
|
||||
<th {{ column.attrs.th.as_html }}><a href="{% querystring page=column.order_by_alias.next %}">{{ column.header }}</a></th>
|
||||
{% else %}
|
||||
<th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
{% endif %}
|
||||
<tbody>
|
||||
{% for row in table.page.object_list|default:table.rows %}
|
||||
<tr {{ row.attrs.as_html }}>
|
||||
{% for column, cell in row.items %}
|
||||
<td {{ column.attrs.td.as_html }}>{{ cell }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% empty %}
|
||||
{% if table.empty_text %}
|
||||
<tr>
|
||||
<td colspan="{{ table.columns|length }}">{{ table.empty_text }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% if table.has_footer %}
|
||||
<tfoot>
|
||||
<tr>
|
||||
{% for column in table.columns %}
|
||||
<td>{{ column.footer }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tfoot>
|
||||
{% endif %}
|
||||
</table>
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}Aggregate: {{ aggregate }}{% endblock %}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Aggregate Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{{ ipaddress }}{% endblock %}
|
||||
|
||||
@ -133,17 +132,11 @@
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
{% with heading='Parent Prefixes' %}
|
||||
{% render_table parent_prefixes_table 'panel_table.html' %}
|
||||
{% endwith %}
|
||||
{% include 'panel_table.html' with table=parent_prefixes_table heading='Parent Prefixes' %}
|
||||
{% if duplicate_ips_table.rows %}
|
||||
{% with heading='Duplicate IP Addresses' panel_class='danger' %}
|
||||
{% render_table duplicate_ips_table 'panel_table.html' %}
|
||||
{% endwith %}
|
||||
{% include 'panel_table.html' with table=duplicate_ips_table heading='Duplicate IP Addresses' panel_class='danger' %}
|
||||
{% endif %}
|
||||
{% with heading='Related IP Addresses' %}
|
||||
{% render_table related_ips_table 'panel_table.html' %}
|
||||
{% endwith %}
|
||||
{% include 'panel_table.html' with table=related_ips_table heading='Related IP Addresses' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}IP Address Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{{ prefix }}{% endblock %}
|
||||
|
||||
@ -134,13 +133,9 @@
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
{% if duplicate_prefix_table.rows %}
|
||||
{% with heading='Duplicate Prefixes' panel_class='danger' %}
|
||||
{% render_table duplicate_prefix_table 'panel_table.html' %}
|
||||
{% endwith %}
|
||||
{% include 'panel_table.html' with table=duplicate_prefix_table heading='Duplicate Prefixes' panel_class='danger' %}
|
||||
{% endif %}
|
||||
{% with heading='Parent Prefixes' %}
|
||||
{% render_table parent_prefix_table 'panel_table.html' %}
|
||||
{% endwith %}
|
||||
{% include 'panel_table.html' with table=parent_prefix_table heading='Parent Prefixes' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Prefix Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}{{ prefix }}{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}VLAN {{ vlan.display_name }}{% endblock %}
|
||||
|
||||
@ -136,7 +135,7 @@
|
||||
<div class="panel-heading">
|
||||
<strong>Prefixes</strong>
|
||||
</div>
|
||||
{% render_table prefix_table %}
|
||||
{% include 'responsive_table.html' with table=prefix_table %}
|
||||
{% if perms.ipam.add_prefix %}
|
||||
<div class="panel-footer text-right">
|
||||
<a href="{% url 'ipam:prefix_add' %}?{% if vlan.tenant %}tenant={{ vlan.tenant.pk }}&{% endif %}site={{ vlan.site.pk }}&vlan={{ vlan.pk }}" class="btn btn-primary btn-xs">
|
||||
|
@ -5,11 +5,11 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><strong>VLAN</strong></div>
|
||||
<div class="panel-body">
|
||||
{% render_field form.site %}
|
||||
{% render_field form.group %}
|
||||
{% render_field form.vid %}
|
||||
{% render_field form.name %}
|
||||
{% render_field form.status %}
|
||||
{% render_field form.site %}
|
||||
{% render_field form.group %}
|
||||
{% render_field form.role %}
|
||||
{% render_field form.description %}
|
||||
</div>
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}VLAN Import{% endblock %}
|
||||
|
||||
@ -17,7 +15,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Site</td>
|
||||
<td>Name of assigned site</td>
|
||||
<td>Name of assigned site (optional)</td>
|
||||
<td>LAS2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{% block title %}VRF {{ vrf }}{% endblock %}
|
||||
|
||||
@ -92,7 +91,7 @@
|
||||
<div class="panel-heading">
|
||||
<strong>Prefixes</strong>
|
||||
</div>
|
||||
{% render_table prefix_table %}
|
||||
{% include 'responsive_table.html' with table=prefix_table %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}VRF Import{% endblock %}
|
||||
|
||||
|
@ -1,10 +1,5 @@
|
||||
{% extends 'django_tables2/table.html' %}
|
||||
{% load django_tables2 %}
|
||||
{% load i18n %}
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
{# Wraps a table inside a Bootstrap panel and includes custom pagination rendering #}
|
||||
|
||||
{% block table %}
|
||||
<div class="panel panel-{{ panel_class|default:'default' }}">
|
||||
{% if heading %}
|
||||
<div class="panel-heading">
|
||||
@ -12,15 +7,14 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if table.rows %}
|
||||
{{ block.super }}
|
||||
{% render_table table 'inc/table.html' %}
|
||||
{% else %}
|
||||
<div class="panel-body text-muted">None</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagination %}
|
||||
{% if not hide_paginator %}
|
||||
{% include 'table_paginator.html' %}
|
||||
{% endif %}
|
||||
{% endblock pagination %}
|
||||
{% if table.rows and not hide_paginator %}
|
||||
{% with paginator=table.paginator page=table.page %}
|
||||
{% include 'inc/paginator.html' %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
8
netbox/templates/responsive_table.html
Normal file
8
netbox/templates/responsive_table.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% load render_table from django_tables2 %}
|
||||
|
||||
<div class="table-responsive">
|
||||
{% render_table table 'inc/table.html' %}
|
||||
</div>
|
||||
{% with paginator=table.paginator page=table.page %}
|
||||
{% include 'inc/paginator.html' %}
|
||||
{% endwith %}
|
@ -1,6 +1,5 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load static from staticfiles %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Secret Import{% endblock %}
|
||||
|
@ -1,10 +0,0 @@
|
||||
{% extends 'django_tables2/bootstrap-responsive.html' %}
|
||||
{% load django_tables2 %}
|
||||
|
||||
{# Extends the stock django_tables2 template to provide custom formatting of the pagination controls #}
|
||||
|
||||
{% block pagination %}
|
||||
{% if not hide_paginator %}
|
||||
{% include 'table_paginator.html' %}
|
||||
{% endif %}
|
||||
{% endblock pagination %}
|
@ -1,36 +0,0 @@
|
||||
{% load django_tables2 %}
|
||||
|
||||
{# Custom pagination controls to render nicely with Bootstrap CSS. smart_pages requires EnhancedPaginator. #}
|
||||
|
||||
<div class="paginator pull-right">
|
||||
{% if table.paginator.num_pages > 1 %}
|
||||
<nav>
|
||||
<ul class="pagination pull-right">
|
||||
{% if table.page.has_previous %}
|
||||
<li><a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}"><i class="fa fa-angle-double-left"></i></a></li>
|
||||
{% endif %}
|
||||
{% for p in table.page.smart_pages %}
|
||||
{% if p %}
|
||||
<li{% ifequal table.page.number p %} class="active"{% endifequal %}><a href="{% querystring table.prefixed_page_field=p %}">{{ p }}</a></li>
|
||||
{% else %}
|
||||
<li class="disabled"><span>…</span></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if table.page.has_next %}
|
||||
<li><a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}"><i class="fa fa-angle-double-right"></i></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
<div class="text-right text-muted">
|
||||
{% with table.page.paginator.count as total %}
|
||||
Showing {{ table.page.start_index }}-{{ table.page.end_index }} of {{ total }}
|
||||
{% if total == 1 %}
|
||||
{{ table.data.verbose_name }}
|
||||
{% else %}
|
||||
{{ table.data.verbose_name_plural }}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
@ -1,6 +1,4 @@
|
||||
{% extends 'utilities/obj_import.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Tenant Import{% endblock %}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -1,4 +1,3 @@
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load helpers %}
|
||||
{% if permissions.change or permissions.delete %}
|
||||
<form method="post" class="form form-horizontal">
|
||||
@ -15,12 +14,12 @@
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if bulk_edit_url and permissions.change %}
|
||||
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled="disabled">
|
||||
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if bulk_querystring %}?{{ bulk_querystring }}{% elif request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm" disabled="disabled">
|
||||
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit All
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if bulk_delete_url and permissions.delete %}
|
||||
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled="disabled">
|
||||
<button type="submit" name="_delete" formaction="{% url bulk_delete_url %}{% if bulk_querystring %}?{{ bulk_querystring }}{% elif request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-danger btn-sm" disabled="disabled">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete All
|
||||
</button>
|
||||
{% endif %}
|
||||
@ -28,7 +27,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% render_table table table_template|default:'table.html' %}
|
||||
{% include table_template|default:'responsive_table.html' %}
|
||||
{% block extra_actions %}{% endblock %}
|
||||
{% if bulk_edit_url and permissions.change %}
|
||||
<button type="submit" name="_edit" formaction="{% url bulk_edit_url %}{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" class="btn btn-warning btn-sm">
|
||||
@ -42,6 +41,6 @@
|
||||
{% endif %}
|
||||
</form>
|
||||
{% else %}
|
||||
{% render_table table table_template|default:'table.html' %}
|
||||
{% include table_template|default:'responsive_table.html' %}
|
||||
{% endif %}
|
||||
<div class="clearfix"></div>
|
||||
|
@ -443,12 +443,10 @@ class ChainedFieldsMixin(forms.BaseForm):
|
||||
|
||||
filters_dict = {}
|
||||
for db_field, parent_field in field.chains.items():
|
||||
if self.is_bound:
|
||||
filters_dict[db_field] = self.data.get(parent_field) or None
|
||||
if self.is_bound and self.data.get(parent_field):
|
||||
filters_dict[db_field] = self.data[parent_field]
|
||||
elif self.initial.get(parent_field):
|
||||
filters_dict[db_field] = self.initial[parent_field]
|
||||
else:
|
||||
filters_dict[db_field] = None
|
||||
|
||||
if filters_dict:
|
||||
field.queryset = field.queryset.filter(**filters_dict)
|
||||
|
@ -4,7 +4,6 @@ from django_tables2 import RequestConfig
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction, IntegrityError
|
||||
from django.db.models import ProtectedError
|
||||
from django.forms import CharField, ModelMultipleChoiceField, MultipleHiddenInput, TypedChoiceField
|
||||
|
Loading…
Reference in New Issue
Block a user