You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/manager.md
+108-7Lines changed: 108 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,14 +1,58 @@
1
+
# Using the manager
2
+
`django-postgres-extra` provides the `psqlextra.manager.PostgresManager` which exposes a lot of functionality. Your model must use this manager in order to use most of this package's functionality.
3
+
4
+
There's three ways to do this:
5
+
6
+
***Inherit your model from `psqlextra.models.PostgresModel`:**
7
+
8
+
from psqlextra.models import PostgresModel
9
+
10
+
class MyModel(PostgresModel):
11
+
myfield = models.CharField(max_length=255)
12
+
13
+
14
+
***Override default manager with `psqlextra.manager.PostgresManager`:**
15
+
16
+
from django.db import models
17
+
from psqlextra.manager import PostgresManager
18
+
19
+
class MyModel(models.Model):
20
+
# override default django manager
21
+
objects = PostgresManager()
22
+
23
+
myfield = models.CharField(max_length=255)
24
+
25
+
26
+
***Provide `psqlextra.manager.PostgresManager` as a custom manager:**
27
+
28
+
from django.db import models
29
+
from psqlextra.manager import PostgresManager
30
+
31
+
class MyModel(models.Model):
32
+
# custom mananger name
33
+
beer = PostgresManager()
34
+
35
+
myfield = models.CharField(max_length=255)
36
+
37
+
# use like this:
38
+
MyModel.beer.upsert(..)
39
+
40
+
# not like this:
41
+
MyModel.objects.upsert(..) # error!
42
+
43
+
## Upserting
1
44
An "upsert" is an operation where a piece of data is inserted/created if it doesn't exist yet and updated (overwritten) when it already exists. Django has long provided this functionality through [`update_or_create`](https://docs.djangoproject.com/en/1.10/ref/models/querysets/#update-or-create). It does this by first checking whether the record exists and creating it not.
2
45
3
46
The major problem with this approach is possibility of race conditions. In between the `SELECT` and `INSERT`, another process could perform the `INSERT`. The last `INSERT` would most likely fail because it would be duplicating a `UNIQUE` constraint.
4
47
5
48
In order to combat this, PostgreSQL added native upserts. Also known as [`ON CONFLICT DO ...`](https://www.postgresql.org/docs/9.5/static/sql-insert.html#SQL-ON-CONFLICT). This allows a user to specify what to do when a conflict occurs.
6
49
50
+
### `upsert`
51
+
Attempts to insert a row with the specified data or updates (and overwrites) the duplicate row, and then returns the primary key of the row that was created/updated.
7
52
8
-
## upsert
9
-
The `upsert` method attempts to insert a row with the specified data or updates (and overwrites) the duplicate row, and then returns the primary key of the row that was created/updated.
53
+
Upserts work by catching conflcits. PostgreSQL requires to know whichconflicts to react to. You have to specify the name of the column to which you want to react to. This is specified in the `conflict_target` parameter.
10
54
11
-
Upserts work by catching conflicts. PostgreSQL requires to know which conflicts to react to. You have to specify the name of the column which's constraint you want to react to. This is specified in the `conflict_target` field. If the constraint you're trying to react to consists of multiple columns, specify multiple columns.
55
+
You can only specify a single "constraint" in this field. You **cannot** react to conflicts in multiple fields. This is a limitation by PostgreSQL. Note that this means **single constraint**, not necessarily a single column. A constraint can cover multiple columns.
12
56
13
57
from django.db import models
14
58
from psqlextra.models import PostgresModel
@@ -34,17 +78,74 @@ Upserts work by catching conflicts. PostgreSQL requires to know which conflicts
34
78
35
79
Note that a single call to `upsert` results in a single `INSERT INTO ... ON CONFLICT DO UPDATE ...`. This fixes the problem outlined earlier about another process doing the `INSERT` in the mean time.
36
80
37
-
## upsert_and_get
38
-
`upsert_and_get` does the same thing as `upsert`, but returns a model instance rather than the primary key of the row that was created/updated. This also happens in a single query using `RETURNING` clause on the `INSERT INTO` statement:
81
+
#### unique_together
82
+
As mentioned earlier, `conflict_target` expects a single column name, or multiple if the constraint you want to react to spans multiple columns. Django's [unique_together](https://docs.djangoproject.com/en/1.11/ref/models/options/#unique-together) has this. If you want to react to this constraint that covers multiple columns, specify those columns in the `conflict_target` parameter:
83
+
84
+
from django.db import models
85
+
from psqlextra.models import PostgresModel
86
+
87
+
class MyModel(PostgresModel):
88
+
class Meta:
89
+
unique_together = ('myfield1', 'myfield2')
90
+
91
+
myfield1 = models.CharField(max_length=255)
92
+
myfield1 = models.CharField(max_length=255)
93
+
94
+
MyModel.objects.upsert(
95
+
conflict_target=['myfield1', 'myfield2'],
96
+
fields=dict(
97
+
myfield1='beer'
98
+
myfield2='moar beer'
99
+
)
100
+
)
101
+
102
+
#### hstore
103
+
You can specify HStore keys that have a unique constraint as a `conflict_target`:
104
+
105
+
from django.db import models
106
+
from psqlextra.models import PostgresModel
107
+
from psqlextra.fields import HStoreField
108
+
109
+
class MyModel(PostgresModel):
110
+
# values in the key 'en' have to be unique
111
+
myfield = HStoreField(uniqueness=['en'])
112
+
113
+
MyModel.objects.upsert(
114
+
conflict_target=[('myfield', 'en')],
115
+
fields=dict(
116
+
myfield={'en': 'beer'}
117
+
)
118
+
)
119
+
120
+
It also supports specifying a "unique together" constraint on HStore keys:
Does the same thing as `upsert`, but returns a model instance rather than the primary key of the row that was created/updated. This also happens in a single query using `RETURNING` clause on the `INSERT INTO` statement:
0 commit comments