mixins.py 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import time
  2. from decimal import Decimal
  3. from django import forms
  4. from django.core.validators import MaxValueValidator, MinValueValidator
  5. from django.utils.translation import gettext_lazy as _
  6. __all__ = (
  7. 'BackgroundJobMixin',
  8. 'CheckLastUpdatedMixin',
  9. 'DistanceValidationMixin',
  10. )
  11. class BackgroundJobMixin(forms.Form):
  12. background_job = forms.BooleanField(
  13. label=_('Background job'),
  14. help_text=_("Execute this task via a background job"),
  15. required=False,
  16. )
  17. def __init__(self, *args, **kwargs):
  18. super().__init__(*args, **kwargs)
  19. # Declare background_job a meta field
  20. if hasattr(self, 'meta_fields'):
  21. self.meta_fields.append('background_job')
  22. else:
  23. self.meta_fields = ['background_job']
  24. class CheckLastUpdatedMixin(forms.Form):
  25. """
  26. Checks whether the object being saved has been updated since the form was initialized. If so, validation fails.
  27. This prevents a user from inadvertently overwriting any changes made to the object between when the form was
  28. initialized and when it was submitted.
  29. This validation does not apply to newly created objects, or if the `_init_time` field is not present in the form
  30. data.
  31. """
  32. _init_time = forms.DecimalField(
  33. initial=time.time,
  34. required=False,
  35. widget=forms.HiddenInput()
  36. )
  37. def clean(self):
  38. super().clean()
  39. # Skip for absent or newly created instances
  40. if not self.instance or not self.instance.pk:
  41. return
  42. # Skip if a form init time has not been specified
  43. if not (form_init_time := self.cleaned_data.get('_init_time')):
  44. return
  45. # Skip if the object does not have a last_updated value
  46. if not (last_updated := getattr(self.instance, 'last_updated', None)):
  47. return
  48. # Check that the submitted initialization time is not earlier than the object's modification time
  49. if form_init_time < last_updated.timestamp():
  50. raise forms.ValidationError(_(
  51. "This object has been modified since the form was rendered. Please consult the object's change "
  52. "log for details."
  53. ))
  54. class DistanceValidationMixin(forms.Form):
  55. distance = forms.DecimalField(
  56. required=False,
  57. validators=[
  58. MinValueValidator(Decimal(0)),
  59. MaxValueValidator(Decimal(100000)),
  60. ]
  61. )