-
Notifications
You must be signed in to change notification settings - Fork 369
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
New schema docs #3290
base: develop
Are you sure you want to change the base?
New schema docs #3290
Changes from all commits
523a418
18e6ce3
86c68e4
e7488f9
1f81dab
9d8e69b
209ae69
7a42d73
debafa1
fad3b90
c46bf3a
79e20f2
bd67794
eabd194
d94b243
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,113 @@ | ||
--- | ||
|
||
title: Custom row classes | ||
description: Use your own classes as data classes for drift tables | ||
title: Dataclass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still not too happy with the generic |
||
description: Dataclass for reading and writing data to the database. | ||
|
||
--- | ||
|
||
For each table declared in Dart or in a drift file, `drift_dev` generates a row class (sometimes also referred to as _data class_) | ||
to hold a full row and a companion class for updates and inserts. | ||
This works well for most cases: Drift knows what columns your table has, and it can generate a simple class for all of that. | ||
In some cases, you might want to customize the generated classes though. | ||
For instance, you might want to add a mixin, let it extend another class or interface, or use other builders like | ||
`json_serializable` to customize how it gets serialized to json. | ||
|
||
As a solution, drift allows you to use your own classes as data classes for the database. | ||
|
||
## Using custom classes | ||
# Generated Dataclass | ||
|
||
To use a custom row class, simply annotate your table definition with `@UseRowClass`. | ||
Drift generates a dataclass for each table in your database. These dataclasses represent query results and come with built-in equality, hashing, and serialization support. They also include a `copyWith` method for easy modification. | ||
|
||
**Example:** | ||
|
||
For a `Users` table, Drift automatically generates a `User` dataclass. This dataclass is used for all read operations from the `Users` table, ensuring type-safe and structured data retrieval. | ||
|
||
{{ load_snippet('generated-dataclass','lib/snippets/dart_api/dataclass.dart.excerpt.json') }} | ||
|
||
## Dataclass Name | ||
|
||
The dataclass name is derived from the table name. | ||
|
||
- If the name ends in `s`, the dataclass name will be the name with `s` removed. | ||
- Example: `Users` -> `User` | ||
- Otherwise, the dataclass name will be the name with `Data` appended. | ||
- Example: `UserInfo` -> `UserInfoData` | ||
|
||
|
||
To use a custom name use the `@DataClassName` annotation. | ||
|
||
**Example:** | ||
|
||
{{ load_snippet('data-class-name','lib/snippets/dart_api/dataclass.dart.excerpt.json') }} | ||
|
||
## Json serialization | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should not be this far up - and there should be a little warning saying that using custom row classes is the preferred approach for JSON serialization. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drift is a relational persistence library, and ideally shouldn't have to do anything to do with serialization. It's a historical artifact that I regret, but it's too late to fix this now. Our serialization capabilities are incredibly restricted compared to packages that actually intend to do serialziation, which is why I recommend using custom row classes to combine drift with other libraries where necessary. |
||
|
||
### Key names | ||
|
||
When serializing to json, the generated dataclass will use the column name in `snake_case` for the json keys. | ||
|
||
**Example:** | ||
|
||
{{ load_snippet('default-json-keys','lib/snippets/dart_api/dataclass.dart.excerpt.json') }} | ||
|
||
```json | ||
{ | ||
"id": 1, | ||
"title": "Todo 1", | ||
"created_at": "2024-02-29T12:00:00Z" | ||
} | ||
``` | ||
|
||
### Custom json keys | ||
|
||
To use a custom name for JSON serialization, use the `@JsonKey` annotation. | ||
dickermoshe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Note that the `@JsonKey` class from `package:drift` is not same as the `@JsonKey` annotation from `package:json_annotation`, and the two are not compatible with each other. | ||
|
||
**Example:** | ||
|
||
{{ load_snippet('custom-json-keys','lib/snippets/dart_api/dataclass.dart.excerpt.json') }} | ||
|
||
```json | ||
{ | ||
"id": 1, | ||
"title": "Todo 1", | ||
"created": "2024-02-29T12:00:00Z" | ||
} | ||
``` | ||
|
||
If you prefer to use the actual column name in SQL as the JSON key, set `use_sql_column_name_as_json_key` to `true` in the `build.yaml` file. | ||
|
||
```yaml title="build.yaml" | ||
targets: | ||
$default: | ||
builders: | ||
drift_dev: | ||
options: | ||
use_sql_column_name_as_json_key : true | ||
``` | ||
For more details on customizing column names in SQL, refer to the [column name](tables.md#column-names) documentation. | ||
|
||
## Companions | ||
|
||
In addition to the generated dataclass representing a complete row, Drift also generates a companion object for each table, which represents a partial row and can be used to update existing rows. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a pretty short explanation for an important subject. I think we should take something like this as a better reference on how to introduce companions. We could also mention that companions are not as relevant with the manager API as their structure is reflected by the callbacks for creating and updating rows. (similar to the annotation below) |
||
|
||
<div class="annotate" markdown> | ||
|
||
{{ load_snippet('generated-companion','lib/snippets/dart_api/dataclass.dart.excerpt.json') }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use a tabbed view here with Manager vs. the Core query builder instead of having it in a single snippet. |
||
|
||
</div> | ||
1. `o()` is just a helper function that creates a `UsersCompanion`. | ||
|
||
### Value object | ||
When using the companion object to update a row, optional fields must be wrapped in a `Value` object. This is used by Drift to distinguish between `null` and not present values. | ||
|
||
{{ load_snippet('generated-value','lib/snippets/dart_api/dataclass.dart.excerpt.json') }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This example really isn't too helpful. Values were also used in the example above for updates, perhaps we could reference that here.
A better explanation would be to mention what drift actually does with that: For updates, there's a distinction between setting the value of a column to |
||
|
||
## Custom dataclass | ||
|
||
The generated dataclass works well for most cases, but you might want to use your own class as a dataclass for a table. | ||
|
||
For instance, you might want to add a mixin, let it extend another class or interface, or use other builders like `json_serializable` to customize how it gets serialized to json. | ||
|
||
!!! note "Row Class" | ||
|
||
In the documentation, we use the terms _row class_ and _dataclass_ interchangeably. | ||
Both refer to a class that represents a row of a database table. | ||
|
||
To use a custom row class, simply annotate your table definition with `@UseRowClass`. | ||
|
||
{{ load_snippet('start','lib/snippets/custom_row_classes/default.dart.excerpt.json','lib/snippets/custom_row_classes/named.dart.excerpt.json') }} | ||
|
||
|
@@ -46,6 +136,32 @@ If you want to use another constructor, set the `constructor` parameter on the | |
|
||
{{ load_snippet('named','lib/snippets/custom_row_classes/default.dart.excerpt.json','lib/snippets/custom_row_classes/named.dart.excerpt.json') }} | ||
|
||
### Custom companions | ||
|
||
In most cases, generated companion classes are the right tool for updates and inserts. | ||
If you prefer to use your custom row class for inserts, just make it implement `Insertable<T>`, where | ||
`T` is the tye of your row class itself. | ||
For instance, the previous class could be changed like this: | ||
|
||
```dart | ||
class User implements Insertable<User> { | ||
final int id; | ||
final String name; | ||
final DateTime birthDate; | ||
|
||
User({required this.id, required this.name, required this.birthDate}); | ||
|
||
@override | ||
Map<String, Expression> toColumns(bool nullToAbsent) { | ||
return UsersCompanion( | ||
id: Value(id), | ||
name: Value(name), | ||
birthDate: Value(birthDate), | ||
).toColumns(nullToAbsent); | ||
} | ||
} | ||
``` | ||
|
||
### Static and asynchronous factories | ||
|
||
Starting with drift 2.0, the custom constructor set with the `constructor` | ||
|
@@ -65,7 +181,7 @@ class User { | |
} | ||
``` | ||
|
||
### Existing row classes in drift files | ||
### Custom dataclass in drift files | ||
|
||
To use existing row classes in drift files, use the `WITH` keyword at the end of the | ||
table declaration. Also, don't forget to import the Dart file declaring the row | ||
|
@@ -98,33 +214,7 @@ CREATE TABLE users( | |
) WITH User.myNamedConstructor; | ||
``` | ||
|
||
## Inserts and updates with custom classes | ||
|
||
In most cases, generated companion classes are the right tool for updates and inserts. | ||
If you prefer to use your custom row class for inserts, just make it implement `Insertable<T>`, where | ||
`T` is the name of your row class itself. | ||
For instance, the previous class could be changed like this: | ||
|
||
```dart | ||
class User implements Insertable<User> { | ||
final int id; | ||
final String name; | ||
final DateTime birthDate; | ||
|
||
User({required this.id, required this.name, required this.birthDate}); | ||
|
||
@override | ||
Map<String, Expression> toColumns(bool nullToAbsent) { | ||
return UsersCompanion( | ||
id: Value(id), | ||
name: Value(name), | ||
birthDate: Value(birthDate), | ||
).toColumns(nullToAbsent); | ||
} | ||
} | ||
``` | ||
|
||
## Existing row classes for queries | ||
#### Custom dataclass for queries | ||
|
||
Existing row classes may also be applied to named queries defined in a `.drift` file. | ||
They have a similar syntax, adding the `WITH` keyword after the name of the query: | ||
|
@@ -165,10 +255,10 @@ For your convenience, drift is using different generation strategies even for qu | |
an existing row class. It is helpful to enumerate them because they affect the allowed type for | ||
fields in existing types as well. | ||
|
||
1. Nested tables: When the [`SELECT table.**` syntax](sql_api/drift_files.md#nested-results) | ||
1. Nested tables: When the [`SELECT table.**` syntax](../sql_api/drift_files.md#nested-results) | ||
is used in a query, drift will pack columns from `table` into a nested object instead of generating fields | ||
for every column. | ||
2. Nested list results: The [`LIST()` macro](sql_api/drift_files.md#list-subqueries) | ||
2. Nested list results: The [`LIST()` macro](../sql_api/drift_files.md#list-subqueries) | ||
can be used to expose results of a subquery as a list. | ||
3. Single-table results: When a select statement reads all columns from a table (and no additional columns), | ||
like in `SELECT * FROM table`, drift will use the data class of the table instead of generating a new one. | ||
|
@@ -257,25 +347,3 @@ If you have questions about existing result classes, or think you have found an | |
properly handled, please [start a discussion](https://github.com/simolus3/drift/discussions/new) in | ||
the drift repository, thanks! | ||
|
||
## When custom classes make sense | ||
|
||
The default drift-generated classes are a good default for most applications. | ||
In some advanced use-cases, custom classes can be a better alternative though: | ||
|
||
- Reduce generated code size: Due to historical reasons and backwards-compatibility, drift's classes | ||
contain a number of methods for json serialization and `copyWith` that might not be necessary | ||
for all users. | ||
Custom row classes can reduce bloat here. | ||
- Custom superclasses: A custom row class can extend and class and implement or mix-in other classes | ||
as desired. | ||
- Other code generators: Since you control the row class, you can make better use of other builders like | ||
`json_serializable` or `built_value`. | ||
|
||
## Limitations | ||
|
||
These restrictions will be gradually lifted in upcoming drift versions. Follow [#1134](https://github.com/simolus3/drift/issues/1134) for details. | ||
|
||
For now, this feature is subject to the following limitations: | ||
|
||
- In drift files, you can only use the default unnamed constructor | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -76,6 +76,20 @@ bitwise operations: | |||||
|
||||||
{{ load_snippet('bitwise','lib/snippets/dart_api/expressions.dart.excerpt.json') }} | ||||||
|
||||||
### BigInt | ||||||
|
||||||
You may want to cast an expression to a `BigInt` if: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
- The result of an arithmetic operation will be extremely large[^1]. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the point of being imprecise here? We can just write
Suggested change
|
||||||
- You are compiling to JavaScript. | ||||||
|
||||||
[^1]: Like bigger than 4,503,599,627,370,496! | ||||||
|
||||||
Using `dartCast<BigInt>()` will ensure that the result is interpreted as a `BigInt` by drift. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also note that this doesn't change the behavior in SQL at all. |
||||||
|
||||||
**Example:** | ||||||
For an expression `(table.columnA * table.columnB).dartCast<BigInt>()`, drift will report the resulting value as a `BigInt` even if `columnA` and `columnB` were defined as regular integers. | ||||||
|
||||||
## Null checks | ||||||
To check whether an expression evaluates to `NULL` in SQL, you can use the `isNull` extension: | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,11 @@ | ||
--- | ||
|
||
title: Manager | ||
title: Queries | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO we should leave this as-is before the restructuring is more complete. At the moment we'd then effectively have multiple pages on queries which is more confusing than before. We can name it "Queries with Manager" or something and also point out in the introduction/getting started guide that there are two ways to write these queries (like we already do on the new table page). |
||
description: Use easier bindings for common queries. | ||
|
||
--- | ||
|
||
|
||
|
||
With generated code, drift allows writing SQL queries in type-safe Dart. | ||
While this is provides lots of flexibility, it requires familiarity with SQL. | ||
As a simpler alternative, drift 2.18 introduced a new set of APIs designed to | ||
make common queries much easier to write. | ||
|
||
The examples on this page use the database from the [setup](../setup.md) | ||
instructions. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these be without the domain like the others above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Remove the leading url