RobertCraigie / prisma-client-py

Prisma Client Python is an auto-generated and fully type-safe database client designed for ease of use
https://prisma-client-py.readthedocs.io
Apache License 2.0
1.86k stars 81 forks source link

Add full support for filtering by relations #17

Closed RobertCraigie closed 3 years ago

RobertCraigie commented 3 years ago

Problem

Currently only support nested filtering (through the include argument), this means that even if nothing matches the nested filter the base filter still passes, which could be confusing.

Suggested solution

Add relational fields to WhereInput, e.g.

model Post {
  ...
  categories Category[] @relation(references: [id])
}

model Category {
  id    Int    @id @default(autoincrement())
  posts Post[] @relation(references: [id])
  name  String
}
class PostWhereInput(TypedDict, total=False):
    ...
    categories: Optional['CategoryListRelationalFilter']

class CategoryListRelationalFilter(TypedDict, total=False):
    every: Optional['CategoryWhereInput']
    some: Optional['CategoryWhereInput']
    none: Optional['CategoryWhereInput']

Additional context

Playground schema screenshot

danielweil commented 2 years ago

Is this feature available in v0.4?

I have been trying to use relation filter in where clauses but I keep getting Field does not exist on enclosing type.atQuery.findUniqueRole.where.RoleWhereUniqueInput.User.

I have tried it in some different ways:

image image image image

RobertCraigie commented 2 years ago

@danielweil You need to wrap the relational field query with either is or is_not for one-to-one relations and either some, none or every for one-to many / many-to-many relations.

https://prisma-client-py.readthedocs.io/en/latest/reference/operations/#filtering-by-relational-fields

In your case it should be:

role = await client.role.find_first(
    where={
        'User': {
            'email': email,
        },
    },
)

Also it looks like you're using VSCode, if so you should definitely install the Pylance extension which will suggest the query arguments for you when you trigger auto-complete: https://github.com/RobertCraigie/prisma-client-py/blob/main/docs/showcase.gif

danielweil commented 2 years ago

Thanks!

danielweil commented 2 years ago

Do I have to apply any setting for the autocomplete to work? Here it still doesn't show up

RobertCraigie commented 2 years ago

You have to manually trigger autocomplete: https://code.visualstudio.com/docs/editor/intellisense#_intellisense-features

The Client instance also has to be resolvable by the type checker, i.e. if you hover over it should say something like client: Client

danielweil commented 2 years ago

It does not.

image image

RobertCraigie commented 2 years ago

Ah so that's your problem, you need to annotate your get_client() function to return prisma.Client, e.g.

from prisma import Client

async def get_client() -> Client:
    ...

May I ask what your use case is for writing your own Client factory?

danielweil commented 2 years ago

It already is. image

Can this be related to my python running in WSL?

I am writing it because I use Azure AD postgres JWT access to database.

RobertCraigie commented 2 years ago

The issue might be with any part of DBClient.instance() then, is that correctly resolved?

danielweil commented 2 years ago

About find_first relational query: It gave this error message.... image

Failed to validate the query: Field does not exist on enclosing type. at `Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.email

RobertCraigie commented 2 years ago

Can this be related to my python running in WSL?

I don't think so, I've used Python with WSL before without any issues

I am writing it because I use Azure AD postgres JWT access to database.

Ah I see, is this because you have to update the access token?

RobertCraigie commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at `Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.email

@danielweil I'm so sorry I just realised I gave you the wrong query, it should be:

role = await client.role.find_first(
    where={
        'User': {
            'is': {
                'email': email,
            },
        },
    },
)
danielweil commented 2 years ago

Ah I see, is this because you have to update the access token?

Yep.

About the Client type, It is very weird why it doesn't work. Check this out! image

RobertCraigie commented 2 years ago

That is bizarre, can you share how you're importing the client please?

danielweil commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at `Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.email

@danielweil I'm so sorry I just realised I gave you the wrong query, it should be:

role = await client.role.find_first(
    where={
        'User': {
            'is': {
                'email': email,
            },
        },
    },
)

Might be something here!

Failed to validate the query: Field does not exist on enclosing type. at Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.is

danielweil commented 2 years ago

That is bizarre, can you share how you're importing the client please?

Very

image

RobertCraigie commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.is

Ah sorry I missed that it was a list relation, replace is with every

danielweil commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.is

Ah sorry I missed that it was a list relation, replace is with every

That worked out!

RobertCraigie commented 2 years ago

@danielweil Glad I could help!

About the Client type, does hovering over the import resolve correctly?

RobertCraigie commented 2 years ago

If that does, are you overriding the Client name anywhere?

danielweil commented 2 years ago

It does not and I do not override it anywhere

RobertCraigie commented 2 years ago

That sounds like VSCode is using the wrong python interpreter then. Are you using a venv?

RobertCraigie commented 2 years ago

At the bottom left of your screen you should see something like this: e49abce59784c99292c9a54a01c6ef80

Click on the button that says Python ... and select the python interpreter that you're using.

danielweil commented 2 years ago

I opened VSCode in a remote session and now the imports show the correct class. By my get_client method still does not. I guess it is because of the async, but I can't get it to work.

On other hand, I have another issue with upsert:

I am trying to use it as a findOrCreate idea, but something in the where clause is causing an error: image

Failed to validate the query: Field does not exist on enclosing type. at `Mutation.upsertOneUser.where.UserWhereUniqueInput.email

RobertCraigie commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at `Mutation.upsertOneUser.where.UserWhereUniqueInput.email

You need to add an @unique to the email field definition.

model User {
  //
  email String @db.VarChar(255) @unique
  //
}
danielweil commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at `Mutation.upsertOneUser.where.UserWhereUniqueInput.email

You need to add an @unique to the email field definition.

model User {
  //
  email String @db.VarChar(255) @unique
  //
}

Thanks!

RobertCraigie commented 2 years ago

I opened VSCode in a remote session and now the imports show the correct class. By my get_client method still does not. I guess it is because of the async, but I can't get it to work.

Do you know what doesn't work? Is the Client return type resolved correctly?

danielweil commented 2 years ago

Not by my function:

image image

image

How should it be with async functions in python? I tried Coroutine but failed.

RobertCraigie commented 2 years ago

The issue is that you've typed the instance() function incorrectly. It needs to be typed to return whatever class implements the get_client() method.

danielweil commented 2 years ago

Thanks! worked perfectly

farzad-salimijazi commented 2 years ago

Failed to validate the query: Field does not exist on enclosing type. at `Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.email

@danielweil I'm so sorry I just realised I gave you the wrong query, it should be:

role = await client.role.find_first(
    where={
        'User': {
            'is': {
                'email': email,
            },
        },
    },
)

Might be something here!

Failed to validate the query: Field does not exist on enclosing type. at Query.findFirstRole.where.RoleWhereInput.User.UserListRelationFilter.is

Thanks! worked perfectly

@danielweil how did you solve this ? we tried "is", and "every" according to the documents but we can't query based on relation fields? any suggestions?

RobertCraigie commented 2 years ago

@farzad-salimijazi I'm sorry you're encountering difficulties, could you share your schema and the query you're trying to write so I can offer some help :)