validate data against a model.

def validate_model(  # noqa: C901 (ignore complexity)
    model: Union[BaseModel, Type[BaseModel]], input_data: 'DictStrAny', raise_exc: bool = True, cls: 'ModelOrDc' = None
) -> Union['DictStrAny', Tuple['DictStrAny', Optional[ValidationError]]]:
    """
    validate data against a model.
    """
    values = {}
    errors = []
    names_used = set()
    config = model.__config__
    check_extra = config.extra is not Extra.ignore

    for name, field in model.__fields__.items():
        if type(field.type_) == ForwardRef:
            raise ConfigError(
                f'field "{field.name}" not yet prepared so type is still a ForwardRef, '
                f'you might need to call {model.__class__.__name__}.update_forward_refs().'
            )

        value = input_data.get(field.alias, _missing)
        using_name = False
        if value is _missing and config.allow_population_by_alias and field.alt_alias:
            value = input_data.get(field.name, _missing)
            using_name = True

        if value is _missing:
            if field.required:
                errors.append(ErrorWrapper(MissingError(), loc=field.alias, config=model.__config__))
                continue
            value = deepcopy(field.default)
            if not model.__config__.validate_all and not field.validate_always:
                values[name] = value
                continue
        elif check_extra:
            names_used.add(field.name if using_name else field.alias)

        v_, errors_ = field.validate(value, values, loc=field.alias, cls=cls or model.__class__)  # type: ignore
        if isinstance(errors_, ErrorWrapper):
            errors.append(errors_)
        elif isinstance(errors_, list):
            errors.extend(errors_)
        else:
            values[name] = v_

    if check_extra:
        extra = input_data.keys() - names_used
        if extra:
            if config.extra is Extra.allow:
                for f in extra:
                    values[f] = input_data[f]
            else:
                for f in sorted(extra):
                    errors.append(ErrorWrapper(ExtraError(), loc=f, config=config))

    if not raise_exc:
        return values, ValidationError(errors) if errors else None

    if errors:
        raise ValidationError(errors)
    return values