Ticket #16986: model_clean_mixin.py

File model_clean_mixin.py, 2.3 KB (added by David Foster, 8 years ago)
Line 
1class _ModelCleanWorkaroundMixin(object):
2    """
3    Mixin for Model subclasses that wish to raise ValidationErrors
4    in Model.clean() that target individual fields. As of Django 2.3
5    this is not possible, due to <https://code.djangoproject.com/ticket/16986>.
6    """
7   
8    # Workaround for: https://code.djangoproject.com/ticket/16986
9    def clean_fields(self, **kwargs):
10        try:
11            super(_ModelCleanWorkaroundMixin, self).clean_fields(**kwargs)
12        except ValidationError as e1:
13            if not hasattr(e1, 'message_dict'):
14                e1.message_dict = {NON_FIELD_ERRORS: e1.messages}
15        else:
16            e1 = None
17       
18        try:
19            # If not for this workaround, this method would be called clean()
20            self.clean_self()
21        except ValidationError as e2:
22            pass
23        else:
24            e2 = None
25       
26        if e1 and e2:
27            raise ValidationError(dict(e1.message_dict, **e2.message_dict))
28        elif e1 and not e2:
29            raise e1
30        elif not e1 and e2:
31            raise e2
32
33def _createLatitudeField():
34    return models.DecimalField(max_digits=8, decimal_places=6,
35        validators=[
36            validators.MinValueValidator(Decimal('-90.000000')),
37            validators.MaxValueValidator(Decimal('90.000000')),
38        ])
39
40def _createLongitudeField():
41    return models.DecimalField(max_digits=9, decimal_places=6,
42        validators=[
43            validators.MinValueValidator(Decimal('-180.000000')),
44            validators.MaxValueValidator(Decimal('180.000000')),
45        ])
46
47
48class Region(_ModelCleanWorkaroundMixin, models.Model):
49    south       = _createLatitudeField()
50    west        = _createLongitudeField()
51    north       = _createLatitudeField()
52    east        = _createLongitudeField()
53   
54    def clean_self(self):
55        message_dict = defaultdict(list)
56       
57        if self.west and self.east and (not self.west <= self.east):
58            message_dict['south'].append('Southwest pin must be west of the northeast pin.')
59        if self.south and self.north and (not self.south <= self.north):
60            message_dict['south'].append('Southwest pin must be south of the northeast pin.')
61       
62        if len(message_dict):
63            raise ValidationError(message_dict)
Back to Top