Refactor how form errors are handled

- Use form.add_error (as the django gods intended)
This commit is contained in:
Oliver Walters 2020-10-28 23:33:33 +11:00
parent fb28204dfd
commit c533f59405
8 changed files with 80 additions and 65 deletions

View File

@ -485,7 +485,7 @@ class AjaxDeleteView(AjaxMixin, UpdateView):
""" """
form_class = DeleteForm form_class = DeleteForm
ajax_form_title = "Delete Item" ajax_form_title = _("Delete Item")
ajax_template_name = "modal_delete_form.html" ajax_template_name = "modal_delete_form.html"
context_object_name = 'item' context_object_name = 'item'
@ -534,7 +534,7 @@ class AjaxDeleteView(AjaxMixin, UpdateView):
if confirmed: if confirmed:
obj.delete() obj.delete()
else: else:
form.errors['confirm_delete'] = ['Check box to confirm item deletion'] form.add_error('confirm_delete', _('Check box to confirm item deletion'))
context[self.context_object_name] = self.get_object() context[self.context_object_name] = self.get_object()
data = { data = {
@ -549,7 +549,7 @@ class EditUserView(AjaxUpdateView):
""" View for editing user information """ """ View for editing user information """
ajax_template_name = "modal_form.html" ajax_template_name = "modal_form.html"
ajax_form_title = "Edit User Information" ajax_form_title = _("Edit User Information")
form_class = EditUserForm form_class = EditUserForm
def get_object(self): def get_object(self):
@ -560,7 +560,7 @@ class SetPasswordView(AjaxUpdateView):
""" View for setting user password """ """ View for setting user password """
ajax_template_name = "InvenTree/password.html" ajax_template_name = "InvenTree/password.html"
ajax_form_title = "Set Password" ajax_form_title = _("Set Password")
form_class = SetPasswordForm form_class = SetPasswordForm
def get_object(self): def get_object(self):
@ -579,9 +579,9 @@ class SetPasswordView(AjaxUpdateView):
# Passwords must match # Passwords must match
if not p1 == p2: if not p1 == p2:
error = 'Password fields must match' error = _('Password fields must match')
form.errors['enter_password'] = [error] form.add_error('enter_password', error)
form.errors['confirm_password'] = [error] form.add_error('confirm_password', error)
valid = False valid = False

View File

@ -74,7 +74,7 @@ class BuildCancel(AjaxUpdateView):
if confirm: if confirm:
build.cancelBuild(request.user) build.cancelBuild(request.user)
else: else:
form.errors['confirm_cancel'] = [_('Confirm build cancellation')] form.add_error('confirm_cancel', _('Confirm build cancellation'))
valid = False valid = False
data = { data = {
@ -128,8 +128,8 @@ class BuildAutoAllocate(AjaxUpdateView):
valid = False valid = False
if confirm is False: if confirm is False:
form.errors['confirm'] = [_('Confirm stock allocation')] form.add_error('confirm', _('Confirm stock allocation'))
form.non_field_errors = [_('Check the confirmation box at the bottom of the list')] form.add_error(None, _('Check the confirmation box at the bottom of the list'))
else: else:
build.autoAllocate() build.autoAllocate()
valid = True valid = True
@ -163,8 +163,8 @@ class BuildUnallocate(AjaxUpdateView):
valid = False valid = False
if confirm is False: if confirm is False:
form.errors['confirm'] = [_('Confirm unallocation of build stock')] form.add_error('confirm', _('Confirm unallocation of build stock'))
form.non_field_errors = [_('Check the confirmation box')] form.add_error(None, _('Check the confirmation box'))
else: else:
build.unallocateStock() build.unallocateStock()
valid = True valid = True
@ -266,15 +266,13 @@ class BuildComplete(AjaxUpdateView):
valid = False valid = False
if confirm is False: if confirm is False:
form.errors['confirm'] = [ form.add_error('confirm', _('Confirm completion of build'))
_('Confirm completion of build'),
]
else: else:
try: try:
location = StockLocation.objects.get(id=loc_id) location = StockLocation.objects.get(id=loc_id)
valid = True valid = True
except (ValueError, StockLocation.DoesNotExist): except (ValueError, StockLocation.DoesNotExist):
form.errors['location'] = [_('Invalid location selected')] form.add_error('location', _('Invalid location selected'))
serials = [] serials = []
@ -291,24 +289,20 @@ class BuildComplete(AjaxUpdateView):
# Exctract a list of provided serial numbers # Exctract a list of provided serial numbers
serials = ExtractSerialNumbers(sn, build.quantity) serials = ExtractSerialNumbers(sn, build.quantity)
existing = [] existing = build.part.find_conflicting_serial_numbers(serials)
for serial in serials:
if build.part.checkIfSerialNumberExists(serial):
existing.append(serial)
if len(existing) > 0: if len(existing) > 0:
exists = ",".join([str(x) for x in existing]) exists = ",".join([str(x) for x in existing])
form.errors['serial_numbers'] = [_('The following serial numbers already exist: ({sn})'.format(sn=exists))] form.add_error('serial_numbers', _('The following serial numbers already exist: ({sn})'.format(sn=exists)))
valid = False valid = False
except ValidationError as e: except ValidationError as e:
form.errors['serial_numbers'] = e.messages form.add_error('serial_numbers', e.messages)
valid = False valid = False
if valid: if valid:
if not build.completeBuild(location, serials, request.user): if not build.completeBuild(location, serials, request.user):
form.non_field_errors = [('Build could not be completed')] form.add_error(None, _('Build could not be completed'))
valid = False valid = False
data = { data = {

View File

@ -415,7 +415,7 @@ class PurchaseOrderCancel(AjaxUpdateView):
valid = False valid = False
if not confirm: if not confirm:
form.errors['confirm'] = [_('Confirm order cancellation')] form.add_error('confirm', _('Confirm order cancellation'))
else: else:
valid = True valid = True
@ -448,13 +448,13 @@ class SalesOrderCancel(AjaxUpdateView):
valid = False valid = False
if not confirm: if not confirm:
form.errors['confirm'] = [_('Confirm order cancellation')] form.add_error('confirm', _('Confirm order cancellation'))
else: else:
valid = True valid = True
if valid: if valid:
if not order.cancel_order(): if not order.cancel_order():
form.non_field_errors = [_('Could not cancel order')] form.add_error(None, _('Could not cancel order'))
valid = False valid = False
data = { data = {
@ -484,7 +484,7 @@ class PurchaseOrderIssue(AjaxUpdateView):
valid = False valid = False
if not confirm: if not confirm:
form.errors['confirm'] = [_('Confirm order placement')] form.add_error('confirm', _('Confirm order placement'))
else: else:
valid = True valid = True
@ -558,13 +558,13 @@ class SalesOrderShip(AjaxUpdateView):
valid = False valid = False
if not confirm: if not confirm:
form.errors['confirm'] = [_('Confirm order shipment')] form.add_error('confirm', _('Confirm order shipment'))
else: else:
valid = True valid = True
if valid: if valid:
if not order.ship_order(request.user): if not order.ship_order(request.user):
form.non_field_errors = [_('Could not ship order')] form.add_error(None, _('Could not ship order'))
valid = False valid = False
data = { data = {
@ -1135,7 +1135,7 @@ class POLineItemCreate(AjaxCreateView):
order = PurchaseOrder.objects.get(id=order_id) order = PurchaseOrder.objects.get(id=order_id)
except (ValueError, PurchaseOrder.DoesNotExist): except (ValueError, PurchaseOrder.DoesNotExist):
order = None order = None
form.errors['order'] = [_('Invalid Purchase Order')] form.add_error('order', _('Invalid Purchase Order'))
valid = False valid = False
try: try:
@ -1143,12 +1143,12 @@ class POLineItemCreate(AjaxCreateView):
if order is not None: if order is not None:
if not sp.supplier == order.supplier: if not sp.supplier == order.supplier:
form.errors['part'] = [_('Supplier must match for Part and Order')] form.add_error('part', _('Supplier must match for Part and Order'))
valid = False valid = False
except (SupplierPart.DoesNotExist, ValueError): except (SupplierPart.DoesNotExist, ValueError):
valid = False valid = False
form.errors['part'] = [_('Invalid SupplierPart selection')] form.add_error('part', _('Invalid SupplierPart selection'))
data = { data = {
'form_valid': valid, 'form_valid': valid,

View File

@ -360,7 +360,7 @@ class Part(MPTTModel):
# And recursively check too # And recursively check too
item.sub_part.checkAddToBOM(parent) item.sub_part.checkAddToBOM(parent)
def checkIfSerialNumberExists(self, sn): def checkIfSerialNumberExists(self, sn, exclude_self=False):
""" """
Check if a serial number exists for this Part. Check if a serial number exists for this Part.
@ -369,10 +369,27 @@ class Part(MPTTModel):
""" """
parts = Part.objects.filter(tree_id=self.tree_id) parts = Part.objects.filter(tree_id=self.tree_id)
stock = StockModels.StockItem.objects.filter(part__in=parts, serial=sn) stock = StockModels.StockItem.objects.filter(part__in=parts, serial=sn)
if exclude_self:
stock = stock.exclude(pk=self.pk)
return stock.exists() return stock.exists()
def find_conflicting_serial_numbers(self, serials):
"""
For a provided list of serials, return a list of those which are conflicting.
"""
conflicts = []
for serial in serials:
if self.checkIfSerialNumberExists(serial, exclude_self=True):
conflicts.append(serial)
return conflicts
def getLatestSerialNumber(self): def getLatestSerialNumber(self):
""" """
Return the "latest" serial number for this Part. Return the "latest" serial number for this Part.

View File

@ -446,9 +446,9 @@ class PartDuplicate(AjaxCreateView):
confirmed = str2bool(request.POST.get('confirm_creation', False)) confirmed = str2bool(request.POST.get('confirm_creation', False))
if not confirmed: if not confirmed:
form.errors['confirm_creation'] = ['Possible matches exist - confirm creation of new part'] msg = _('Possible matches exist - confirm creation of new part')
form.add_error('confirm_creation', msg)
form.pre_form_warning = 'Possible matches exist - confirm creation of new part' form.pre_form_warning = msg
valid = False valid = False
data = { data = {
@ -576,9 +576,10 @@ class PartCreate(AjaxCreateView):
confirmed = str2bool(request.POST.get('confirm_creation', False)) confirmed = str2bool(request.POST.get('confirm_creation', False))
if not confirmed: if not confirmed:
form.errors['confirm_creation'] = ['Possible matches exist - confirm creation of new part'] msg = _('Possible matches exist - confirm creation of new part')
form.add_error('confirm_creation', msg)
form.pre_form_warning = 'Possible matches exist - confirm creation of new part' form.pre_form_warning = msg
valid = False valid = False
data = { data = {
@ -915,7 +916,7 @@ class BomValidate(AjaxUpdateView):
if confirmed: if confirmed:
part.validate_bom(request.user) part.validate_bom(request.user)
else: else:
form.errors['validate'] = ['Confirm that the BOM is valid'] form.add_error('validate', _('Confirm that the BOM is valid'))
data = { data = {
'form_valid': confirmed 'form_valid': confirmed
@ -1054,7 +1055,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
bom_file_valid = False bom_file_valid = False
if bom_file is None: if bom_file is None:
self.form.errors['bom_file'] = [_('No BOM file provided')] self.form.add_error('bom_file', _('No BOM file provided'))
else: else:
# Create a BomUploadManager object - will perform initial data validation # Create a BomUploadManager object - will perform initial data validation
# (and raise a ValidationError if there is something wrong with the file) # (and raise a ValidationError if there is something wrong with the file)
@ -1065,7 +1066,7 @@ class BomUpload(InvenTreeRoleMixin, FormView):
errors = e.error_dict errors = e.error_dict
for k, v in errors.items(): for k, v in errors.items():
self.form.errors[k] = v self.form.add_error(k, v)
if bom_file_valid: if bom_file_valid:
# BOM file is valid? Proceed to the next step! # BOM file is valid? Proceed to the next step!

View File

@ -814,14 +814,11 @@ class StockItem(MPTTModel):
raise ValidationError({"quantity": _("Quantity does not match serial numbers")}) raise ValidationError({"quantity": _("Quantity does not match serial numbers")})
# Test if each of the serial numbers are valid # Test if each of the serial numbers are valid
existing = [] existing = self.part.find_conflicting_serial_numbers(serials)
for serial in serials:
if self.part.checkIfSerialNumberExists(serial):
existing.append(serial)
if len(existing) > 0: if len(existing) > 0:
raise ValidationError({"serial_numbers": _("Serial numbers already exist: ") + str(existing)}) exists = ','.join([str(x) for x in existing])
raise ValidationError({"serial_numbers": _("Serial numbers already exist") + ': ' + exists})
# Create a new stock item for each unique serial number # Create a new stock item for each unique serial number
for serial in serials: for serial in serials:

View File

@ -407,6 +407,13 @@ class VariantTest(StockTest):
self.assertEqual(chair.getLatestSerialNumber(), '22') self.assertEqual(chair.getLatestSerialNumber(), '22')
# Check for conflicting serial numbers
to_check = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
conflicts = chair.find_conflicting_serial_numbers(to_check)
self.assertEqual(len(conflicts), 6)
# Same operations on a sub-item # Same operations on a sub-item
variant = Part.objects.get(pk=10003) variant = Part.objects.get(pk=10003)
self.assertEqual(variant.getLatestSerialNumber(), '22') self.assertEqual(variant.getLatestSerialNumber(), '22')

View File

@ -417,8 +417,8 @@ class StockItemDeleteTestData(AjaxUpdateView):
confirm = str2bool(request.POST.get('confirm', False)) confirm = str2bool(request.POST.get('confirm', False))
if confirm is not True: if confirm is not True:
form.errors['confirm'] = [_('Confirm test data deletion')] form.add_error('confirm', _('Confirm test data deletion'))
form.non_field_errors = [_('Check the confirmation box')] form.add_error(None, _('Check the confirmation box'))
else: else:
stock_item.test_results.all().delete() stock_item.test_results.all().delete()
valid = True valid = True
@ -918,7 +918,7 @@ class StockItemUninstall(AjaxView, FormMixin):
if not confirmed: if not confirmed:
valid = False valid = False
form.errors['confirm'] = [_('Confirm stock adjustment')] form.add_error('confirm', _('Confirm stock adjustment'))
data = { data = {
'form_valid': valid, 'form_valid': valid,
@ -1116,7 +1116,7 @@ class StockAdjust(AjaxView, FormMixin):
if not confirmed: if not confirmed:
valid = False valid = False
form.errors['confirm'] = [_('Confirm stock adjustment')] form.add_error('confirm', _('Confirm stock adjustment'))
data = { data = {
'form_valid': valid, 'form_valid': valid,
@ -1416,7 +1416,7 @@ class StockItemSerialize(AjaxUpdateView):
try: try:
numbers = ExtractSerialNumbers(serials, quantity) numbers = ExtractSerialNumbers(serials, quantity)
except ValidationError as e: except ValidationError as e:
form.errors['serial_numbers'] = e.messages form.add_error('serial_numbers', e.messages)
valid = False valid = False
numbers = [] numbers = []
@ -1428,9 +1428,9 @@ class StockItemSerialize(AjaxUpdateView):
for k in messages.keys(): for k in messages.keys():
if k in ['quantity', 'destination', 'serial_numbers']: if k in ['quantity', 'destination', 'serial_numbers']:
form.errors[k] = messages[k] form.add_error(k, messages[k])
else: else:
form.non_field_errors = [messages[k]] form.add_error(None, messages[k])
valid = False valid = False
@ -1622,14 +1622,14 @@ class StockItemCreate(AjaxCreateView):
part = None part = None
quantity = 1 quantity = 1
valid = False valid = False
form.errors['quantity'] = [_('Invalid quantity')] form.add_error('quantity', _('Invalid quantity'))
if quantity < 0: if quantity < 0:
form.errors['quantity'] = [_('Quantity cannot be less than zero')] form.add_error('quantity', _('Quantity cannot be less than zero'))
valid = False valid = False
if part is None: if part is None:
form.errors['part'] = [_('Invalid part selection')] form.add_error('part', _('Invalid part selection'))
else: else:
# A trackable part must provide serial numbesr # A trackable part must provide serial numbesr
if part.trackable: if part.trackable:
@ -1642,15 +1642,14 @@ class StockItemCreate(AjaxCreateView):
try: try:
serials = ExtractSerialNumbers(sn, quantity) serials = ExtractSerialNumbers(sn, quantity)
existing = [] existing = part.find_conflicting_serial_numbers(serials)
for serial in serials:
if part.checkIfSerialNumberExists(serial):
existing.append(serial)
if len(existing) > 0: if len(existing) > 0:
exists = ",".join([str(x) for x in existing]) exists = ",".join([str(x) for x in existing])
form.errors['serial_numbers'] = [_('The following serial numbers already exist: ({sn})'.format(sn=exists))] form.add_error(
'serial_numbers',
_('Serial numbers already exist') + ': ' + exists
)
valid = False valid = False
else: else:
@ -1682,7 +1681,7 @@ class StockItemCreate(AjaxCreateView):
valid = True valid = True
except ValidationError as e: except ValidationError as e:
form.errors['serial_numbers'] = e.messages form.add_error('serial_numbers', e.messages)
valid = False valid = False
else: else: