Skip to content

Commit 280b38f

Browse files
authored
Only warn if a field doesn't exist on the Django model (#862)
* Only warn if a field doesn't exist on the Django model Also don't warn if the field name matches a custom field. * Expand warning messages
1 parent 1310509 commit 280b38f

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

graphene_django/tests/test_types.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,26 +320,34 @@ class Meta:
320320

321321
@with_local_registry
322322
def test_django_objecttype_fields_exclude_exist_on_model():
323-
with pytest.raises(Exception, match=r"Field .* doesn't exist"):
323+
with pytest.warns(UserWarning, match=r"Field name .* doesn't exist"):
324324

325325
class Reporter(DjangoObjectType):
326326
class Meta:
327327
model = ReporterModel
328328
fields = ["first_name", "foo", "email"]
329329

330-
with pytest.raises(Exception, match=r"Field .* doesn't exist"):
330+
with pytest.warns(
331+
UserWarning,
332+
match=r"Field name .* matches an attribute on Django model .* but it's not a model field",
333+
) as record:
331334

332335
class Reporter2(DjangoObjectType):
333336
class Meta:
334337
model = ReporterModel
335-
exclude = ["first_name", "foo", "email"]
338+
fields = ["first_name", "some_method", "email"]
336339

337-
with pytest.raises(Exception, match=r".* exists on model .* but it's not a field"):
340+
# Don't warn if selecting a custom field
341+
with pytest.warns(None) as record:
338342

339343
class Reporter3(DjangoObjectType):
344+
custom_field = String()
345+
340346
class Meta:
341347
model = ReporterModel
342-
fields = ["first_name", "some_method", "email"]
348+
fields = ["first_name", "custom_field", "email"]
349+
350+
assert len(record) == 0
343351

344352

345353
class TestDjangoObjectType:

graphene_django/types.py

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,6 @@ def construct_fields(
3333
):
3434
_model_fields = get_model_fields(model)
3535

36-
# Validate the given fields against the model's fields.
37-
model_field_names = set(field[0] for field in _model_fields)
38-
for fields_list in (only_fields, exclude_fields):
39-
if not fields_list:
40-
continue
41-
for name in fields_list:
42-
if name in model_field_names:
43-
continue
44-
45-
if hasattr(model, name):
46-
raise Exception(
47-
'"{}" exists on model {} but it\'s not a field.'.format(name, model)
48-
)
49-
else:
50-
raise Exception(
51-
'Field "{}" doesn\'t exist on model {}.'.format(name, model)
52-
)
53-
5436
fields = OrderedDict()
5537
for name, field in _model_fields:
5638
is_not_in_only = only_fields and name not in only_fields
@@ -80,6 +62,44 @@ def construct_fields(
8062
return fields
8163

8264

65+
def validate_fields(type_, model, fields, only_fields, exclude_fields):
66+
# Validate the given fields against the model's fields and custom fields
67+
all_field_names = set(fields.keys())
68+
for fields_list in (only_fields, exclude_fields):
69+
if not fields_list:
70+
continue
71+
for name in fields_list:
72+
if name in all_field_names:
73+
continue
74+
75+
if hasattr(model, name):
76+
warnings.warn(
77+
(
78+
'Field name "{field_name}" matches an attribute on Django model "{app_label}.{object_name}" '
79+
"but it's not a model field so Graphene cannot determine what type it should be. "
80+
'Either define the type of the field on DjangoObjectType "{type_}" or remove it from the "fields" list.'
81+
).format(
82+
field_name=name,
83+
app_label=model._meta.app_label,
84+
object_name=model._meta.object_name,
85+
type_=type_,
86+
)
87+
)
88+
89+
else:
90+
warnings.warn(
91+
(
92+
'Field name "{field_name}" doesn\'t exist on Django model "{app_label}.{object_name}". '
93+
'Consider removing the field from the "fields" list of DjangoObjectType "{type_}" because it has no effect.'
94+
).format(
95+
field_name=name,
96+
app_label=model._meta.app_label,
97+
object_name=model._meta.object_name,
98+
type_=type_,
99+
)
100+
)
101+
102+
83103
class DjangoObjectTypeOptions(ObjectTypeOptions):
84104
model = None # type: Model
85105
registry = None # type: Registry
@@ -211,6 +231,9 @@ def __init_subclass_with_meta__(
211231
_meta=_meta, interfaces=interfaces, **options
212232
)
213233

234+
# Validate fields
235+
validate_fields(cls, model, _meta.fields, fields, exclude)
236+
214237
if not skip_registry:
215238
registry.register(cls)
216239

0 commit comments

Comments
 (0)