在Django中创建和管理Postgres SQL视图
项目描述
Postgres SQL视图
在Django ORM中为PostgreSQL视图添加了一级支持。由mypebble创建的原始django-pgviews的分支,支持Django 3.2+。
安装
通过pip安装
pip install django-pgviews-redux
将应用程序添加到settings.py中的已安装应用程序
INSTALLED_APPS = (
# ...
'django_pgviews',
)
示例
from django.db import models
from django_pgviews import view as pg
class Customer(models.Model):
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
is_preferred = models.BooleanField(default=False)
class Meta:
app_label = 'myapp'
class PreferredCustomer(pg.View):
projection = ['myapp.Customer.*',]
dependencies = ['myapp.OtherView',]
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
class Meta:
app_label = 'myapp'
db_table = 'myapp_preferredcustomer'
managed = False
注意 在Meta中包含managed = False
非常重要,这样Django 1.7迁移就不会尝试为该视图创建数据库表。
此SQL生成的可能如下所示
CREATE VIEW myapp_preferredcustomer AS
SELECT * FROM myapp_customer WHERE is_preferred = TRUE;
要创建所有视图,运行python manage.py sync_pgviews
您还可以指定字段名称,它们将映射到视图中的字段
from django_pgviews import view as pg
VIEW_SQL = """
SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE
"""
class PreferredCustomer(pg.View):
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
sql = VIEW_SQL
用法
要将映射到视图,只需扩展 pg_views.view.View
,将 SQL 赋值给 sql
参数并定义一个 db_table
。您必须在 Meta
类中始终设置 managed = False
。
视图可以通过多种方式创建
- 定义映射到视图输出的字段
- 定义一个描述视图字段的投影
定义字段
就像使用任何 Django 模型一样定义字段
from django_pgviews import view as pg
VIEW_SQL = """
SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE
"""
class PreferredCustomer(pg.View):
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
sql = VIEW_SQL
class Meta:
managed = False
db_table = 'my_sql_view'
定义投影
django-pgviews
可以接受一个投影来确定它需要映射到视图的字段。要使用此功能,设置 projection
属性
from django_pgviews import view as pg
class PreferredCustomer(pg.View):
projection = ['myapp.Customer.*',]
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
class Meta:
db_table = 'my_sql_view'
managed = False
这会将 myapp.Customer
上的所有字段应用到 PreferredCustomer
上
特性
更新视图
有时您的模型发生变化,您需要数据库视图反映新的数据。更新视图逻辑就像修改底层 SQL 并运行
python manage.py sync_pgviews --force
这将强制更新与您的新 SQL 冲突的任何视图
依赖项
您可以指定您依赖的其他视图。这确保了其他视图在之前安装。使用依赖项还确保在使用 sync_pgviews --force
时正确刷新您的视图。
注意:视图在 Django 应用迁移后同步,将模型添加到依赖列表会导致同步失败。
示例
from django_pgviews import view as pg
class PreferredCustomer(pg.View):
dependencies = ['myapp.OtherView',]
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
class Meta:
app_label = 'myapp'
db_table = 'myapp_preferredcustomer'
managed = False
物化视图
Postgres 9.3 及更高版本支持 物化视图,允许您缓存视图的结果,从而可能使它们加载更快。
但是,您需要手动刷新视图。为此,您可以附加 信号 并调用刷新函数。
示例
from django_pgviews import view as pg
VIEW_SQL = """
SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE
"""
class Customer(models.Model):
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
is_preferred = models.BooleanField(default=True)
class PreferredCustomer(pg.MaterializedView):
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
sql = VIEW_SQL
@receiver(post_save, sender=Customer)
def customer_saved(sender, action=None, instance=None, **kwargs):
PreferredCustomer.refresh()
并发刷新
Postgres 9.4 及更高版本允许在存在唯一索引的情况下并发刷新物化视图,而不会阻塞读取。要启用并发刷新,指定可以用于物化视图唯一索引的列名称。唯一索引可以定义在物化视图的多个列上。一旦启用,将 concurrently=True
传递给模型的刷新方法将导致 postgres 并发执行刷新。(请注意,刷新方法本身会阻塞,直到刷新完成;并发刷新在物化视图在另一个进程或线程中更新时最有用。)
示例
from django_pgviews import view as pg
VIEW_SQL = """
SELECT id, name, post_code FROM myapp_customer WHERE is_preferred = TRUE
"""
class PreferredCustomer(pg.MaterializedView):
concurrent_index = 'id, post_code'
sql = VIEW_SQL
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
@receiver(post_save, sender=Customer)
def customer_saved(sender, action=None, instance=None, **kwargs):
PreferredCustomer.refresh(concurrently=True)
索引
由于物化视图不是通过通常的 Django 模型字段定义的,因此在那里定义的任何索引都不会创建在物化视图中。幸运的是,Django 提供了一个名为 indexes
的 Meta 选项,可以用于向模型添加自定义索引。pg_views
支持使用此选项在物化视图中定义索引。
在以下示例中,将创建一个索引,位于 name
列上。post_code
字段定义上的 db_index=True
将被忽略。
from django_pgviews import view as pg
VIEW_SQL = """
SELECT id, name, post_code FROM myapp_customer WHERE is_preferred = TRUE
"""
class PreferredCustomer(pg.MaterializedView):
sql = VIEW_SQL
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20, db_index=True)
class Meta:
managed = False # don't forget this, otherwise Django will think it's a regular model
indexes = [
models.Index(fields=["name"]),
]
WITH NO DATA
物化视图可以创建为有数据或无数据。默认情况下,它们是有数据的,但是 pg_views
支持通过为 pg.MaterializedView
类定义 with_data = False
来创建无数据的物化视图。此类视图在第一次刷新之前不支持查询(引发 django.db.utils.OperationalError
)。
示例
from django_pgviews import view as pg
class PreferredCustomer(pg.MaterializedView):
concurrent_index = 'id, post_code'
sql = """
SELECT id, name, post_code FROM myapp_customer WHERE is_preferred = TRUE
"""
with_data = False
name = models.CharField(max_length=100)
post_code = models.CharField(max_length=20)
条件性物化视图重建
由于所有物化视图在运行 migrate
时都会被重新创建,即使视图的定义没有发生变化,这也可能导致过时的重新创建。为了避免这种情况,版本 0.7.0 及更高版本包含一个功能,该功能会检查数据库中现有的物化视图定义(如果存在的话),并将定义与您当前在 pg.MaterializedView
子类中定义的进行比较。如果定义完全匹配,则会跳过物化视图的重新创建。
通过将 Django 设置中的 MATERIALIZED_VIEWS_CHECK_SQL_CHANGED
设置为 True
,可以启用此功能,使其在运行 migrate
时生效。命令 sync_pgviews
也使用此设置,但同时还具有开关 --enable-materialized-views-check-sql-changed
和 --disable-materialized-views-check-sql-changed
,这些开关将覆盖此命令的设置。
此功能还考虑了索引。当认为视图不需要重新创建时,过程仍会检查表上的索引,并删除任何额外的索引并创建任何缺失的索引。这种协调是通过索引名称完成的,所以如果您为索引使用了自定义名称,那么在内容变化而名称不变的情况下,它可能不会得到更新。
模式
默认情况下,视图将在数据库的模式中创建,这通常是 public
。该包支持通过使用选项("OPTIONS": {"options": "-c search_path=custom_schema"}
)在设置中定义数据库的模式。
如果使用,该包还支持 django-tenants
。
如果与数据库的默认模式不同,可以为视图显式定义模式,如下所示
from django_pgviews import view as pg
class PreferredCustomer(pg.View):
sql = """SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""
class Meta:
db_table = 'my_custom_schema.preferredcustomer'
managed = False
动态视图 SQL
如果您需要动态视图 SQL(例如,如果它需要从中获取设置的值),您可以通过覆盖视图上的 run_sql
类方法来返回 SQL。该方法应返回一个 namedtuple ViewSQL
,其中包含查询和可能传递给 cursor.execute
调用的参数。参数可以是 None 或查询参数的列表。
from django.conf import settings
from django_pgviews import view as pg
class PreferredCustomer(pg.View):
@classmethod
def get_sql(cls):
return pg.ViewSQL(
"""SELECT * FROM myapp_customer WHERE is_preferred = TRUE and created_at >= %s;""",
[settings.MIN_PREFERRED_CUSTOMER_CREATED_AT]
)
class Meta:
db_table = 'preferredcustomer'
managed = False
同步监听器
django-pgviews 0.5.0 添加了监听 post_sync
事件的能力。
view_synced
每次视图与数据库同步时都会触发。
提供参数
sender
- 视图类update
- 是否需要更新的视图force
- 是否传递了force
status
- 创建视图的结果,例如EXISTS
、FORCE_REQUIRED
has_changed
- 视图是否需要更改
all_views_synced
在所有 Postgres 视图同步后发送。
提供参数
sender
- 总是None
多个数据库
django-pgviews 可以使用多个数据库。类似于 Django 的 migrate
管理命令,我们的命令(clear_pgviews
、refresh_pgviews
、sync_pgviews
)一次只操作一个数据库。您可以通过提供 --database
选项来指定要同步的数据库。例如
python manage.py sync_pgviews # uses default db
python manage.py sync_pgviews --database=myotherdb
除非使用自定义路由器,否则 django-pgviews 将将所有视图同步到指定的数据库。如果您想自动与多个数据库交互,则需要采取一些额外的步骤。请参阅 Django 的 自动数据库路由 以将视图固定到特定数据库。
Django 兼容性
Django 版本 | Django-PGView 版本 |
---|---|
1.4 及以下 | 不受支持 |
1.5 | 0.0.1 |
1.6 | 0.0.3 |
1.7 | 0.0.4 |
1.9 | 0.1.0 |
1.10 | 0.2.0 |
2.2 | 0.6.0 |
3.0 | 0.6.0 |
3.1 | 0.6.1 |
3.2 | 0.7.1 |
4.0 | 0.8.1 |
4.1 | 0.8.4 |
4.2 | 0.9.2 |
5.0 | 0.9.4 |
Python 3 支持
Django PGViews Redux 仅官方支持 Python 3.7+,它可能在 3.6 上工作,但没有保证。
项目详细信息
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。
源代码分发
构建分发
哈希值 for django_pgviews_redux-0.10.1-py3-none-any.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 89a635cd062978d85c13d7eb32534cacad3baa0d4aa12f66b7e16f0ebe576004 |
|
MD5 | 0d1f31053fdd978c1d1ad1085df026c6 |
|
BLAKE2b-256 | a4a4ec9c24bfb6933b73e59d3e2b066609c24f1fa0576f7a4be5b612ffd13a3e |