mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-11-04 18:08:01 +03:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									1310509fa1
								
							
						
					
					
						commit
						280b38f804
					
				| 
						 | 
					@ -320,26 +320,34 @@ def test_django_objecttype_fields_exclude_type_checking():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@with_local_registry
 | 
					@with_local_registry
 | 
				
			||||||
def test_django_objecttype_fields_exclude_exist_on_model():
 | 
					def test_django_objecttype_fields_exclude_exist_on_model():
 | 
				
			||||||
    with pytest.raises(Exception, match=r"Field .* doesn't exist"):
 | 
					    with pytest.warns(UserWarning, match=r"Field name .* doesn't exist"):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class Reporter(DjangoObjectType):
 | 
					        class Reporter(DjangoObjectType):
 | 
				
			||||||
            class Meta:
 | 
					            class Meta:
 | 
				
			||||||
                model = ReporterModel
 | 
					                model = ReporterModel
 | 
				
			||||||
                fields = ["first_name", "foo", "email"]
 | 
					                fields = ["first_name", "foo", "email"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(Exception, match=r"Field .* doesn't exist"):
 | 
					    with pytest.warns(
 | 
				
			||||||
 | 
					        UserWarning,
 | 
				
			||||||
 | 
					        match=r"Field name .* matches an attribute on Django model .* but it's not a model field",
 | 
				
			||||||
 | 
					    ) as record:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class Reporter2(DjangoObjectType):
 | 
					        class Reporter2(DjangoObjectType):
 | 
				
			||||||
            class Meta:
 | 
					            class Meta:
 | 
				
			||||||
                model = ReporterModel
 | 
					                model = ReporterModel
 | 
				
			||||||
                exclude = ["first_name", "foo", "email"]
 | 
					                fields = ["first_name", "some_method", "email"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(Exception, match=r".* exists on model .* but it's not a field"):
 | 
					    # Don't warn if selecting a custom field
 | 
				
			||||||
 | 
					    with pytest.warns(None) as record:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class Reporter3(DjangoObjectType):
 | 
					        class Reporter3(DjangoObjectType):
 | 
				
			||||||
 | 
					            custom_field = String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            class Meta:
 | 
					            class Meta:
 | 
				
			||||||
                model = ReporterModel
 | 
					                model = ReporterModel
 | 
				
			||||||
                fields = ["first_name", "some_method", "email"]
 | 
					                fields = ["first_name", "custom_field", "email"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert len(record) == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestDjangoObjectType:
 | 
					class TestDjangoObjectType:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,24 +33,6 @@ def construct_fields(
 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    _model_fields = get_model_fields(model)
 | 
					    _model_fields = get_model_fields(model)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Validate the given fields against the model's fields.
 | 
					 | 
				
			||||||
    model_field_names = set(field[0] for field in _model_fields)
 | 
					 | 
				
			||||||
    for fields_list in (only_fields, exclude_fields):
 | 
					 | 
				
			||||||
        if not fields_list:
 | 
					 | 
				
			||||||
            continue
 | 
					 | 
				
			||||||
        for name in fields_list:
 | 
					 | 
				
			||||||
            if name in model_field_names:
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if hasattr(model, name):
 | 
					 | 
				
			||||||
                raise Exception(
 | 
					 | 
				
			||||||
                    '"{}" exists on model {} but it\'s not a field.'.format(name, model)
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                raise Exception(
 | 
					 | 
				
			||||||
                    'Field "{}" doesn\'t exist on model {}.'.format(name, model)
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fields = OrderedDict()
 | 
					    fields = OrderedDict()
 | 
				
			||||||
    for name, field in _model_fields:
 | 
					    for name, field in _model_fields:
 | 
				
			||||||
        is_not_in_only = only_fields and name not in only_fields
 | 
					        is_not_in_only = only_fields and name not in only_fields
 | 
				
			||||||
| 
						 | 
					@ -80,6 +62,44 @@ def construct_fields(
 | 
				
			||||||
    return fields
 | 
					    return fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def validate_fields(type_, model, fields, only_fields, exclude_fields):
 | 
				
			||||||
 | 
					    # Validate the given fields against the model's fields and custom fields
 | 
				
			||||||
 | 
					    all_field_names = set(fields.keys())
 | 
				
			||||||
 | 
					    for fields_list in (only_fields, exclude_fields):
 | 
				
			||||||
 | 
					        if not fields_list:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        for name in fields_list:
 | 
				
			||||||
 | 
					            if name in all_field_names:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if hasattr(model, name):
 | 
				
			||||||
 | 
					                warnings.warn(
 | 
				
			||||||
 | 
					                    (
 | 
				
			||||||
 | 
					                        'Field name "{field_name}" matches an attribute on Django model "{app_label}.{object_name}" '
 | 
				
			||||||
 | 
					                        "but it's not a model field so Graphene cannot determine what type it should be. "
 | 
				
			||||||
 | 
					                        'Either define the type of the field on DjangoObjectType "{type_}" or remove it from the "fields" list.'
 | 
				
			||||||
 | 
					                    ).format(
 | 
				
			||||||
 | 
					                        field_name=name,
 | 
				
			||||||
 | 
					                        app_label=model._meta.app_label,
 | 
				
			||||||
 | 
					                        object_name=model._meta.object_name,
 | 
				
			||||||
 | 
					                        type_=type_,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                warnings.warn(
 | 
				
			||||||
 | 
					                    (
 | 
				
			||||||
 | 
					                        'Field name "{field_name}" doesn\'t exist on Django model "{app_label}.{object_name}". '
 | 
				
			||||||
 | 
					                        'Consider removing the field from the "fields" list of DjangoObjectType "{type_}" because it has no effect.'
 | 
				
			||||||
 | 
					                    ).format(
 | 
				
			||||||
 | 
					                        field_name=name,
 | 
				
			||||||
 | 
					                        app_label=model._meta.app_label,
 | 
				
			||||||
 | 
					                        object_name=model._meta.object_name,
 | 
				
			||||||
 | 
					                        type_=type_,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DjangoObjectTypeOptions(ObjectTypeOptions):
 | 
					class DjangoObjectTypeOptions(ObjectTypeOptions):
 | 
				
			||||||
    model = None  # type: Model
 | 
					    model = None  # type: Model
 | 
				
			||||||
    registry = None  # type: Registry
 | 
					    registry = None  # type: Registry
 | 
				
			||||||
| 
						 | 
					@ -211,6 +231,9 @@ class DjangoObjectType(ObjectType):
 | 
				
			||||||
            _meta=_meta, interfaces=interfaces, **options
 | 
					            _meta=_meta, interfaces=interfaces, **options
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Validate fields
 | 
				
			||||||
 | 
					        validate_fields(cls, model, _meta.fields, fields, exclude)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not skip_registry:
 | 
					        if not skip_registry:
 | 
				
			||||||
            registry.register(cls)
 | 
					            registry.register(cls)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user