1 # Copyright (c) 2009 Dennis Kaarsemaker <dennis@kaarsemaker.net> 2 # All rights reserved. 3 # 4 # Redistribution and use in source and binary forms, with or without modification, 5 # are permitted provided that the following conditions are met: 6 # 7 # 1. Redistributions of source code must retain the above copyright notice, 8 # this list of conditions and the following disclaimer. 9 # 10 # 2. Redistributions in binary form must reproduce the above copyright 11 # notice, this list of conditions and the following disclaimer in the 12 # documentation and/or other materials provided with the distribution. 13 # 14 # 3. Neither the name of Django nor the names of its contributors may be used 15 # to endorse or promote products derived from this software without
1 # Copyright (c) 2009 Dennis Kaarsemaker <dennis@kaarsemaker.net>
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without modification,
5 # are permitted provided that the following conditions are met:
6 #
7 # 1. Redistributions of source code must retain the above copyright notice,
8 # this list of conditions and the following disclaimer.
9 #
10 # 2. Redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution.
13 #
14 # 3. Neither the name of Django nor the names of its contributors may be used
15 # to endorse or promote products derived from this software without
16 # specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 from django.core.management.base import AppCommand
30
31 def sql_create_views(app, style):
32 from django.db import connection, models
33 from django.conf import settings
34
35 if settings.DATABASE_ENGINE == 'dummy':
36 # This must be the "dummy" database backend, which means the user
37 # hasn't set DATABASE_ENGINE.
38 raise CommandError("Django doesn't know which syntax to use for your SQL statements,\n" +
39 "because you haven't specified the DATABASE_ENGINE setting.\n" +
40 "Edit your settings file and change DATABASE_ENGINE to something like 'postgresql' or 'mysql'.")
41
42 app_models = models.get_models(app)
43 final_output = []
44
45 for model in app_models:
46 if not model._meta.parents:
47 continue # Not a multi-table-inheritance class
48 final_output.append(connection.creation.sql_create_model_view(model, style))
49 return final_output
50
51 # Patch django.core.management.sql to know sql_create_views
52 import django.core.management.sql
53 django.core.management.sql.sql_create_views = sql_create_views
54
55 def sql_create_model_view(self, model, style):
56 import re
57 qn = self.connection.ops.quote_name
58 view = style.SQL_KEYWORD('CREATE VIEW') + ' ' + \
59 style.SQL_TABLE(qn(model._meta.db_table + '_view')) + ' ' + \
60 style.SQL_KEYWORD('AS') + '\n' + style.SQL_KEYWORD('SELECT') + '\n'
61
62 fields_seen = []
63 def hl(m,filter=True):
64 table = style.SQL_TABLE(qn(m.group(1)))
65 field = m.group(2)
66 if field:
67 if filter and (field in fields_seen or field.endswith('_ptr_id')):
68 return ''
69 fields_seen.append(field)
70 table += '.' + qn(style.SQL_FIELD(field))
71 if filter:
72 return ' %s,\n' % table
73 return table
74
75 # Use the query internals to create the SELECT query
76 # Since that outputs quoted text, unquote it and mangle formatting
77 # Slightly hackish but works
78 query = model.objects.order_by().query
79 query.pre_sql_setup()
80 for col in query.get_columns():
81 view += re.sub(r'%s\.%s' % (qn(r'(\S+?)'), qn(r'(\S+?)')), hl, col)
82
83 view = view[:-2] + '\n' + style.SQL_KEYWORD('FROM')
84 for clause in query.get_from_clause()[0]:
85 clause = ' ' + re.sub(r'%s(?:\.%s)?' % (qn(r'(\S+?)'), qn(r'(\S+?)')), lambda m: hl(m, False), clause) + '\n'
86 clause = clause.replace(' ON ', ' ' + style.SQL_KEYWORD('ON') + ' ')
87 clause = clause.replace(' INNER JOIN ', ' ' + style.SQL_KEYWORD('INNER JOIN') + ' ')
88 view += clause
89 return view[:-1] + ';'
90
91 # Patch django.db.backends.creation to know sql_create_model_view
92 import django.db.backends.creation
93 django.db.backends.creation.BaseDatabaseCreation.sql_create_model_view = sql_create_model_view
94
95 class Command(AppCommand):
96 help = "Print CREATE VIEW statements for multi-table inheritance models"
97
98 output_transaction = True
99
100 def handle_app(self, app, **options):
101 from django.core.management.sql import sql_create_views
102 return u'\n'.join(sql_create_views(app, self.style)).encode('utf-8')
Show all