Skip to content

Commit 097e6d5

Browse files
scott-wzacharyvoase
authored andcommitted
Split syncing of views into a separate mgmt command.
1 parent 8bf8e48 commit 097e6d5

File tree

10 files changed

+67
-40
lines changed

10 files changed

+67
-40
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,5 @@ CREATE TABLE myapp_customer (
107107
CREATE VIEW myapp_preferredcustomer AS
108108
SELECT * FROM myapp_customer WHERE is_preferred = TRUE;
109109
```
110+
111+
To create all your views, run ``python manage.py sync_pgviews``

dev_requirements

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Django>=1.3
2+
django-nose>=1.1

django_postgres/management/__init__.py

Whitespace-only changes.

django_postgres/management/commands/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""Synchronise SQL Views.
2+
"""
3+
from django.core.management.base import NoArgsCommand
4+
from django.db import models
5+
6+
from django_postgres.view import create_views
7+
8+
9+
class Command(NoArgsCommand):
10+
help = 'Creates and Updates all SQL Views'
11+
12+
def handle_noargs(self, **options):
13+
all_modules = models.get_apps()
14+
modules = '\n '.join((m.__name__ for m in all_modules))
15+
self.stdout.write(
16+
'Creating Views for all modules:\n {modules}\n'.format(
17+
modules=modules
18+
)
19+
)
20+
for module in all_modules:
21+
create_views(module)

django_postgres/view.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import copy
55
import re
66

7-
from django.db import connection
7+
from django.db import connection, transaction
88
from django.db import models
99

1010

@@ -51,22 +51,26 @@ def realize_deferred_projections(sender, *args, **kwargs):
5151
models.signals.class_prepared.connect(realize_deferred_projections)
5252

5353

54-
def create_views(sender, *args, **kwargs):
54+
def create_views(models_module, *args, **kwargs):
5555
"""Create the database views after syncdb."""
56-
models_module = sender
5756
for name, view_cls in vars(models_module).iteritems():
5857
if not (isinstance(view_cls, type) and
5958
issubclass(view_cls, View) and
6059
hasattr(view_cls, 'sql')):
6160
continue
62-
query = "CREATE OR REPLACE VIEW %s AS %s;" % (view_cls._meta.db_table,
63-
view_cls.sql)
61+
62+
query = 'CREATE OR REPLACE VIEW {table} AS {select};'
63+
64+
query = query.format(
65+
table=view_cls._meta.db_table,
66+
select=view_cls.sql)
67+
6468
cursor = connection.cursor()
6569
try:
6670
cursor.execute(query)
71+
transaction.commit_unless_managed()
6772
finally:
6873
cursor.close()
69-
models.signals.post_syncdb.connect(create_views)
7074

7175

7276
def get_fields_by_name(model_cls, *field_names):

doc/views.rst

+9-19
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ You can create this view by just subclassing :class:`django_postgres.View`. In
4040
.. attribute:: sql
4141

4242
The SQL for this view (typically a ``SELECT`` query). This attribute is
43-
optional, but if present, the view will be created on ``syncdb`` (which is
44-
probably what you want).
43+
optional, but if present, the view will be created on ``sync_pgviews``
44+
(which is probably what you want).
4545

4646
.. attribute:: projection
4747

@@ -93,23 +93,13 @@ Or add an ``id`` column to your view's SQL query (this example uses
9393
FROM auth_user;"""
9494

9595

96-
Migrations
97-
==========
98-
99-
Views play well with South migrations; just create the view using raw SQL in a
100-
schema migration:
101-
102-
.. code-block:: bash
96+
Creating the Views
97+
==================
10398

104-
$ ./manage.py schemamigration --empty myapp create_view_viewname
105-
Created 0001_create_view_latest_override.py.
106-
$ edit myapp/migrations/0001_create_view_viewname.py
99+
Creating the views is simple. Just run the ``sync_pgviews`` command.
107100

108-
In the migration file::
109-
110-
def forwards(self, orm):
111-
db.execute('''CREATE OR REPLACE VIEW myapp_viewname AS
112-
SELECT * FROM myapp_table WHERE condition;''')
101+
Migrations
102+
==========
113103

114-
def backwards(self, orm):
115-
db.execute('''DROP VIEW myapp_viewname;''')
104+
Views play well with South migrations; just run ``sync_pgviews`` after
105+
``migrate`` to ensure any required tables have been created/updated.

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
setup(
44
name='django-postgres',
5-
version='0.0.1',
5+
version='0.0.2',
66
description="First-class Postgres feature support for the Django ORM.",
77
author='Zachary Voase',
88
author_email='z@zacharyvoase.com',
99
license='Public Domain',
1010
packages=find_packages(),
1111
install_requires=[
1212
'bitstring',
13+
'Django>=1.3',
1314
],
1415
)

test_project/test_project/settings.py

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def git_name_and_email():
113113
'django.contrib.auth',
114114
'django.contrib.contenttypes',
115115
'django_nose',
116+
'django_postgres',
116117
'viewtest',
117118
'arraytest',
118119
'bitstringtest',

test_project/viewtest/tests.py

+20-14
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
11
from contextlib import closing
22

33
from django.contrib import auth
4+
from django.core.management import call_command
45
from django.db import connection
56
from django.test import TestCase
67

78
import models
89

910

10-
class ViewTest(TestCase):
11+
class ViewTestCase(TestCase):
12+
13+
def setUp(self):
14+
call_command('sync_pgviews', *[], **{})
1115

1216
def test_views_have_been_created(self):
1317
with closing(connection.cursor()) as cur:
1418
cur.execute('''SELECT COUNT(*) FROM pg_views
1519
WHERE viewname LIKE 'viewtest_%';''')
20+
1621
count, = cur.fetchone()
17-
assert count == 3
22+
self.assertEqual(count, 3)
1823

1924
def test_wildcard_projection(self):
20-
foo_user = auth.models.User.objects.create(username='foo',
21-
is_superuser=True)
25+
foo_user = auth.models.User.objects.create(
26+
username='foo', is_superuser=True)
2227
foo_user.set_password('blah')
2328
foo_user.save()
2429

2530
foo_superuser = models.Superusers.objects.get(username='foo')
2631

27-
assert foo_user.id == foo_superuser.id
28-
assert foo_user.password == foo_superuser.password
32+
self.assertEqual(foo_user.id, foo_superuser.id)
33+
self.assertEqual(foo_user.password, foo_superuser.password)
2934

3035
def test_limited_projection(self):
31-
foo_user = auth.models.User.objects.create(username='foo',
32-
is_superuser=True)
36+
foo_user = auth.models.User.objects.create(
37+
username='foo', is_superuser=True)
3338
foo_user.set_password('blah')
3439
foo_user.save()
3540

3641
foo_simple = models.SimpleUser.objects.get(username='foo')
37-
assert foo_simple.username == foo_user.username
38-
assert foo_simple.password == foo_user.password
39-
assert not hasattr(foo_simple, 'date_joined')
42+
self.assertEqual(foo_simple.username, foo_user.username)
43+
self.assertEqual(foo_simple.password, foo_user.password)
44+
self.assertFalse(hasattr(foo_simple, 'date_joined'))
4045

4146
def test_queryset_based_view(self):
42-
foo_user = auth.models.User.objects.create(username='foo',
43-
is_staff=True)
47+
auth.models.User.objects.create(
48+
username='foo', is_staff=True)
4449

45-
assert models.Staffness.objects.filter(username='foo').exists()
50+
self.assertTrue(
51+
models.Staffness.objects.filter(username='foo').exists())

0 commit comments

Comments
 (0)