9
9
10
10
from odoo import SUPERUSER_ID , _ , api , fields , models
11
11
from odoo .exceptions import UserError , ValidationError
12
- from odoo .tools import sql , table_columns
12
+ from odoo .tools import sql
13
13
from odoo .tools .safe_eval import safe_eval
14
14
15
15
_logger = logging .getLogger (__name__ )
@@ -44,14 +44,12 @@ class BiSQLView(models.Model):
44
44
45
45
view_name = fields .Char (
46
46
compute = "_compute_view_name" ,
47
- readonly = True ,
48
47
store = True ,
49
48
help = "Full name of the SQL view" ,
50
49
)
51
50
52
51
model_name = fields .Char (
53
52
compute = "_compute_model_name" ,
54
- readonly = True ,
55
53
store = True ,
56
54
help = "Full Qualified Name of the transient model that will" " be created." ,
57
55
)
@@ -65,16 +63,15 @@ class BiSQLView(models.Model):
65
63
66
64
size = fields .Char (
67
65
string = "Database Size" ,
68
- readonly = True ,
69
66
help = "Size of the materialized view and its indexes" ,
70
67
)
71
68
72
69
state = fields .Selection (selection_add = _STATE_SQL_EDITOR )
73
70
74
71
view_order = fields .Char (
75
72
required = True ,
76
- default = "pivot,graph,tree " ,
77
- help = "Comma-separated text. Possible values:" ' "graph", "pivot" or "tree "' ,
73
+ default = "pivot,graph,list " ,
74
+ help = "Comma-separated text. Possible values:" ' "graph", "pivot" or "list "' ,
78
75
)
79
76
80
77
query = fields .Text (
@@ -111,9 +108,7 @@ class BiSQLView(models.Model):
111
108
inverse_name = "bi_sql_view_id" ,
112
109
)
113
110
114
- model_id = fields .Many2one (
115
- string = "Odoo Model" , comodel_name = "ir.model" , readonly = True
116
- )
111
+ model_id = fields .Many2one (string = "Odoo Model" , comodel_name = "ir.model" )
117
112
# UI related fields
118
113
# 1. Editable fields, which can be set by the user (optional) before
119
114
# creating the UI elements
@@ -133,39 +128,30 @@ def _default_parent_menu_id(self):
133
128
)
134
129
135
130
# 2. Readonly fields, non editable by the user
136
- tree_view_id = fields .Many2one (
137
- string = "Odoo Tree View" , comodel_name = "ir.ui.view" , readonly = True
138
- )
131
+ tree_view_id = fields .Many2one (string = "Odoo List View" , comodel_name = "ir.ui.view" )
139
132
140
- graph_view_id = fields .Many2one (
141
- string = "Odoo Graph View" , comodel_name = "ir.ui.view" , readonly = True
142
- )
133
+ graph_view_id = fields .Many2one (string = "Odoo Graph View" , comodel_name = "ir.ui.view" )
143
134
144
- pivot_view_id = fields .Many2one (
145
- string = "Odoo Pivot View" , comodel_name = "ir.ui.view" , readonly = True
146
- )
135
+ pivot_view_id = fields .Many2one (string = "Odoo Pivot View" , comodel_name = "ir.ui.view" )
147
136
148
137
search_view_id = fields .Many2one (
149
- string = "Odoo Search View" , comodel_name = "ir.ui.view" , readonly = True
138
+ string = "Odoo Search View" , comodel_name = "ir.ui.view"
150
139
)
151
140
152
141
action_id = fields .Many2one (
153
- string = "Odoo Action" , comodel_name = "ir.actions.act_window" , readonly = True
142
+ string = "Odoo Action" , comodel_name = "ir.actions.act_window"
154
143
)
155
144
156
- menu_id = fields .Many2one (
157
- string = "Odoo Menu" , comodel_name = "ir.ui.menu" , readonly = True
158
- )
145
+ menu_id = fields .Many2one (string = "Odoo Menu" , comodel_name = "ir.ui.menu" )
159
146
160
147
cron_id = fields .Many2one (
161
148
string = "Odoo Cron" ,
162
149
comodel_name = "ir.cron" ,
163
- readonly = True ,
164
150
help = "Cron Task that will refresh the materialized view" ,
165
151
ondelete = "cascade" ,
166
152
)
167
153
168
- rule_id = fields .Many2one (string = "Odoo Rule" , comodel_name = "ir.rule" , readonly = True )
154
+ rule_id = fields .Many2one (string = "Odoo Rule" , comodel_name = "ir.rule" )
169
155
170
156
sequence = fields .Integer (string = "sequence" )
171
157
@@ -183,9 +169,9 @@ def _check_view_order(self):
183
169
for rec in self :
184
170
if rec .view_order :
185
171
for vtype in rec .view_order .split ("," ):
186
- if vtype not in ("graph" , "pivot" , "tree " ):
172
+ if vtype not in ("graph" , "pivot" , "list " ):
187
173
raise UserError (
188
- _ ("Only graph, pivot or tree views are supported" )
174
+ _ ("Only graph, pivot or list views are supported" )
189
175
)
190
176
191
177
# Compute Section
@@ -244,15 +230,15 @@ def write(self, vals):
244
230
rec .menu_id .sequence = rec .sequence
245
231
return res
246
232
247
- def unlink (self ):
233
+ @api .ondelete (at_uninstall = False )
234
+ def _check_unlink_constraints (self ):
248
235
if any (view .state not in ("draft" , "sql_valid" ) for view in self ):
249
236
raise UserError (
250
237
_ (
251
- "You can only unlink draft views."
238
+ "You can only unlink draft views. "
252
239
"If you want to delete them, first set them to draft."
253
240
)
254
241
)
255
- return super ().unlink ()
256
242
257
243
def copy (self , default = None ):
258
244
self .ensure_one ()
@@ -395,8 +381,7 @@ def _prepare_cron(self):
395
381
.search ([("model" , "=" , self ._name )], limit = 1 )
396
382
.id ,
397
383
"state" : "code" ,
398
- "code" : "model._refresh_materialized_view_cron(%s)" % self .ids ,
399
- "numbercall" : - 1 ,
384
+ "code" : f"model._refresh_materialized_view_cron({ self .ids } )" ,
400
385
"interval_number" : 1 ,
401
386
"interval_type" : "days" ,
402
387
"nextcall" : now + timedelta (days = 1 ),
@@ -416,11 +401,11 @@ def _prepare_tree_view(self):
416
401
self .ensure_one ()
417
402
return {
418
403
"name" : self .name ,
419
- "type" : "tree " ,
404
+ "type" : "list " ,
420
405
"model" : self .model_id .model ,
421
406
"arch" : """<?xml version="1.0"?>"""
422
- """<tree name="Analysis">{}"""
423
- """</tree >""" .format (
407
+ """<list name="Analysis">{}"""
408
+ """</list >""" .format (
424
409
"" .join ([x ._prepare_tree_field () for x in self .bi_sql_view_field_ids ])
425
410
),
426
411
}
@@ -477,7 +462,7 @@ def _prepare_action(self):
477
462
self .ensure_one ()
478
463
view_mode = self .view_order
479
464
first_view = view_mode .split ("," )[0 ]
480
- if first_view == "tree " :
465
+ if first_view == "list " :
481
466
view_id = self .tree_view_id .id
482
467
elif first_view == "pivot" :
483
468
view_id = self .pivot_view_id .id
@@ -510,13 +495,13 @@ def _prepare_menu(self):
510
495
return {
511
496
"name" : self .name ,
512
497
"parent_id" : self .parent_menu_id .id ,
513
- "action" : "ir.actions.act_window,%s" % self .action_id .id ,
498
+ "action" : f "ir.actions.act_window,{ self .action_id .id } " ,
514
499
"sequence" : self .sequence ,
515
500
}
516
501
517
502
# Custom Section
518
503
def _log_execute (self , req ):
519
- _logger .info ("Executing SQL Request %s ..." % req )
504
+ _logger .info (f "Executing SQL Request { req } ..." )
520
505
self .env .cr .execute (req )
521
506
522
507
def _drop_view (self ):
@@ -562,7 +547,7 @@ def _create_model_and_fields(self):
562
547
sql_view .rule_id = self .env ["ir.rule" ].create (self ._prepare_rule ()).id
563
548
# Drop table, created by the ORM
564
549
if sql .table_exists (self ._cr , sql_view .view_name ):
565
- req = "DROP TABLE %s" % sql_view .view_name
550
+ req = f "DROP TABLE { sql_view .view_name } "
566
551
self ._log_execute (req )
567
552
568
553
def _create_model_access (self ):
@@ -585,18 +570,15 @@ def _drop_model_and_fields(self):
585
570
586
571
def _hook_executed_request (self ):
587
572
self .ensure_one ()
588
- req = (
589
- """
573
+ req = f"""
590
574
SELECT attnum,
591
575
attname AS column,
592
576
format_type(atttypid, atttypmod) AS type
593
577
FROM pg_attribute
594
- WHERE attrelid = '%s '::regclass
578
+ WHERE attrelid = '{ self . view_name } '::regclass
595
579
AND NOT attisdropped
596
580
AND attnum > 0
597
581
ORDER BY attnum;"""
598
- % self .view_name
599
- )
600
582
self ._log_execute (req )
601
583
return self .env .cr .fetchall ()
602
584
@@ -606,8 +588,7 @@ def _prepare_request_check_execution(self):
606
588
607
589
def _prepare_request_for_execution (self ):
608
590
self .ensure_one ()
609
- query = (
610
- """
591
+ query = f"""
611
592
SELECT
612
593
CAST(row_number() OVER () as integer) AS id,
613
594
CAST(Null as timestamp without time zone) as create_date,
@@ -616,10 +597,8 @@ def _prepare_request_for_execution(self):
616
597
CAST(Null as integer) as write_uid,
617
598
my_query.*
618
599
FROM
619
- (%s ) as my_query
600
+ ({ self . query } ) as my_query
620
601
"""
621
- % self .query
622
- )
623
602
return f"CREATE { self .materialized_text } VIEW { self .view_name } AS ({ query } );"
624
603
625
604
def _check_execution (self ):
@@ -689,9 +668,9 @@ def _refresh_materialized_view(self):
689
668
690
669
def _refresh_size (self ):
691
670
for sql_view in self :
692
- req = " SELECT pg_size_pretty(pg_total_relation_size('%s'));" % (
693
- sql_view .view_name
694
- )
671
+ req = f""" SELECT pg_size_pretty(pg_total_relation_size(
672
+ ' { sql_view .view_name } '
673
+ ));"""
695
674
self ._log_execute (req )
696
675
sql_view .size = self .env .cr .fetchone ()[0 ]
697
676
@@ -700,7 +679,7 @@ def check_manual_fields(self, model):
700
679
# early on install / startup - particularly problematic during upgrade
701
680
if model ._name .startswith (
702
681
self ._model_prefix
703
- ) and "group_operator" in table_columns (self .env .cr , "bi_sql_view_field" ):
682
+ ) and "group_operator" in sql . table_columns (self .env .cr , "bi_sql_view_field" ):
704
683
# Use SQL instead of ORM, as ORM might not be fully initialised -
705
684
# we have no control over the order that fields are defined!
706
685
# We are not concerned about user security rules.
0 commit comments