Package: django-postgres-views
SQL Views for PostgresAdds first-class support forPostgreSQL Viewsin the Django ORMInstallationInstall via pip:pip install django-postgres-viewsAdd to installed applications in settings.py:INSTALLED_APPS=(# ...'django_postgres_views',)Examplesfromdjango.dbimportmodelsfromdjango_postgres_viewsimportviewaspgclassCustomer(models.Model):name=models.CharField(max_length=100)post_code=models.CharField(max_length=20)is_preferred=models.BooleanField(default=False)classMeta:app_label='myapp'classPreferredCustomer(pg.View):projection=['myapp.Customer.*',]dependencies=['myapp.OtherView',]sql="""SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""classMeta:app_label='myapp'db_table='myapp_preferredcustomer'managed=FalseNOTEIt is important that we include themanaged = Falsein theMetaso
Django 1.7 migrations don't attempt to create DB tables for this view.The SQL produced by this might look like:CREATEVIEWmyapp_preferredcustomerASSELECT*FROMmyapp_customerWHEREis_preferred=TRUE;To create all your views, runpython manage.py sync_postgres_viewsYou can also specify field names, which will map onto fields in your View:fromdjango_postgres_viewsimportviewaspgVIEW_SQL="""SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE"""classPreferredCustomer(pg.View):name=models.CharField(max_length=100)post_code=models.CharField(max_length=20)sql=VIEW_SQLUsageTo map onto a View, simply extendpg_views.view.View, assign SQL to thesqlargument and define adb_table. You mustalwayssetmanaged = Falseon theMetaclass.Views can be created in a number of ways:Define fields to map onto the VIEW outputDefine a projection that describes the VIEW fieldsDefine FieldsDefine the fields as you would with any Django Model:fromdjango_postgres_viewsimportviewaspgVIEW_SQL="""SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE"""classPreferredCustomer(pg.View):name=models.CharField(max_length=100)post_code=models.CharField(max_length=20)sql=VIEW_SQLclassMeta:managed=Falsedb_table='my_sql_view'Define Projectiondjango-postgres-viewscan take a projection to figure out what fields it needs to
map onto for a view. To use this, set theprojectionattribute:fromdjango_postgres_viewsimportviewaspgclassPreferredCustomer(pg.View):projection=['myapp.Customer.*',]sql="""SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""classMeta:db_table='my_sql_view'managed=FalseThis will take all fields onmyapp.Customerand apply them toPreferredCustomerFeaturesUpdating ViewsSometimes your models change and you need your Database Views to reflect the new
data. Updating the View logic is as simple as modifying the underlying SQL and
running:python manage.py sync_postgres_views --forceThis will forcibly update any views that conflict with your new SQL.MigrationsWhen running migrations, we will automatically sync your new views using apostmigrationsignal, if at any moment you want to override this functionality
you can supplement your own by adding the full path toPGVIEW_SYNC_VIEW_PATHwith your own migration command.DependenciesYou can specify other views you depend on. This ensures the other views are
installed beforehand. Using dependencies also ensures that your views get
refreshed correctly when usingsync_postgres_views --force.Note:Views are synced after the Django application has migrated and adding
models to the dependency list will cause syncing to fail.Example:fromdjango_postgres_viewsimportviewaspgclassPreferredCustomer(pg.View):dependencies=['myapp.OtherView',]sql="""SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""classMeta:app_label='myapp'db_table='myapp_preferredcustomer'managed=FalseMaterialized ViewsPostgres 9.3 and up supportsmaterialized viewswhich allow you to cache the results of views, potentially allowing them
to load faster.However, you do need to manually refresh the view. To do this automatically,
you can attachsignalsand call the refresh function.Example:fromdjango_postgres_viewsimportviewaspgVIEW_SQL="""SELECT name, post_code FROM myapp_customer WHERE is_preferred = TRUE"""classCustomer(models.Model):name=models.CharField(max_length=100)post_code=models.CharField(max_length=20)is_preferred=models.BooleanField(default=True)classPreferredCustomer(pg.MaterializedView):name=models.CharField(max_length=100)post_code=models.CharField(max_length=20)sql=VIEW_SQL@receiver(post_save,sender=Customer)defcustomer_saved(sender,action=None,instance=None,**kwargs):PreferredCustomer.refresh()Postgres 9.4 and up allow materialized views to be refreshed concurrently, without blocking reads, as long as a
unique index exists on the materialized view. To enable concurrent refresh, specify the name of a column that can be
used as a unique index on the materialized view. Unique index can be defined on more than one column of a materialized
view. Once enabled, passingconcurrently=Trueto the model's refresh method will result in postgres performing the
refresh concurrently. (Note that the refresh method itself blocks until the refresh is complete; concurrent refresh is
most useful when materialized views are updated in another process or thread.)Example:fromdjango_postgres_viewsimportviewaspgVIEW_SQL="""SELECT id, name, post_code FROM myapp_customer WHERE is_preferred = TRUE"""classPreferredCustomer(pg.MaterializedView):concurrent_index='id, post_code'sql=VIEW_SQLname=models.CharField(max_length=100)post_code=models.CharField(max_length=20)@receiver(post_save,sender=Customer)defcustomer_saved(sender,action=None,instance=None,**kwargs):PreferredCustomer.refresh(concurrently=True)Custom SchemaYou can define any table name you wish for your views. They can even live inside your own customPostgreSQL schema.fromdjango_postgres_viewsimportviewaspgclassPreferredCustomer(pg.View):sql="""SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""classMeta:db_table='my_custom_schema.preferredcustomer'managed=FalseSync Listenersdjango-postgres-views 0.0.1 adds the ability to listen to when apost_syncevent has
occurred.view_syncedFired every time a VIEW is synchronised with the database.Provides args:sender- View Classupdate- Whether the view to be updatedforce- Whetherforcewas passedstatus- The result of creating the view e.g.EXISTS,FORCE_REQUIREDhas_changed- Whether the view had to changeall_views_syncedSent after all Postgres VIEWs are synchronised.Provides args:sender- AlwaysNone