diff --git a/edtf/fields.py b/edtf/fields.py index 7dba5d4..9cf6b27 100644 --- a/edtf/fields.py +++ b/edtf/fields.py @@ -1,5 +1,6 @@ import pickle +from django.core import checks from django.core.exceptions import FieldDoesNotExist from django.db import models from django.db.models import signals @@ -188,3 +189,44 @@ def contribute_to_class(self, cls, name, **kwargs): # Only run post-initialization values update on non-abstract models if not cls._meta.abstract: signals.post_init.connect(self.update_values, sender=cls) + + def check(self, **kwargs): + errors = super().check(**kwargs) + + for field_alias in [ + "direct_input_field", + "lower_fuzzy_field", + "lower_strict_field", + "natural_text_field", + "upper_fuzzy_field", + "upper_strict_field", + ]: + errors.extend(self._check_field(field_alias)) + + return errors + + def _check_field(self, field_alias): + field_name = getattr(self, field_alias, None) + + # Check if the alias value has been provided in the field definition + if not field_name: + return [ + checks.Error( + f"You must specify a '{field_alias}' for EDTFField", + hint=None, + obj=self, + ) + ] + + # Check if the field that is referenced actually exists + try: + self.model._meta.get_field(field_name) + except FieldDoesNotExist: + return [ + checks.Error( + f"'{self.name}' refers to a non-existent '{field_alias}' field: '{field_name}'", + hint=None, + obj=self, + ) + ] + return [] diff --git a/edtf_django_tests/edtf_integration/tests.py b/edtf_django_tests/edtf_integration/tests.py index 493d0d2..da5bb83 100644 --- a/edtf_django_tests/edtf_integration/tests.py +++ b/edtf_django_tests/edtf_integration/tests.py @@ -122,3 +122,38 @@ def test_comparison(self): self.event2.date_edtf, "2019-11 is less than 2021-05-06", ) + + def test_field_related_field_specification(self): + edtf_field_on_model = TestEvent._meta.get_field("date_edtf") + required_fields = ( + "direct_input_field", + "lower_fuzzy_field", + "lower_strict_field", + "natural_text_field", + "upper_fuzzy_field", + "upper_strict_field", + ) + for field_alias in required_fields: + # Remove the alias from the edtf_field + orig_value = getattr(edtf_field_on_model, field_alias) + setattr(edtf_field_on_model, field_alias, None) + errors = edtf_field_on_model.check() + self.assertEqual(len(errors), 1) + self.assertTrue(field_alias in errors[0].msg) + # Replace the field so later tests can still work + setattr(edtf_field_on_model, field_alias, orig_value) + + # TODO: this is not working yet + # # Remove the field from the model + # referenced_field_name = getattr(edtf_field_on_model, field_alias) + # orig_fields = TestEvent._meta.local_fields + # TestEvent._meta.local_fields = [ # type: ignore + # field + # for field in TestEvent._meta.local_fields + # if field.name != referenced_field_name + # ] + # errors = TestEvent._meta.get_field("date_edtf").check() + # self.assertEqual(len(errors), 1) + # self.assertTrue(referenced_field_name in errors[0].msg) + # # Replace the field so later tests can still work + # TestEvent._meta.local_fields = orig_fields