Prisma Client Python is an auto-generated and fully type-safe database client designed for ease of use
APACHE-2.0 License
Bot releases are hidden (Show)
The last release, v0.14.0
, included a regression with the handling of optional values for BigInt
, Decimal
& Json
fields where an error would be raised if the value was None
.
Massive thank you to @AstreaTSS for a very detailed bug report and contributing a fix!
.create_many()
for SQLiteThe create_many()
method is now supported in SQLite!
total = await client.user.create_many([{'name': 'Robert'}, {'name': 'Tegan'}])
# 2
However the skip_duplicates
argument is unfortunately not supported yet.
Massive thank you to @AdeelK93 for contributing this feature!
connect_or_create
You can now connect or create a relational record through the .create()
method, for example:
post = await client.post.create(
data={
'title': 'Post 1',
'published': True,
'author': {
'connect_or_create': {
'where': {
'id': user.id,
},
'create': {
'name': 'Robert',
},
},
},
},
include={
'author': True,
},
)
Thanks to @AdeelK93 for contributing this feature!
A new search
parameter has been added for string field filters in PostgreSQL and MySQL. The syntax within the string is entirely dependent on the underlying database.
For example, in PostgreSQL, a filter for all posts where the title contains "cat" or "dog" would look like this:
posts = await client.post.find_many(
where={
'title': {
'search': 'cats | dogs',
},
}
)
To use full text search you'll need to add it as a preview feature
generator client {
provider = "prisma-client-py"
previewFeatures = ["fullTextSearch"]
}
See these docs for more details on full text search.
Massive thank you again(!) to @AdeelK93 for contirbuting this feature.
Published by RobertCraigie 3 months ago
Prisma now has preview support for splitting your schema.prisma
file into multiple separate files!
For more information see their docs.
Python 3.7 has been EOL for a while now and accounts for less than 1% of the downloads of Prisma Client Python, as such, support was dropped in this release.
The internal Prisma version has been updated from v5.11.0
to v5.17.0
which includes a lot of improvements, see the Prisma release notes for more information.
Published by RobertCraigie 7 months ago
This is a patch release to fix a minor regression with datasource env vars and datasource overriding.
Published by RobertCraigie 7 months ago
Up until now, if you had a schema like this defined:
model OrgMember {
// ...
}
You would have to access it like this:
from prisma import Prisma
client = Prisma()
member = client.orgmember.find_unique(...)
With this release you can customise the instance name on the client, e.g. orgmember
, to whatever you would like! For example:
/// @Python(instance_name: "org_member")
model OrgMember {
// ...
}
The OrgMember
model can now be accessed through the client like so:
client = Prisma()
client.org_member.find_unique(...)
A future release will also add support for changing the default casing implementation used in the client!
The internal Prisma version has been bumped from 5.8.0
to 5.11.0
, you can find the release notes for each version below:
Up until now Prisma Client Python didn't declare support for Python 3.12 but it will have worked at runtime. The only user facing changes in this release are to the python -m prisma_cleanup
script to avoid a deprecation warning.
Some minor changes were made to modules that were intended to be private but were not marked with a preceding _
.
prisma.builder
has been moved to prisma._builder
prisma.builder
module, QueryBuilder
, has new required constructor argumentsI've also started laying the ground work for usage of both the async client and the sync client at the same time and hope to support this in the next release!
Published by RobertCraigie 9 months ago
This release bumps the internal Prisma version from 5.4.2
to 5.8.0
bringing major preview improvements to distinct
& joins
.
This release also ensures we use a consistent enum format on Python 3.11 upwards, see this issue for more details.
Published by RobertCraigie 12 months ago
This release bumps the internal Prisma version from 4.15.2
to 5.4.2
bringing major performance improvements.
This release also adds support for retrieving metrics, e.g.
from prisma import Prisma
client = Prisma()
metrics = client.get_metrics()
print(metrics.counters[0])
See the docs for more information.
Published by RobertCraigie about 1 year ago
This release adds support for Pydantic v2 while maintaining backwards compatibility with Pydantic v1.
This should be a completely transparent change for the majority of users with one exception regarding prisma.validate
.
Due to this bug we can't use the new TypeAdapter
concept in v2 and instead rely on the v1
re-exports, this has the implication that any errors raised by the validator will need to be caught using the v1 ValidationError
type instead, e.g.
from pydantic.v1 import ValidationError
import prisma
try:
prisma.validate(...)
except ValidationError:
...
All timeout arguments now accept a datetime.timedelta
instance as well as the previously accepted int
/ float
types, however support for int
/ float
has been deprecated and will be removed in a future release.
The previous values were used inconsistently, sometimes it meant seconds and sometimes it meant milliseconds. This new structure is more flexible and allows you to specify the time in whatever format you'd like. Thanks @jonathanblade!
from datetime import timedelta
from prisma import Prisma
db = Prisma(
connect_timeout=timedelta(seconds=20),
)
Thanks @zspine and @jonathanblade for contributing!
Published by RobertCraigie over 1 year ago
This release pins pydantic
to < 2
as v2.0.0
is not yet compatible with Prisma.
Thanks to @izeye and @Leon0824 for contributing!
Published by RobertCraigie over 1 year ago
This release adds support for interactive transactions! This means you can safely group sets of queries into a single atomic transaction that will be rolled back if any step fails.
Quick example:
from prisma import Prisma
prisma = Prisma()
await prisma.connect()
async with prisma.tx() as transaction:
user = await transaction.user.update(
where={'id': from_user_id},
data={'balance': {'decrement': 50}}
)
if user.balance < 0:
raise ValueError(f'{user.name} does not have enough balance')
await transaction.user.update(
where={'id': to_user_id},
data={'balance': {'increment': 50}}
)
For more information see the docs.
find_unique_or_raise
& find_first_or_raise
This release adds two new client methods, find_unique_or_raise
& find_first_or_raise
which are the exact same as the respective find_unique
& find_first
methods but will raise an error if a record could not be found.
This is useful when you know that you should always find a given record as you won't have to explicitly handle the case where it isn't found anymore:
user = await db.user.find_unique_or_raise(
where={
'id': '...',
},
include={
'posts': True,
},
)
This release bumps the internal Prisma version from v4.11.0
to v4.15.0
.
Some of the highlights:
prisma db seed
For the full release notes, see the v4.12.0, v4.13.0, v4.14.0 and v4.15.0 release notes.
typing-extensions
version from 3.7
to 4.0.1
--generator
option to prisma py generate
as wellHuge thank you to @isometric & @techied for their continued support!
Published by RobertCraigie over 1 year ago
This release adds support for selecting fields at the database level!
This currently only works for queries using model based access either by defining your own model classes or generating them using partial types.
Quick example:
from prisma.bases import BaseUser
class UserWithName(BaseUser):
name: str
# this query will only select the `name` field at the database level!
user = await UserWithName.prisma().find_first(
where={
'country': 'Scotland',
},
)
print(user.name)
For a more detailed guide see the docs.
You can now pass in a distinct
filter to find_first()
and find_many()
queries.
For example, the following query will find all Profile
records that have a distinct, or unique, city
field.
profiles = await db.profiles.find_many(
distinct=['city'],
)
# [
# { city: 'Paris' },
# { city: 'Lyon' },
# ]
You can also filter by distinct combinations, for example the following query will return all records that have a distinct city
and country
combination.
profiles = await db.profiles.find_many(
distinct=['city', 'country'],
)
# [
# { city: 'Paris', country: 'France' },
# { city: 'Paris', country: 'Denmark' },
# { city: 'Lyon', country: 'France' },
# ]
Thanks to @yukukotani's great work on the CLI you can now ergonomically share the same schema between multiple languages, for example with the following schema:
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator node {
provider = "prisma-client-js"
}
generator python {
provider = "prisma-client-py"
}
model User {
id Int @id
name String
}
You can now skip the generation of the Node client with the --generator
argument:
prisma generate --generator=python
See the generate
documentation for more details.
This release bumps the internal Prisma version from v4.8.0
to v4.10.1
For the full release notes, see the v4.9.0 release notes and the v4.10.0 release notes.
Before this release there were no explicit compatibility requirements for type checkers. From now on we will only support the latest versions of Mypy and Pyright.
In the next release the mypy plugin will be deprecated and later removed entirely. There is a bug in the plugin API in the latest versions of mypy that completely breaks the plugin and seems impossible to fix. See #683 for more information.
Massive thank you to @prisma, @techied, @exponential-hq and @danburonline for their continued support! Thank you to @paudrow for becoming a sponsor!
Published by RobertCraigie almost 2 years ago
This release contains some changes to the format of the Prisma Schema.
Most of these changes are due to a conceptual shift from allowing implicit behaviour to forcing verboseness to reduce the amount of under the hood magic that Prisma does, thankfully this means that a lot of the changes that you will be required to make should be pretty straightforward and easily fixable by running prisma format
which will show you all the places that need changed in your schema.
Changes:
sqlite://
URL prefix, you should now use file://
insteadFor more details see the Prisma v4.0.0 upgrade path.
This release includes an internal restructuring of how raw queries are deserialized. While all these changes should be completely backwards compatible, there may be edge cases that have changed. If you encounter one of these edge cases please open an issue and it will be fixed ASAP.
For some additional context, this restructuring means that most fields will internally be returned as strings until Prisma Client Python deserializes them (previously this was done at the query engine level).
This release completely refactors how the Prisma CLI is downloaded and ran. The previous implementation relied on downloading a single pkg binary, this worked but had several limitations which means we now:
The new solution is involves directly downloading a Node.js binary (if you don't already have it installed) and directly installing the Prisma ClI through npm
. Note that this does not pollute your userspace and does not make Node available to the rest of your system.
This will result in a small size increase (~150MB) in the case where Node is not already installed on your machine, if this matters to you you can install Prisma Client Python with the node
extra, e.g. pip install prisma[node]
, which will install a Node binary to your site-packages
that results in the same storage requirements as the previous pkg
solution. You can also directly install nodejs-bin yourself. It's also worth noting that this release includes significant (~50%) reduction in the size of the Prisma Engine binaries which makes the default Node binary size increase less impactful.
With this release you can now run Prisma Studio from the CLI which makes it incredibly easy to view & edit the data in your database. Simply run the following command
$ prisma studio
Or
$ prisma studio --schema=backend/schema.prisma
Note that there is also a dark mode available
This release adds official support for CockroachDB. You could've used CockroachDB previously by setting provider
to postgresql
but now you can explicitly specify CockroachDB in your Prisma Schema:
datasource db {
provider = "cockroachdb"
url = env("COCKROACHDB_URL")
}
It should be noted that there are a couple of edge cases:
TL;DR for improvements made by Prisma that will now be in Prisma Client Python
Full list of changes:
/tmp
by defaultGoing forward we will now use a GitHub Project to track state and relative priority of certain issues. If you'd like to increase the priority of issues that would benefit you please add 👍 reactions.
This is less of a roadmap per se but will hopefully give you some insight into the priority of given issues / features.
Thank you to @kfields for helping with raw query deserialization!
Massive thank you to @prisma & @techied for their continued support and @exponential-sponsorship for becoming a sponsor!
Published by RobertCraigie almost 2 years ago
This release adds official support for the Windows platform!
The main fix that comes with this release is a workaround for the missing error messages issue that has plagued so many.
A lot of the effort that went into this release was improving our internal testing strategies. This involved a major overhaul of our testing suite so that we can easily test multiple different database providers. This means we will be less likely to ship bugs and will be able to develop database specific features much faster!
In addition to the refactored test suite we also have new docker-based tests for ensuring compatibility with multiple platforms and environments that were previously untested. @jacobdr deserves a massive thank you for this!
Published by RobertCraigie about 2 years ago
Previously there was a mismatch between the resolution algorithm for relative SQLite paths which could cause the Client and the CLI to point to different databases.
The mismatch is caused by the CLI using the path to the Prisma Schema file as the base path whereas the Client used the current working directory as the base path.
The Client will now use the path to the Prisma Schema file as the base path for all relative SQLite paths, absolute paths are unchanged.
pyproject.toml
fileYou can now configure Prisma Client Python using an entry in your pyproject.toml
file instead of having to set environment variables, e.g.
[tool.prisma]
binary_cache_dir = '.binaries'
It should be noted that you can still use environment variables if you so desire, e.g.
PRISMA_BINARY_CACHE_DIR=".binaries"
This will also be useful as a workaround for #413 until the default behaviour is changed in the next release.
See the documentation for more information.
.env
files overriding environment variablesPreviously any environment variables present in the .env
or prisma/.env
file would take precedence over the environment variables set at the system level. This behaviour was not correct as it does not match what the Prisma CLI does. This has now been changed such that any environment variables in the .env
file will only be set if there is not an environment variable already present.
Python 3.11 is now officially supported and tested!
It should be noted that you may encounter some deprecation warnings from the transitive dependencies we use.
Bytes
typesYou can now generate JSON Schemas / OpenAPI Schemas for models that use the Bytes
type.
from prisma import Base64
from pydantic import BaseModel
class MyModel(BaseModel):
image: Base64
print(MyModel.schema_json(indent=2))
{
"title": "MyModel",
"type": "object",
"properties": {
"image": {
"title": "Image",
"type": "string",
"format": "byte"
}
},
"required": [
"image"
]
}
Base64
type in custom pydantic modelsYou can now use the Base64
type in your own Pydantic models and benefit from all the advanced type coercion that Pydantic provides! Previously you would have to manually construct the Base64
instances yourself, now Pydantic will do that for you!
from prisma import Base64
from pydantic import BaseModel
class MyModel(BaseModel):
image: Base64
# pass in a raw base64 encoded string and it will be transformed to a Base64 instance!
model = MyModel.parse_obj({'image': 'SGV5IHRoZXJlIGN1cmlvdXMgbWluZCA6KQ=='})
print(repr(model.image)) # Base64(b'SGV5IHRoZXJlIGN1cmlvdXMgbWluZCA6KQ==')
It should be noted that this assumes that the data you pass is a valid base64 string, it does not do any conversion or validation for you.
You can now unregister a client instance, this can be very useful for writing tests that interface with Prisma Client Python. However, you shouldn't ever have to use this outside of a testing context as you should only be creating a single Prisma
instance for each Python process unless you are supporting multi-tenancy. Thanks @leejayhsu for this!
from prisma.testing import unregister_client
unregister_client()
You can now access the location of the Prisma Schema file used to generate Prisma Client Python.
from prisma import SCHEMA_PATH
print(SCHEMA_PATH) # Path('/absolute/path/prisma/schema.prisma')
builtins
module, thanks @leejayhsu!*.pyc
and __pycache__
files during client generation\
exclude
and exclude_relational_fields
are givenMany thanks to @leejayhsu, @lewoudar, @tyteen4a03 and @nesb1 for contributing to this release!
A massive thank you to @prisma and @techied for their continued support! It is incredibly appreciated 💜
I'd also like to thank GitHub themselves for sponsoring me as part of Maintainer Month!
Published by RobertCraigie over 2 years ago
This release is a patch release to fix a regression, #402, introduced by the latest Pydantic release.
Published by RobertCraigie over 2 years ago
This change is only applied when generating recursive types as mypy does not support
LiteralString
yet.
PEP 675 introduces a new string type, LiteralString
, this type is a supertype of literal string types that allows functions to accept any arbitrary literal string type such as 'foo'
or 'bar'
for example.
All raw query methods, namely execute_raw
, query_raw
and query_first
now take the LiteralString
type as the query argument instead of str
. This change means that any static type checker thats supports PEP 675 will report an error if you try and pass a string that cannot be defined statically, for example:
await User.prisma().query_raw(f'SELECT * FROM User WHERE id = {user_id}')
This change has been made to help prevent SQL injection attacks.
Thank you to @leejayhsu for contributing this feature!
None
valuesYou can now filter records to remove or include occurrences where a field is None
or not. For example, the following query will return all User records with an email that is not None:
await client.user.find_many(
where={
'NOT': [{'email': None}]
},
)
It should be noted that nested None checks are not supported yet, for example this is not valid:
await client.user.find_many(
where={
'NOT': [{'email': {'equals': None}}]
},
)
It should also be noted that this does not change the return type and you will still have to perform not None checks to appease type checkers. e.g.
users = await client.user.find_many(
where={
'NOT': [{'email': None}]
},
)
for user in users:
assert user.email is not None
print(user.email.split('@'))
There are two new exception classes, ForeignKeyViolationError
and FieldNotFoundError
.
The ForeignKeyViolationError
is raised when a foreign key field has been provided but is not valid, for example, trying to create a post and connecting it to a non existent user:
await client.post.create(
data={
'title': 'My first post!',
'published': True,
'author_id': '<unknown user ID>',
}
)
The FieldNotFoundError
is raised when a field has been provided but is not valid in that context, for example, creating a record and setting a field that does not exist on that record:
await client.post.create(
data={
'title': 'foo',
'published': True,
'non_existent_field': 'foo',
}
)
The type definitions for creating records now contain the scalar relational fields as well as an alternative to the longer form for connecting relational fields, for example:
model User {
id String @id @default(cuid())
name String
email String @unique
posts Post[]
}
model Post {
id String @id @default(cuid())
author User? @relation(fields: [author_id], references: [id])
author_id String?
}
With the above schema and an already existent User
record. You can now create a new Post
record and connect it to the user by directly setting the author_id
field:
await Post.prisma().create(
data={
'author_id': '<existing user ID>',
'title': 'My first post!',
},
)
This is provided as an alternative to this query:
await Post.prisma().create(
data={
'title': 'My first post!',
'author': {
'connect': {
'id': '<existing user ID>'
}
}
},
)
Although the above query should be preferred as it also exposes other methods, such as creating the relational record inline or connecting based on other unique fields.
The internal Prisma binaries that Prisma Python makes use of have been upgraded from v3.11.1 to v3.13.0. For a full changelog see the v3.12.0 release notes and v3.13.0 release notes.
Many thanks to @q0w and @leejayhsu for their first contributions!
Published by RobertCraigie over 2 years ago
Decimal
typeExperimental support for the Decimal type has been added. The reason that support for this type is experimental is due to a missing internal feature in Prisma that means we cannot provide the same guarantees when working with the Decimal API as we can with the API for other types. For example, we cannot:
Decimal
value with a greater precision than the database supports, leading to implicit truncation which may cause confusing errorsdecimal.Decimal
objects to match the database level, potentially leading to even more confusing errors.If you need to use Decimal and are happy to work around these potential footguns then you must explicitly specify that you are aware of the limitations by setting a flag in the Prisma Schema:
generator py {
provider = "prisma-client-py"
enable_experimental_decimal = true
}
model User {
id String @id @default(cuid())
balance Decimal
}
The Decimal
type maps to the standard library's Decimal class. All available query operations can be found below:
from decimal import Decimal
from prisma import Prisma
prisma = Prisma()
user = await prisma.user.find_first(
where={
'balance': Decimal(1),
# or
'balance': {
'equals': Decimal('1.23823923283'),
'in': [Decimal('1.3'), Decimal('5.6')],
'not_in': [Decimal(10), Decimal(20)],
'gte': Decimal(5),
'gt': Decimal(11),
'lt': Decimal(4),
'lte': Decimal(3),
'not': Decimal('123456.28'),
},
},
)
Updates on the status of support for Decimal
will be posted in #106.
You can now add comments to your Prisma Schema and have them appear in the docstring for models and fields! For example:
/// The User model
model User {
/// The user's email address
email String
}
Will generate a model that looks like this:
class User(BaseModel):
"""The User model"""
email: str
"""The user's email address"""
If you try to import Prisma
or Client
before you've run prisma generate
then instead of getting an opaque error message:
>>> from prisma import Prisma
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'Prisma' from 'prisma' (/prisma/__init__.py)
you will now get an error like this:
>>> from prisma import Prisma
Traceback (most recent call last):
...
RuntimeError: The Client hasn't been generated yet, you must run `prisma generate` before you can use the client.
See https://prisma-client-py.readthedocs.io/en/stable/reference/troubleshooting/#client-has-not-been-generated-yet
You can now import the query batcher directly from the root package, making it much easier to type hint and providing support for an alternative API style:
from prisma import Prisma, Batch
prisma = Prisma()
async with Batch(prisma) as batcher:
...
def takes_batcher(batcher: Batch) -> None:
...
The internal Prisma binaries that Prisma Python makes use of have been upgraded from v3.10.0 to v3.11.1. For a full changelog see the v3.11.0 release notes and v3.11.1 release notes.
This project is now being sponsored by @prisma and @techied. I am so incredibly grateful to both for supporting Prisma Client Python 💜
Published by RobertCraigie over 2 years ago
This release is a patch release to fix incompatibilities between the documented MongoDB Prisma Schema and our version. In v3.10.0
Prisma made some breaking changes to MongoDB schemas, for example:
@default(dbgenerated())
with @default(auto())
@db.Array(ObjectId)
with @db.ObjectId
This caused some confusion as following an official Prisma guide in their documentation resulted in an error (#326).
Published by RobertCraigie over 2 years ago
In v0.6.0
we renamed Prisma
to Client
, in doing so we accidentally removed the export for the previous Client
name which was kept for backwards compatibility. This release re-exports it so the following code will no longer raise an error:
from prisma import Client
in
and not_in
for Bytes
fieldsFor example the following query will find the first record where binary_data
is either my binary data
or my other binary data
.
from prisma import Base64
from prisma.models import Data
await Data.prisma().find_first(
where={
'binary_data': {
'in': [
Base64.encode(b'my binary data'),
Base64.encode(b'my other binary data'),
],
},
},
)
And if you want to find a record that doesn't match any of the arguments you can use not_in
from prisma import Base64
from prisma.models import Data
await Data.prisma().find_first(
where={
'binary_data': {
'not_in': [
Base64.encode(b'my binary data'),
Base64.encode(b'my other binary data'),
],
},
},
)
__slots__
definitionsAll applicable classes now define the __slots__
attribute for improved performance and memory usage, for more information on what this means, see the Python documentation.
Thank you to @matyasrichter 💜
Published by RobertCraigie over 2 years ago
Although very rare, it is sometimes possible to get your Prisma Client Python installation into a corrupted state when upgrading to a newer version. In this situation you could try uninstalling and reinstalling Prisma Client Python however doing so will not always fix the client state, in this case you have to remove all of the files that are auto-generated by Prisma Client Python. To achieve this you would either have to manually remove them or download and run a script that we use internally.
With this release you can now automatically remove all auto-generated files by running the following command:
python -m prisma_cleanup
This will find your installed prisma
package and remove the auto-generated files.
If you're using a custom output location then all you need to do is pass the import path, the same way you do to use the client in your code, for example:
python -m prisma_cleanup app.prisma
The name change that occurred in the last release has been reverted, see #300 for reasoning.