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.81k stars 76 forks source link

Request for guides / tutorials / documentation #175

Open RobertCraigie opened 2 years ago

RobertCraigie commented 2 years ago

I am looking into improving the documentation for this project and a part of that is creating guides for using Prisma Client Python with existing frameworks.

This issue serves as the place to request that I write a tutorial / guide for any framework, please leave a comment if there is any specific framework you would like me to write a tutorial for!

Guides:

HOZHENWAI commented 1 year ago

Hi,

Can we documentation on how to set up tests using the client? I'm using pytest and I have no clue if there an easy way to tear down the created objects after the tests. Moreover, is there a way to configure the client to use a mock instance of the database for testing?

Thank you.

RobertCraigie commented 1 year ago

Hey @HOZHENWAI! Thanks for the suggestion, this definitely needs good documentation. In the meantime you can take a look at the conftest.py that we use internally: https://github.com/RobertCraigie/prisma-client-py/blob/main/lib/testing/shared_conftest/_shared_conftest.py

This shows how you can automatically connect to the client and cleanup the database. Let me know if anything else isn't clear / if you have any questions!

Moreover, is there a way to configure the client to use a mock instance of the database for testing?

I'd highly recommend not mocking the Prisma Client. By mocking you are leaving an important layer in your application untested. In my opinion your tests should use as little mocking as possible, only doing so when it would be difficult / expensive to not mock.

If you mock the client you could easily introduce bugs that are not caught by your tests. For example, consider the case where you're running a find_many query where you want all of the records that have a field with a value greater than the given input. By mocking the client, this interaction is never tested. You could accidentally change the query from a gt to a lt or you could be assuming the input is always positive but a negative input is given and your tests would pass.

HOZHENWAI commented 1 year ago

Hello @RobertCraigie. I have some issues understanding some part of the shared_conftest, so if you don't mind, I'll try to write what I understood and if you could correct me I'd be grateful:

RobertCraigie commented 1 year ago

it seems to me that the cleanup is done is the cleanupclient async function, what I don't understand is the process as batch "groups write queries into a single transaction". So I have to two questions, if we use a create or a create_many, does the result both get stored into the batch?

I'm not sure I understand what you mean by "does the result both get stored into the batch?". The query batching is done so that there is only one internal HTTP call (how we communicate with the Prisma Query Engine) for a slight performance improvement.

This documentation might be helpful for you if you haven't already read it: https://prisma-client-py.readthedocs.io/en/stable/reference/batching/

about the client_fixture, it looks like the cleanup is always done before returning the client, shouldn't it be after a yield to cleanup the database?

For our internal tests we don't really care what the result of the database is, just that before each test the database is clean. If you wanted to perform cleanup after a test is ran, that would be perfectly fine but you'd also want to cleanup before as well, otherwise the first test in your test suite would not have a clean database.

it seems to me that setup_client_fixture is actually the one cleaning up the database after each test, is it right?

Yes that is right, apart from that it is cleaning up the database before each test.

how does the persist_data marker work, set it up for a test and the next test will have the created objects in database from the last test?

The persist data marker works by skipping the database cleanup we do, the way it does this here: https://github.com/RobertCraigie/prisma-client-py/blob/49f54002050b04dad2a846be7c2c7af9072173b2/lib/testing/shared_conftest/_shared_conftest.py#L55

The way I use it is by defining a fixture that will be automatically ran and then decorating every test in the file with persist_data, you can see an example of this here: https://github.com/RobertCraigie/prisma-client-py/blob/main/databases/tests/test_group_by.py

I'd also like to point out that this is purely used for convenience / test speedup, you can completely omit using this in your tests.

if i'm not wrong the event_loop should only be used for low level functions used by prisma, right, I am not sure of it's importance for high level testing.

No you are completely right. The only wwreason it is there is to change the default behaviour of pytest-asyncio which will close the event loop and re-create it for each test. This is a perfectly reasonable default but it means we wouldn't be able to re-use the same Prisma client for each test and would have to connect() again which makes the tests much slower.

Thank you for your remark. TIL about mocking client.

Always happy to help!

sammyolo commented 1 year ago

Could the custom generator be expanded in terms of documentation, I think it's possible but can you use custom generator like "extension" in prisma nodejs? I'd like to add methods to all my models and partials.

RobertCraigie commented 1 year ago

Yes you could use a custom generator to generate models that inherit from the default. But it should be noted that you'd have to update your code to use that everywhere which may be a bit annoying.

Unfortunately an equivalent of client extensions isn't possible due to the limitations of the Python type system.

Also unfortunately I don't have the capacity to provide an example right now but I'm happy to help if you run into any issues :)

ntindle commented 9 months ago

I'd love a tutorial for working with FastAPI Dependency Injection with a class that contains Prisma.

class Commons:
    def __init__(self) -> None:
        self.settings = Settings()
        self.prisma = Prisma()
        self.other_things = ...

    async def _init(self):
        await self.prisma.connect()
        self.user_storage = self.prisma.user

async def get_commons() -> Commons:
    commons = Commons()
    await commons._init()
    return commons

and then

router = APIRouter(
    prefix="/test",
    dependencies=[
        ...
    ],
)

as well as

@router.post("/create_user")
async def create_user(
    request: Request,
    background_tasks: BackgroundTasks,
    commons=Depends(get_commons),
):
    await commons.prisma.user.create(data={...})

I'm almost certain there's a better way to do this without re-initing every request but I'm not familiar enough with python prisma to do it.

Asleep123 commented 9 months ago

More documentation on updating arrays. I’m not quite sure how to append to an array with a reference to another object.

ntindle commented 4 months ago

Docs on how to use npm-based generators alongside the Python ones. A few examples of what I'm talking about below: