Skip to content

Commit f84e034

Browse files
committed
Update README
1 parent 02fbcb4 commit f84e034

File tree

1 file changed

+56
-46
lines changed

1 file changed

+56
-46
lines changed

README.md

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ INSTALLED_APPS = (
2222
)
2323
```
2424

25-
## Examples
25+
## Basic example
2626

2727
```python
2828
from django.db import models
@@ -35,17 +35,14 @@ class Customer(models.Model):
3535
post_code = models.CharField(max_length=20)
3636
is_preferred = models.BooleanField(default=False)
3737

38-
class Meta:
39-
app_label = 'myapp'
40-
41-
class PreferredCustomer(pg.View):
42-
projection = ['myapp.Customer.*',]
43-
dependencies = ['myapp.OtherView',]
44-
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
38+
39+
class PreferredCustomer(pg.View):
40+
name = models.CharField(max_length=100)
41+
post_code = models.CharField(max_length=20)
42+
43+
sql = """SELECT id, name, post_code FROM myapp_customer WHERE is_preferred IS TRUE"""
4544

4645
class Meta:
47-
app_label = 'myapp'
48-
db_table = 'myapp_preferredcustomer'
4946
managed = False
5047
```
5148

@@ -59,33 +56,16 @@ CREATE VIEW myapp_preferredcustomer AS
5956
SELECT * FROM myapp_customer WHERE is_preferred = TRUE;
6057
```
6158

62-
To create all your views, run ``python manage.py sync_pgviews``
63-
64-
You can also specify field names, which will map onto fields in your View:
65-
66-
```python
67-
from django_pgviews import view as pg
68-
69-
70-
VIEW_SQL = """
71-
SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE
72-
"""
73-
59+
To create this view, run `python manage.py migrate`, or `python manage.py sync_pgviews`.
7460

75-
class PreferredCustomer(pg.View):
76-
name = models.CharField(max_length=100)
77-
post_code = models.CharField(max_length=20)
78-
79-
sql = VIEW_SQL
80-
```
61+
Then you can query `PreferredCustomer` like any other model.
8162

8263
## Usage
8364

84-
To map onto a View, simply extend `pg_views.view.View`, assign SQL to the
85-
`sql` argument, and define a `db_table`. You must _always_ set `managed = False`
86-
on the `Meta` class.
65+
To create a view, create a new class that subclasses `django_pgviews.view.View` instead of `models.Model`,
66+
set `managed = False` on the `Meta` class, and define the `sql` class attribute with the definition of the view.
8767

88-
Views can be created in a number of ways:
68+
Views can be created in two basic ways:
8969

9070
1. Define fields to map onto the VIEW output
9171
2. Define a projection that describes the VIEW fields
@@ -98,20 +78,14 @@ Define the fields as you would with any Django Model:
9878
from django_pgviews import view as pg
9979

10080

101-
VIEW_SQL = """
102-
SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE
103-
"""
104-
105-
10681
class PreferredCustomer(pg.View):
10782
name = models.CharField(max_length=100)
10883
post_code = models.CharField(max_length=20)
10984

110-
sql = VIEW_SQL
85+
sql = "SELECT id, name, post_code FROM myapp_customer WHERE is_preferred = TRUE"
11186

11287
class Meta:
11388
managed = False
114-
db_table = 'my_sql_view'
11589
```
11690

11791
### Define Projection
@@ -124,30 +98,68 @@ from django_pgviews import view as pg
12498

12599

126100
class PreferredCustomer(pg.View):
127-
projection = ['myapp.Customer.*',]
101+
projection = ['myapp.Customer.*']
128102
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
129103

130104
class Meta:
131-
db_table = 'my_sql_view'
132105
managed = False
133106
```
134107

135108
This will take all fields on `myapp.Customer` and apply them to `PreferredCustomer`
136109

110+
## Migrations
111+
112+
When you run `makemigrations`, `django-pgviews` will detect changes in views and create migrations to register new views and to drop renamed or removed views.
113+
114+
By default, when you run `migrate`, `django-pgviews` will create or update your views – you may turn this off with `MATERIALIZED_VIEWS_DISABLE_SYNC_ON_MIGRATE`, see below.
115+
116+
### Autodetector
117+
118+
If you use another library which updates the Django migration AutoDetector, if you want to keep full functionality, you need to subclass
119+
the AutoDetector class to subclass from `django_pgviews.db.migrations.autodetector.PGViewsAutodetector` as well.
120+
121+
### Changing upstream fields
122+
123+
If you need to change a column which is used in a view definition, you may get an error like this:
124+
125+
```
126+
django.db.utils.NotSupportedError: cannot alter type of a column used by a view or rule
127+
DETAIL: rule _RETURN on materialized view some_view depends on column "some_column"
128+
```
129+
130+
To handle this, you can use the migrations to drop the view before the migration gets applied, and then recreate it afterwards.
131+
132+
1. Add an empty migration to the app with your view (`python manage.py makemigrations --empty app_name`)
133+
2. Add database operation to the migration
134+
```python
135+
migrations.SeparateDatabaseAndState(
136+
database_operations=[
137+
django_pgviews.db.migrations.operations.DeleteViewOperation(
138+
name="SomeView", # CHANGEME
139+
materialized=True, # CHANGEME
140+
db_name="some_view", # CHANGEME
141+
),
142+
]
143+
),
144+
```
145+
3. Make the migration changing the column depend on the migration dropping the view
146+
147+
When you then run migrations, the view will be dropped, the column will be changed, and the view will be recreated by the command after all migrations are applied.
148+
137149
## Features
138150

139151
### Configuration
140152

141153
`MATERIALIZED_VIEWS_DISABLE_SYNC_ON_MIGRATE`
142154

143-
When set to True, it skips running `sync_pgview` during migrations, which can be useful if you want to control the synchronization manually or avoid potential overhead during migrations. (default: False)
155+
When set to True, it skips running `sync_pgviews` during migrations, which can be useful if you want to control the synchronization manually or avoid potential overhead during migrations. (default: False)
144156
```
145157
MATERIALIZED_VIEWS_DISABLE_SYNC_ON_MIGRATE = True
146158
```
147159

148160
### Updating Views
149161

150-
Sometimes your models change, and you need your Database Views to reflect the new data.
162+
Sometimes your models change, and you need your views to reflect the new data.
151163
Updating the View logic is as simple as modifying the underlying SQL and running:
152164

153165
```
@@ -175,8 +187,6 @@ class PreferredCustomer(pg.View):
175187
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
176188

177189
class Meta:
178-
app_label = 'myapp'
179-
db_table = 'myapp_preferredcustomer'
180190
managed = False
181191
```
182192

@@ -254,7 +264,7 @@ def customer_saved(sender, action=None, instance=None, **kwargs):
254264

255265
As the materialized view isn't defined through the usual Django model fields, any indexes defined there won't be
256266
created on the materialized view. Luckily Django provides a Meta option called `indexes` which can be used to add custom
257-
indexes to models. `pg_views` supports defining indexes on materialized views using this option.
267+
indexes to models. `django-pgviews` supports defining indexes on materialized views using this option.
258268

259269
In the following example, one index will be created, on the `name` column. The `db_index=True` on the field definition
260270
for `post_code` will get ignored.

0 commit comments

Comments
 (0)