Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type of "create_pydantic_model" is partially unknown (Python 3.11) #900

Open
Conobi opened this issue Nov 3, 2023 · 3 comments
Open

Type of "create_pydantic_model" is partially unknown (Python 3.11) #900

Conobi opened this issue Nov 3, 2023 · 3 comments

Comments

@Conobi
Copy link

Conobi commented Nov 3, 2023

Hey!
When trying to use Piccolo v1.1 with Pydantic V2, Python 3.11 and Pylance in strict mode I've got this issue:

Type of "create_pydantic_model" is partially unknown
  Type of "create_pydantic_model" is "(table: type[Table], nested: bool | Tuple[ForeignKey, ...] = False, exclude_columns: Tuple[Column, ...] = (), include_columns: Tuple[Column, ...] = (), include_default_columns: bool = False, include_readable: bool = False, all_optional: bool = False, model_name: str | None = None, deserialize_json: bool = False, recursion_depth: int = 0, max_recursion_depth: int = 5, pydantic_config: ConfigDict | None = None, **schema_extra_kwargs: Unknown) -> type[BaseModel]"

Here is any way to run a FastAPI project in strict type mode? Or for now it is recommended to use it in basic mode?

And by the way, when I'm trying this (in basic mode):

Foo_pydantic = create_pydantic_model(
    table=Foo,
)
def bar(foo: Foo_pydantic):
    pass

I also run into an issue:

Variable not allowed in type expression
(variable) Image_schema: Unknown
@dantownsend
Copy link
Member

Interesting - I haven't run Pylance in strict mode before. I just turned it on to have a look.

I've seen the Variable not allowed in type expression error before. It's because create_pydantic_model creates a new class, which is assigned to a variable. Type checkers don't like it when you use a variable as a type. Functionally it works fine, so it's OK to ignore these errors.

The way we'll fix that long term is having an alternative to create_pydantic_model which works like this instead:

class MyModel(PiccoloPydanticModel, table=MyTable):
    ...

As for the Type of "create_pydantic_model" is partially unknown error, I made this example script:

from piccolo.table import Table
from piccolo.columns import Varchar

from piccolo.utils.pydantic import create_pydantic_model


class MyTable(Table):
    name = Varchar()


MyModel = create_pydantic_model(
    table=MyTable,
)


def bar(model: MyModel):
    pass

The cause might be that Pylance is looking at Pydantic v1 for some reason. It tries to find pydantic.config.ConfigDict and it's can't because that's only available in Pydantic v2.

Screenshot 2023-11-03 at 15 42 33

Have a quick look to see if that's the case.

@Conobi
Copy link
Author

Conobi commented Nov 6, 2023

Hey!
I've been trying things for a while, and I found something really quite cool, that works with basic mode.
This works with Pylance:

class Image_pydantic(
    create_pydantic_model(
        table=Image,
        exclude_columns=(Image.parameters, Image.author),
    )
):
    pass

This syntax, for declaring a Pydantic model, is really interesting because it also allows us to change how we serialize specific fields.
For example, I can do this:

class Expense_pydantic(
    create_pydantic_model(
        table=Expense,
        exclude_columns=(Expense.customer,),
        nested=True,
    )
):
    image: Image_pydantic

And it will automatically use the above pydantic model to serialize nested data from Expense.
Another example of interesting use of this declaration:

class Customer_pydantic(
    create_pydantic_model(
        table=Customer,
        include_columns=(
            Customer.id,
            Customer.email,
            Customer.created_on,
            Customer.updated_on,
            Customer.external_token,
        ),
    )
):
    external_token: Annotated[
        Optional[str],
        Field(
            max_length=40,
            examples=["r8_FoO**********************************"],
        ),
    ] = None

    @model_validator(mode="after")
    def post_root(self) -> "Customer_schema":
        if self.external_token is not None and len(self.external_token) > 7:
            self.external_token = self.external_token[:6] + "*" * (
                len(self.external_token) - 6
            )
        if self.external_token == "":
            self.external_token = None
        return self

This would share a field stored in the DB, but only give a hint about its content.

For the Pylance error on create_pydantic_model, the only one I see is this one:
Screenshot of my VSCode type error

Thanks for the fast answer and for your work btw, Piccolo is trully underrated!!

@dantownsend
Copy link
Member

@Donokami That's a really nice solution!

What I've been doing in the past is something like:

MyTableBaseModel = create_pydantic_model(MyTable)


class MyTableModel(MyTableBaseModel):
    ...

But what you've done is definitely cleaner. I'll modify this bit of the docs to include it.

I upgraded VSCode, and Pylance is way more aggressive now. Previously I didn't encounter any warnings with the Piccolo project, but now there are quite a few I need to look into.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants