Package: django-pgviews
SQL Views for PostgresAdds first-class support forPostgreSQL Viewsin the Django ORMInstallationInstall via pip:pip install django-pgviewsAdd to installed applications in settings.py:INSTALLED_APPS=(# ...'django_pgviews',)Examplesfromdjango.dbimportmodelsfromdjango_pgviewsimportviewaspgclassCustomer(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_pgviewsYou can also specify field names, which will map onto fields in your View:fromdjango_pgviewsimportviewaspgVIEW_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_pgviewsimportviewaspgVIEW_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-pgviewscan take a projection to figure out what fields it needs to
map onto for a view. To use this, set theprojectionattribute:fromdjango_pgviewsimportviewaspgclassPreferredCustomer(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_pgviews --forceThis will forcibly update any views that conflict with your new SQL.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_pgviews --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_pgviewsimportviewaspgclassPreferredCustomer(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_pgviewsimportviewaspgVIEW_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_pgviewsimportviewaspgVIEW_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_pgviewsimportviewaspgclassPreferredCustomer(pg.View):sql="""SELECT * FROM myapp_customer WHERE is_preferred = TRUE;"""classMeta:db_table='my_custom_schema.preferredcustomer'managed=FalseSync Listenersdjango-pgviews 0.5.0 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- AlwaysNoneDjango CompatibilityDjango VersionDjango-PGView Version1.4 and downUnsupported1.50.0.11.60.0.31.70.0.41.90.1.01.100.2.0Django 1.7 NoteDjango 1.7 changed how models are loaded so that it's no longer possible to dosql = str(User.objects.all().query)because the dependent models aren't
yet loaded by Django.Django 1.9 NoteYou now have to use the.viewmodule directly.Django 1.10 NoteWhen updating to Django 1.10, if you seeAttributeError: can't set attributewhen you try to migrate or run tests, you need to check your migrations for
where_base_manageror_default_managerget set on the model and replace it
withobjectsinside the migration.This also applies toDjango MPTTwho have covered this in a bit more detail.Python 3 SupportDjango PGViews supports Python 3 in versions 0.0.7 and above.Django 3.0 NoteChangedfrom django.utils import sixtoimport sixin theviews.pyfile, and addedsixas a dependency module due to the module being removed from Django.