Skip to main content

在 Django 中创建和管理 Postgres SQL 视图

项目描述

Postgres 的 SQL 视图

圈子CI 代码风格:黑色

在 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

注意重要的是,我们将 包含managed = FalseMetaDjango 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. 您必须始终设置类managed = FalseMeta

可以通过多种方式创建视图:

  1. 定义要映射到 VIEW 输出的字段
  2. 定义描述 VIEW 字段的投影

定义字段

像使用任何 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

     
      托管 =  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

特征

更新视图

有时您的模型会发生变化,您需要数据库视图来反映新数据。更新 View 逻辑就像修改底层 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 提供了一个名为 Meta 的选项indexes,可用于向模型添加自定义索引。pg_views支持使用此选项在物化视图上定义索引。

在以下示例中,将在name列上创建一个索引。的db_index=True字段定义post_code将被忽略。

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"]),
        ]

没有数据

可以使用或不使用数据来创建物化视图。默认情况下,它们是用数据创建的,但是 pg_views支持通过定义类来创建没有数据的物化with_data = False视图 pg.MaterializedViewdjango.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 及更高版本包含一项功能,该功能检查数据库中现有的物化视图定义(如果 mat.view 存在的话)并将定义与当前在pg.MaterializedView子类中定义的定义进行比较。如果定义完全匹配,则跳过物化视图的重新创建。

通过将MATERIALIZED_VIEWS_CHECK_SQL_CHANGEDDjango 设置中的 设置为 来True启用此功能,这会在运行时启用该功能migrate。该命令sync_pgviews也使用此设置,但它也有开关--enable-materialized-views-check-sql-changed,并且 --disable-materialized-views-check-sql-changed会覆盖该命令的此设置。

此功能还考虑了索引。当视图被认为不需要重新创建时,该过程仍将检查表上的索引并删除任何额外的索引并创建任何缺失的索引。这种协调是通过索引名称完成的,因此如果您为索引使用自定义名称,它可能不会随着内容的变化而不是名称的变化而更新。

自定义架构

您可以为视图定义任何您希望的表名。它们甚至可以存在于您自己的自定义 PostgreSQL 模式中。

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 视图上的 classmethod 以返回 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

每次 VIEW 与数据库同步时触发。

提供参数:

  • sender- 查看课程
  • update- 视图是否要更新
  • force- 是否force通过
  • status- 创建视图的结果,例如EXISTSFORCE_REQUIRED
  • has_changed- 视图是否必须改变

all_views_synced

在所有 Postgres VIEW 同步后发送。

提供参数:

  • sender- 总是None

多个数据库

django-pgviews 可以使用多个数据库。与 Django 的migrate 管理命令类似,我们的命令(clear_pgviewsrefresh_pgviewssync_pgviews)一次对一个数据库进行操作。--database您可以通过提供选项来指定要同步的数据库。例如:

python manage.py sync_pgviews  # uses default db
python manage.py sync_pgviews --database=myotherdb

除非使用自定义路由器,否则 django-pgviews 会将所有视图同步到指定的数据库。如果您想自动与多个数据库交互,则需要采取一些额外的步骤。请参阅 Django 的自动数据库路由 以将视图固定到特定数据库。

Django 兼容性

Django 版本 Django-PG查看版本
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

Python 3 支持

Django PGViews Redux 仅正式支持 Python 3.7+,它可能适用于 3.6,但不能保证。

下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

django-pgviews-redux-0.8.4.tar.gz (18.2 kB 图哈希)

已上传 source

内置分布

django_pgviews_redux-0.8.4-py3-none-any.whl (17.6 kB 图哈希)

已上传 py3