Browse Source

Add create and drop of SQL views

pull/245/head
Guewen Baconnier 9 years ago
parent
commit
5af43941ae
  1. 1
      sql_view/__init__.py
  2. 38
      sql_view/__openerp__.py
  3. 78
      sql_view/models/sql_view.py
  4. 3
      sql_view/security/ir.model.access.csv
  5. 28
      sql_view/views/sql_view_views.xml
  6. 3
      sql_view/wizards/__init__.py
  7. 82
      sql_view/wizards/sql_view_csv_preview.py
  8. 37
      sql_view/wizards/sql_view_csv_preview_views.xml

1
sql_view/__init__.py

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizards

38
sql_view/__openerp__.py

@ -33,31 +33,34 @@ SQL Views
This addon allows to create SQL views on the database. It also features
a simple CSV export of the views to check their result.
Configuration
=============
To configure this module, you need to:
* go to ...
Usage
=====
To use this module, you need to:
To create new SQL views, you need to go to ``Settings > Technical >
Database Structure > SQL Views``.
Give a view a human name, a SQL name (which will be prefixed with
``sql_view_`` in the database, and the definition of the view itself
(without trailing semicolon).
* go to ...
Known issues / Roadmap
======================
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/7.0
The CSV preview can be used to read any data on the database. So this
menu **must** be accessible only by allowed admin users. By
default, the module is configured to be accessible only by users having
the ``Settings`` administration level.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/server-tools/issues/new?body=module:%20sql_view%0Aversion:%207.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
If you spotted it first, help us smashing it by providing a detailed and
welcomed feedback
`here
<https://github.com/OCA/server-tools/issues/new?body=module:%20sql_view%0Aversion:%207.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Credits
=======
@ -83,7 +86,10 @@ promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.
""",
'website': 'http://www.camptocamp.com',
'data': ['views/sql_view_views.xml',
'external_dependencies': {'python': ['unicodecsv']},
'data': ['wizards/sql_view_csv_preview_views.xml',
'views/sql_view_views.xml',
'security/ir.model.access.csv',
],
'installable': True,
}

78
sql_view/models/sql_view.py

@ -20,7 +20,9 @@
#
import re
import psycopg2
from openerp.osv import orm, fields
from openerp.tools.translate import _
# views are created with a prefix to prevent conflicts
SQL_VIEW_PREFIX = u'sql_view_'
@ -30,13 +32,26 @@ USER_NAME_SIZE = 63 - len(SQL_VIEW_PREFIX)
PG_NAME_RE = re.compile(r'^[a-z_][a-z0-9_$]*$', re.I)
class sql_view(orm.Model):
class SQLView(orm.Model):
_name = 'sql.view'
def _compute_complete_sql_name(self, cr, uid, ids, name, args,
context=None):
res = {}
for sql_view in self.browse(cr, uid, ids, context=context):
res[sql_view.id] = SQL_VIEW_PREFIX + sql_view.sql_name
return res
_columns = {
'name': fields.char(string='View Name', required=True),
'sql_name': fields.char(string='SQL Name', required=True,
size=USER_NAME_SIZE),
size=USER_NAME_SIZE,
help="Name of the view. Will be prefixed "
"by %s" % (SQL_VIEW_PREFIX,)),
'complete_sql_name': fields.function(_compute_complete_sql_name,
string='Complete SQL Name',
readonly=True,
type='char'),
'definition': fields.text(string='Definition', required=True),
}
@ -49,3 +64,62 @@ class sql_view(orm.Model):
'The SQL name is not a valid PostgreSQL identifier',
['sql_name']),
]
def _sql_view_comment(self, cr, uid, sql_view, context=None):
return "%s (created by the module sql_view)" % sql_view.name
def _create_or_replace_sql_view(self, cr, uid, sql_view, context=None):
view_name = sql_view.complete_sql_name
try:
# The parenthesis around the definition are important:
# they prevent to insert a semicolon and another query after
# the view declaration
cr.execute(
"CREATE VIEW {view_name} AS ({definition})".format(
view_name=view_name,
definition=sql_view.definition)
)
except psycopg2.ProgrammingError as err:
raise orm.except_orm(
_('Error'),
_('The definition of the view is not correct:\n\n%s') % (err,)
)
comment = self._sql_view_comment(cr, uid, sql_view, context=context)
cr.execute(
"COMMENT ON VIEW {view_name} IS %s".format(view_name=view_name),
(comment,)
)
def _delete_sql_view(self, cr, uid, sql_view, context=None):
view_name = sql_view.complete_sql_name
cr.execute("DROP view IF EXISTS %s" % (view_name,))
def create(self, cr, uid, vals, context=None):
record_id = super(SQLView, self).create(cr, uid, vals,
context=context)
record = self.browse(cr, uid, record_id, context=context)
self._create_or_replace_sql_view(cr, uid, record, context=context)
return record_id
def write(self, cr, uid, ids, vals, context=None):
# If the name has been changed, we have to drop the view,
# it will be created with the new name.
# If the definition have been changed, we better have to delete
# it and create it again otherwise we can have 'cannot drop
# columns from view' errors.
for record in self.browse(cr, uid, ids, context=context):
self._delete_sql_view(cr, uid, record, context=context)
result = super(SQLView, self).write(cr, uid, ids, vals,
context=context)
for record in self.browse(cr, uid, ids, context=context):
self._create_or_replace_sql_view(cr, uid, record,
context=context)
return result
def unlink(self, cr, uid, ids, context=None):
for record in self.browse(cr, uid, ids, context=context):
self._delete_sql_view(cr, uid, record, context=context)
result = super(SQLView, self).unlink(cr, uid, ids, context=context)
return result

3
sql_view/security/ir.model.access.csv

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sql_view_admin,sql_view admin,model_sql_view,base.group_system,1,1,1,1

28
sql_view/views/sql_view_views.xml

@ -5,12 +5,24 @@
<record id="view_sql_view_form" model="ir.ui.view">
<field name="name">sql.view.form</field>
<field name="model">sql.view</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="SQL Views">
<field name="name"/>
<field name="sql_name"/>
<field name="definition" colspan="4"/>
<form string="SQL View" version="7.0">
<header>
<button name="%(action_sql_view_csv_preview)d"
type="action"
class="oe_highlight"
string="CSV Preview"/>
</header>
<sheet string="SQL View">
<group>
<field name="name"/>
<field name="sql_name"/>
<field name="complete_sql_name"/>
</group>
<group string="Definition">
<field name="definition" nolabel="1"/>
</group>
</sheet>
</form>
</field>
</record>
@ -18,11 +30,10 @@
<record id="view_sql_view_tree" model="ir.ui.view">
<field name="name">sql.view.tree</field>
<field name="model">sql.view</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="SQL Views">
<field name="name"/>
<field name="sql_name"/>
<field name="complete_sql_name"/>
</tree>
</field>
</record>
@ -30,11 +41,10 @@
<record id="view_sql_view_search" model="ir.ui.view">
<field name="name">sql.view.filter</field>
<field name="model">sql.view</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="SQL Views">
<field name="name"/>
<field name="sql_name"/>
<field name="complete_sql_name"/>
</search>
</field>
</record>

3
sql_view/wizards/__init__.py

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import sql_view_csv_preview

82
sql_view/wizards/sql_view_csv_preview.py

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
#
#
# Authors: Guewen Baconnier
# Copyright 2015 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
import base64
from StringIO import StringIO
import unicodecsv
from openerp.osv import orm, fields
class SQLViewCSVPreview(orm.TransientModel):
_name = 'sql.view.csv.preview'
_columns = {
'limit': fields.integer(string='Limit',
help='Number of records. 0 means infinite.'),
'data': fields.binary('CSV', readonly=True),
'filename': fields.char('File Name', readonly=True),
}
_defaults = {
'filename': 'csv-preview.csv',
'limit': 100,
}
def _query(self, cr, uid, form, sql_view, context=None):
view_name = sql_view.complete_sql_name
query = "SELECT * FROM {view_name} "
if form.limit:
query += "LIMIT {limit}"
return query.format(view_name=view_name, limit=form.limit)
def export_csv(self, cr, uid, ids, context=None):
if context is None:
return
sql_view_ids = context.get('active_ids', [])
assert len(ids) == 1, "1 wizard ID expected"
assert len(sql_view_ids) == 1, "1 active ID expected"
form = self.browse(cr, uid, ids[0], context=context)
sql_view = self.pool['sql.view'].browse(cr, uid, sql_view_ids[0],
context=context)
query = self._query(cr, uid, form, sql_view, context=context)
cr.execute(query)
headers = [desc[0] for desc in cr.description]
records = cr.fetchall()
filedata = StringIO()
try:
writer = unicodecsv.writer(filedata, encoding='utf-8')
writer.writerow(headers)
writer.writerows(records)
form.write({'data': base64.encodestring(filedata.getvalue())})
finally:
filedata.close()
return {
'type': 'ir.actions.act_window',
'res_model': self._name,
'view_mode': 'form',
'view_type': 'form',
'res_id': ids[0],
'views': [(False, 'form')],
'target': 'new',
}

37
sql_view/wizards/sql_view_csv_preview_views.xml

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<record id="view_sql_view_csv_preview_form" model="ir.ui.view">
<field name="name">sql.view.csv.preview.form</field>
<field name="model">sql.view.csv.preview</field>
<field name="arch" type="xml">
<form string="SQL View CSV Preview" version="7.0">
<group>
<field name="limit" attrs="{'invisible': [('data', '!=', False)]}"/>
<field name="filename" invisible="1"/>
<field name="data" attrs="{'invisible': [('data', '=', False)]}"
filename="filename"/>
</group>
<footer attrs="{'invisible': [('data', '!=', False)]}">
<button string="Preview" name="export_csv" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
<footer attrs="{'invisible': [('data', '=', False)]}">
<button special="cancel" string="Close" type="object"/>
</footer>
</form>
</field>
</record>
<record id="action_sql_view_csv_preview" model="ir.actions.act_window">
<field name="name">SQL View CSV Preview</field>
<field name="res_model">sql.view.csv.preview</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_sql_view_csv_preview_form"/>
<field name="target">new</field>
</record>
</data>
</openerp>
Loading…
Cancel
Save