mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
VirtualChassis form validation cleanup
This commit is contained in:
@ -2277,6 +2277,57 @@ class VirtualChassisForm(BootstrapMixin, forms.ModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class BaseVCMemberFormSet(forms.BaseModelFormSet):
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super(BaseVCMemberFormSet, self).clean()
|
||||||
|
|
||||||
|
# Check for duplicate VC position values
|
||||||
|
vc_position_list = []
|
||||||
|
for form in self.forms:
|
||||||
|
vc_position = form.cleaned_data['vc_position']
|
||||||
|
if vc_position in vc_position_list:
|
||||||
|
error_msg = 'A virtual chassis member already exists in position {}.'.format(vc_position)
|
||||||
|
form.add_error('vc_position', error_msg)
|
||||||
|
vc_position_list.append(vc_position)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceVCMembershipForm(forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Device
|
||||||
|
fields = ['vc_position', 'vc_priority']
|
||||||
|
labels = {
|
||||||
|
'vc_position': 'Position',
|
||||||
|
'vc_priority': 'Priority',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, validate_vc_position=False, **kwargs):
|
||||||
|
super(DeviceVCMembershipForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Require VC position (only required when the Device is a VirtualChassis member)
|
||||||
|
self.fields['vc_position'].required = True
|
||||||
|
|
||||||
|
# Validation of vc_position is optional. This is only required when adding a new member to an existing
|
||||||
|
# VirtualChassis. Otherwise, vc_position validation is handled by BaseVCMemberFormSet.
|
||||||
|
self.validate_vc_position = validate_vc_position
|
||||||
|
|
||||||
|
def clean_vc_position(self):
|
||||||
|
vc_position = self.cleaned_data['vc_position']
|
||||||
|
|
||||||
|
if self.validate_vc_position:
|
||||||
|
conflicting_members = Device.objects.filter(
|
||||||
|
virtual_chassis=self.instance.virtual_chassis,
|
||||||
|
vc_position=vc_position
|
||||||
|
)
|
||||||
|
if conflicting_members.exists():
|
||||||
|
raise forms.ValidationError(
|
||||||
|
'A virtual chassis member already exists in position {}.'.format(vc_position)
|
||||||
|
)
|
||||||
|
|
||||||
|
return vc_position
|
||||||
|
|
||||||
|
|
||||||
class VCMemberSelectForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
|
class VCMemberSelectForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
|
||||||
site = forms.ModelChoiceField(
|
site = forms.ModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
@ -2315,27 +2366,4 @@ class VCMemberSelectForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
|
|||||||
device = self.cleaned_data['device']
|
device = self.cleaned_data['device']
|
||||||
if device.virtual_chassis is not None:
|
if device.virtual_chassis is not None:
|
||||||
raise forms.ValidationError("Device {} is already assigned to a virtual chassis.".format(device))
|
raise forms.ValidationError("Device {} is already assigned to a virtual chassis.".format(device))
|
||||||
|
return device
|
||||||
|
|
||||||
class DeviceVCMembershipForm(forms.ModelForm):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Device
|
|
||||||
fields = ['vc_position', 'vc_priority']
|
|
||||||
labels = {
|
|
||||||
'vc_position': 'Position',
|
|
||||||
'vc_priority': 'Priority',
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(DeviceVCMembershipForm, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Require VC position when assigning a member
|
|
||||||
self.fields['vc_position'].required = True
|
|
||||||
|
|
||||||
def clean_vc_position(self):
|
|
||||||
vc_position = self.cleaned_data['vc_position']
|
|
||||||
if Device.objects.filter(virtual_chassis=self.instance.virtual_chassis, vc_position=vc_position).exists():
|
|
||||||
raise forms.ValidationError("A virtual chassis member already exists in this position.")
|
|
||||||
|
|
||||||
return vc_position
|
|
||||||
|
@ -857,7 +857,7 @@ class DeviceView(View):
|
|||||||
|
|
||||||
# VirtualChassis members
|
# VirtualChassis members
|
||||||
if device.virtual_chassis is not None:
|
if device.virtual_chassis is not None:
|
||||||
vc_members = Device.objects.filter(virtual_chassis=device.virtual_chassis)
|
vc_members = Device.objects.filter(virtual_chassis=device.virtual_chassis).order_by('vc_position')
|
||||||
else:
|
else:
|
||||||
vc_members = []
|
vc_members = []
|
||||||
|
|
||||||
@ -2080,20 +2080,26 @@ class VirtualChassisCreateView(PermissionRequiredMixin, View):
|
|||||||
# Get the list of devices being added to a VirtualChassis
|
# Get the list of devices being added to a VirtualChassis
|
||||||
pk_form = forms.DeviceSelectionForm(request.POST)
|
pk_form = forms.DeviceSelectionForm(request.POST)
|
||||||
pk_form.full_clean()
|
pk_form.full_clean()
|
||||||
device_list = pk_form.cleaned_data.get('pk')
|
device_queryset = Device.objects.filter(
|
||||||
|
pk__in=pk_form.cleaned_data.get('pk')
|
||||||
|
).select_related('rack').order_by('vc_position')
|
||||||
|
|
||||||
if not device_list:
|
if not device_queryset:
|
||||||
messages.warning(request, "No devices were selected.")
|
messages.warning(request, "No devices were selected.")
|
||||||
return redirect('dcim:device_list')
|
return redirect('dcim:device_list')
|
||||||
|
|
||||||
# TODO: Error if any of the devices already belong to a VC
|
VCMemberFormSet = modelformset_factory(
|
||||||
|
model=Device,
|
||||||
VCMemberFormSet = modelformset_factory(model=Device, fields=('vc_position', 'vc_priority'), extra=0)
|
formset=forms.BaseVCMemberFormSet,
|
||||||
|
form=forms.DeviceVCMembershipForm,
|
||||||
|
extra=0
|
||||||
|
)
|
||||||
|
|
||||||
if '_create' in request.POST:
|
if '_create' in request.POST:
|
||||||
|
|
||||||
vc_form = forms.VirtualChassisForm(request.POST)
|
vc_form = forms.VirtualChassisForm(request.POST)
|
||||||
formset = VCMemberFormSet(request.POST)
|
vc_form.fields['master'].queryset = device_queryset
|
||||||
|
formset = VCMemberFormSet(request.POST, queryset=device_queryset)
|
||||||
|
|
||||||
if vc_form.is_valid() and formset.is_valid():
|
if vc_form.is_valid() and formset.is_valid():
|
||||||
|
|
||||||
@ -2111,8 +2117,8 @@ class VirtualChassisCreateView(PermissionRequiredMixin, View):
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
vc_form = forms.VirtualChassisForm()
|
vc_form = forms.VirtualChassisForm()
|
||||||
vc_form.fields['master'].queryset = Device.objects.filter(pk__in=device_list)
|
vc_form.fields['master'].queryset = device_queryset
|
||||||
formset = VCMemberFormSet(queryset=Device.objects.filter(pk__in=device_list))
|
formset = VCMemberFormSet(queryset=device_queryset)
|
||||||
|
|
||||||
return render(request, 'dcim/virtualchassis_edit.html', {
|
return render(request, 'dcim/virtualchassis_edit.html', {
|
||||||
'pk_form': pk_form,
|
'pk_form': pk_form,
|
||||||
@ -2128,11 +2134,17 @@ class VirtualChassisEditView(PermissionRequiredMixin, GetReturnURLMixin, View):
|
|||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
|
|
||||||
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
|
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
|
||||||
VCMemberFormSet = modelformset_factory(model=Device, fields=('vc_position', 'vc_priority'), extra=0)
|
VCMemberFormSet = modelformset_factory(
|
||||||
|
model=Device,
|
||||||
|
form=forms.DeviceVCMembershipForm,
|
||||||
|
formset=forms.BaseVCMemberFormSet,
|
||||||
|
extra=0
|
||||||
|
)
|
||||||
|
members_queryset = virtual_chassis.members.select_related('rack').order_by('vc_position')
|
||||||
|
|
||||||
vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
|
vc_form = forms.VirtualChassisForm(instance=virtual_chassis)
|
||||||
vc_form.fields['master'].queryset = virtual_chassis.members.all()
|
vc_form.fields['master'].queryset = members_queryset
|
||||||
formset = VCMemberFormSet(queryset=virtual_chassis.members.all())
|
formset = VCMemberFormSet(queryset=members_queryset)
|
||||||
|
|
||||||
return render(request, 'dcim/virtualchassis_edit.html', {
|
return render(request, 'dcim/virtualchassis_edit.html', {
|
||||||
'vc_form': vc_form,
|
'vc_form': vc_form,
|
||||||
@ -2143,11 +2155,17 @@ class VirtualChassisEditView(PermissionRequiredMixin, GetReturnURLMixin, View):
|
|||||||
def post(self, request, pk):
|
def post(self, request, pk):
|
||||||
|
|
||||||
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
|
virtual_chassis = get_object_or_404(VirtualChassis, pk=pk)
|
||||||
VCMemberFormSet = modelformset_factory(model=Device, fields=('vc_position', 'vc_priority'), extra=0)
|
VCMemberFormSet = modelformset_factory(
|
||||||
|
model=Device,
|
||||||
|
form=forms.DeviceVCMembershipForm,
|
||||||
|
formset=forms.BaseVCMemberFormSet,
|
||||||
|
extra=0
|
||||||
|
)
|
||||||
|
members_queryset = virtual_chassis.members.select_related('rack').order_by('vc_position')
|
||||||
|
|
||||||
vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
|
vc_form = forms.VirtualChassisForm(request.POST, instance=virtual_chassis)
|
||||||
vc_form.fields['master'].queryset = virtual_chassis.members.all()
|
vc_form.fields['master'].queryset = members_queryset
|
||||||
formset = VCMemberFormSet(request.POST, queryset=virtual_chassis.members.all())
|
formset = VCMemberFormSet(request.POST, queryset=members_queryset)
|
||||||
|
|
||||||
if vc_form.is_valid() and formset.is_valid():
|
if vc_form.is_valid() and formset.is_valid():
|
||||||
|
|
||||||
@ -2207,7 +2225,7 @@ class VirtualChassisAddMemberView(PermissionRequiredMixin, GetReturnURLMixin, Vi
|
|||||||
device = member_select_form.cleaned_data['device']
|
device = member_select_form.cleaned_data['device']
|
||||||
device.virtual_chassis = virtual_chassis
|
device.virtual_chassis = virtual_chassis
|
||||||
data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
|
data = {k: request.POST[k] for k in ['vc_position', 'vc_priority']}
|
||||||
membership_form = forms.DeviceVCMembershipForm(data, instance=device)
|
membership_form = forms.DeviceVCMembershipForm(data, validate_vc_position=True, instance=device)
|
||||||
|
|
||||||
if membership_form.is_valid():
|
if membership_form.is_valid():
|
||||||
|
|
||||||
|
@ -62,8 +62,18 @@
|
|||||||
<span class="text-muted">N/A</span>
|
<span class="text-muted">N/A</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ form.vc_position }}</td>
|
<td>
|
||||||
<td>{{ form.vc_priority }}</td>
|
{{ form.vc_position }}
|
||||||
|
{% if form.vc_position.errors %}
|
||||||
|
<br /><small class="text-danger">{{ form.vc_position.errors.0 }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ form.vc_priority }}
|
||||||
|
{% if form.vc_priority.errors %}
|
||||||
|
<br /><small class="text-danger">{{ form.vc_priority.errors.0 }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if virtual_chassis.pk %}
|
{% if virtual_chassis.pk %}
|
||||||
<a href="{% url 'dcim:virtualchassis_remove_member' pk=device.pk %}?return_url={% url 'dcim:virtualchassis_edit' pk=virtual_chassis.pk %}" class="btn btn-danger btn-xs{% if virtual_chassis.master == device %} disabled{% endif %}">
|
<a href="{% url 'dcim:virtualchassis_remove_member' pk=device.pk %}?return_url={% url 'dcim:virtualchassis_edit' pk=virtual_chassis.pk %}" class="btn btn-danger btn-xs{% if virtual_chassis.master == device %} disabled{% endif %}">
|
||||||
@ -80,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-md-offset-3 text-right">
|
<div class="col-md-8 col-md-offset-2 text-right">
|
||||||
{% if vc_form.instance.pk %}
|
{% if vc_form.instance.pk %}
|
||||||
<button type="submit" name="_update" class="btn btn-primary">Update</button>
|
<button type="submit" name="_update" class="btn btn-primary">Update</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
Reference in New Issue
Block a user