예제 #1
0
def extract_proper_type_queryset_values(ctx: MethodContext, django_context: DjangoContext) -> MypyType:
    # called on QuerySet, return QuerySet of something
    assert isinstance(ctx.type, Instance)
    assert isinstance(ctx.default_return_type, Instance)

    model_type = _extract_model_type_from_queryset(ctx.type)
    if model_type is None:
        return AnyType(TypeOfAny.from_omitted_generics)

    model_cls = django_context.get_model_class_by_fullname(model_type.type.fullname)
    if model_cls is None:
        return ctx.default_return_type

    field_lookups = resolve_field_lookups(ctx.args[0], django_context)
    if field_lookups is None:
        return AnyType(TypeOfAny.from_error)

    if len(field_lookups) == 0:
        for field in django_context.get_model_fields(model_cls):
            field_lookups.append(field.attname)

    column_types: 'OrderedDict[str, MypyType]' = OrderedDict()
    for field_lookup in field_lookups:
        field_lookup_type = get_field_type_from_lookup(ctx, django_context, model_cls,
                                                       lookup=field_lookup, method='values')
        if field_lookup_type is None:
            return helpers.reparametrize_instance(ctx.default_return_type, [model_type, AnyType(TypeOfAny.from_error)])

        column_types[field_lookup] = field_lookup_type

    row_type = helpers.make_typeddict(ctx.api, column_types, set(column_types.keys()))
    return helpers.reparametrize_instance(ctx.default_return_type, [model_type, row_type])
예제 #2
0
def extract_proper_type_queryset_annotate(
        ctx: MethodContext, django_context: DjangoContext) -> MypyType:
    # called on the Instance, returns QuerySet of something
    assert isinstance(ctx.type, Instance)
    default_return_type = get_proper_type(ctx.default_return_type)
    assert isinstance(default_return_type, Instance)

    model_type = _extract_model_type_from_queryset(ctx.type)
    if model_type is None:
        return AnyType(TypeOfAny.from_omitted_generics)

    api = ctx.api

    field_types = model_type.type.metadata.get("annotated_field_types")
    kwargs = gather_kwargs(ctx)
    if kwargs:
        # For now, we don't try to resolve the output_field of the field would be, but use Any.
        added_field_types = {
            name: AnyType(TypeOfAny.implementation_artifact)
            for name, typ in kwargs.items()
        }
        if field_types is not None:
            # Annotate was called more than once, so add/update existing field types
            field_types.update(added_field_types)
        else:
            field_types = added_field_types

    fields_dict = None
    if field_types is not None:
        fields_dict = helpers.make_typeddict(api,
                                             fields=OrderedDict(field_types),
                                             required_keys=set(
                                                 field_types.keys()))
    annotated_type = get_or_create_annotated_type(api,
                                                  model_type,
                                                  fields_dict=fields_dict)

    row_type: MypyType
    if len(default_return_type.args) > 1:
        original_row_type: MypyType = default_return_type.args[1]
        row_type = original_row_type
        if isinstance(original_row_type, TypedDictType):
            row_type = api.named_generic_type("builtins.dict", [
                api.named_generic_type("builtins.str", []),
                AnyType(TypeOfAny.from_omitted_generics)
            ])
        elif isinstance(original_row_type, TupleType):
            fallback: Instance = original_row_type.partial_fallback
            if fallback is not None and fallback.type.has_base(
                    "typing.NamedTuple"):
                # TODO: Use a NamedTuple which contains the known fields, but also
                #  falls back to allowing any attribute access.
                row_type = AnyType(TypeOfAny.implementation_artifact)
            else:
                row_type = api.named_generic_type(
                    "builtins.tuple",
                    [AnyType(TypeOfAny.from_omitted_generics)])
        elif isinstance(original_row_type,
                        Instance) and original_row_type.type.has_base(
                            fullnames.MODEL_CLASS_FULLNAME):
            row_type = annotated_type
    else:
        row_type = annotated_type
    return helpers.reparametrize_instance(default_return_type,
                                          [annotated_type, row_type])