What's Changed
Raw queries are now typed with LiteralString
> This change is only applied when [generating recursive types](https://prisma-client-py.readthedocs.io/en/stable/reference/config/#recursive) as mypy does not support `LiteralString` yet.
[PEP 675](https://peps.python.org/pep-0675/) 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:
py
await User.prisma().query_raw(f'SELECT * FROM User WHERE id = {user_id}')
This change has been made to help prevent [SQL injection attacks](https://en.wikipedia.org/wiki/SQL_injection).
Thank you to leejayhsu for contributing this feature!
Basic support for filtering by `None` values
You 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:
py
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:
py
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.
py
users = await client.user.find_many(
where={
'NOT': [{'email': None}]
},
)
for user in users:
assert user.email is not None
print(user.email.split(''))
New exception classes
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:
py
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:
py
await client.post.create(
data={
'title': 'foo',
'published': True,
'non_existent_field': 'foo',
}
)
Added scalar relational fields in create input
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:
prisma
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:
py
await Post.prisma().create(
data={
'author_id': '<existing user ID>',
'title': 'My first post!',
},
)
This is provided as an alternative to this query:
py
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.
Prisma Upgrade
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](https://github.com/prisma/prisma/releases/tag/3.12.0) and [v3.13.0 release notes](https://github.com/prisma/prisma/releases/tag/3.13.0).
Minor Changes
- Fixed typos / outdated commands in the documentation
- Fixed long standing style issue with dark mode in the documentation
New Contributors
Many thanks to q0w and leejayhsu for their first contributions!
Sponsors
