Merge lp:~openerp-dev/openobject-server/trunk-smart-fields-pgtype-xmo into lp:openobject-server
- trunk-smart-fields-pgtype-xmo
- Merge into trunk
Proposed by
Xavier (Open ERP)
Status: | Needs review |
---|---|
Proposed branch: | lp:~openerp-dev/openobject-server/trunk-smart-fields-pgtype-xmo |
Merge into: | lp:openobject-server |
Diff against target: |
325 lines (+85/-79) 2 files modified
openerp/osv/fields.py (+75/-4) openerp/osv/orm.py (+10/-75) |
To merge this branch: | bzr merge lp:~openerp-dev/openobject-server/trunk-smart-fields-pgtype-xmo |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenERP Core Team | Pending | ||
Review via email: mp+109134@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Unmerged revisions
- 4189. By Xavier (Open ERP)
-
[IMP] move conversion from OpenERP fields to Postgres types from the ORM to the fields
* Cleaner
* Fields are more self-contained (can understand mapping from reading the field)
* Simpler to create new field types (WIP)
* Removes a bunch of code from orm
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'openerp/osv/fields.py' |
2 | --- openerp/osv/fields.py 2012-05-21 07:38:51 +0000 |
3 | +++ openerp/osv/fields.py 2012-06-07 12:55:24 +0000 |
4 | @@ -78,6 +78,9 @@ |
5 | # used to hide a certain field type in the list of field types |
6 | _deprecated = False |
7 | |
8 | + # Postgres data type for the column, returned by pg_type unless overwritten |
9 | + _pg_type = None |
10 | + |
11 | def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete=None, translate=False, select=False, manual=False, **args): |
12 | """ |
13 | |
14 | @@ -127,6 +130,37 @@ |
15 | res = obj.read(cr, uid, ids, [name], context=context) |
16 | return [x[name] for x in res] |
17 | |
18 | + @property |
19 | + def pg_type(self): |
20 | + """ Postgres column types for the OpenERP field, fully qualified (e.g. |
21 | + including size for char columns with one) |
22 | + |
23 | + :returns: tuple |
24 | + :rtype: (str, str) |
25 | + """ |
26 | + return self.pg_type_for(self) |
27 | + |
28 | + @classmethod |
29 | + def pg_type_for(cls, field): |
30 | + """ Method actually fetching the postgres type for a field. Has to be |
31 | + a class method in case a column type A needs to delegate to a column |
32 | + type B (e.g. function fields) |
33 | + |
34 | + By default, simply returns the value of the _pg_type class attribute. |
35 | + |
36 | + :param cls: current column object |
37 | + :param field: current column instance |
38 | + :returns: a pair of (type, type) |
39 | + :rtype: (str, str) |
40 | + """ |
41 | + return cls._pg_type |
42 | + |
43 | +class _varchar(_column): |
44 | + @classmethod |
45 | + def pg_type_for(cls, field): |
46 | + if not field.size: |
47 | + return 'varchar', 'varchar' |
48 | + return 'varchar', 'varchar(%d)' % field.size |
49 | |
50 | # --------------------------------------------------------- |
51 | # Simple fields |
52 | @@ -137,6 +171,8 @@ |
53 | _symbol_f = lambda x: x and 'True' or 'False' |
54 | _symbol_set = (_symbol_c, _symbol_f) |
55 | |
56 | + _pg_type = ('bool', 'bool') |
57 | + |
58 | def __init__(self, string='unknown', required=False, **args): |
59 | super(boolean, self).__init__(string=string, required=required, **args) |
60 | if required: |
61 | @@ -152,6 +188,8 @@ |
62 | _symbol_set = (_symbol_c, _symbol_f) |
63 | _symbol_get = lambda self,x: x or 0 |
64 | |
65 | + _pg_type = ('int4', 'int4') |
66 | + |
67 | def __init__(self, string='unknown', required=False, **args): |
68 | super(integer, self).__init__(string=string, required=required, **args) |
69 | if required: |
70 | @@ -160,7 +198,7 @@ |
71 | " `required` has no effect, as NULL values are " |
72 | "automatically turned into 0.") |
73 | |
74 | -class reference(_column): |
75 | +class reference(_varchar): |
76 | _type = 'reference' |
77 | _classic_read = False # post-process to handle missing target |
78 | |
79 | @@ -178,7 +216,7 @@ |
80 | result[value['id']] = False |
81 | return result |
82 | |
83 | -class char(_column): |
84 | +class char(_varchar): |
85 | _type = 'char' |
86 | |
87 | def __init__(self, string, size, **args): |
88 | @@ -204,6 +242,8 @@ |
89 | class text(_column): |
90 | _type = 'text' |
91 | |
92 | + _pg_type = ('text', 'text') |
93 | + |
94 | import __builtin__ |
95 | |
96 | class float(_column): |
97 | @@ -213,6 +253,12 @@ |
98 | _symbol_set = (_symbol_c, _symbol_f) |
99 | _symbol_get = lambda self,x: x or 0.0 |
100 | |
101 | + @classmethod |
102 | + def pg_type_for(cls, field): |
103 | + if field.digits: |
104 | + return 'numeric', 'numeric' |
105 | + return 'float8', 'double precision' |
106 | + |
107 | def __init__(self, string='unknown', digits=None, digits_compute=None, required=False, **args): |
108 | _column.__init__(self, string=string, required=required, **args) |
109 | self.digits = digits |
110 | @@ -236,6 +282,8 @@ |
111 | class date(_column): |
112 | _type = 'date' |
113 | |
114 | + _pg_type = ('date', 'date') |
115 | + |
116 | @staticmethod |
117 | def today(*args): |
118 | """ Returns the current date in a format fit for being a |
119 | @@ -281,6 +329,9 @@ |
120 | |
121 | class datetime(_column): |
122 | _type = 'datetime' |
123 | + |
124 | + _pg_type = ('timestamp', 'timestamp') |
125 | + |
126 | @staticmethod |
127 | def now(*args): |
128 | """ Returns the current datetime in a format fit for being a |
129 | @@ -326,6 +377,8 @@ |
130 | _type = 'binary' |
131 | _symbol_c = '%s' |
132 | |
133 | + _pg_type = ('bytea', 'bytea') |
134 | + |
135 | # Binary values may be byte strings (python 2.6 byte array), but |
136 | # the legacy OpenERP convention is to transfer and store binaries |
137 | # as base64-encoded strings. The base64 string may be provided as a |
138 | @@ -368,9 +421,17 @@ |
139 | res[i] = val |
140 | return res |
141 | |
142 | -class selection(_column): |
143 | +class selection(_varchar): |
144 | _type = 'selection' |
145 | |
146 | + @classmethod |
147 | + def pg_type_for(cls, field): |
148 | + if (isinstance(field.selection, list) and isinstance(field.selection[0][0], int))\ |
149 | + or getattr(field, 'size', None) == -1: |
150 | + return 'int4', 'int4' |
151 | + |
152 | + return super(selection, cls).pg_type_for(field) |
153 | + |
154 | def __init__(self, selection, string='unknown', **args): |
155 | _column.__init__(self, string=string, **args) |
156 | self.selection = selection |
157 | @@ -396,6 +457,8 @@ |
158 | _symbol_f = lambda x: x or None |
159 | _symbol_set = (_symbol_c, _symbol_f) |
160 | |
161 | + _pg_type = ('int4', 'int4') |
162 | + |
163 | def __init__(self, obj, string='unknown', **args): |
164 | _column.__init__(self, string=string, **args) |
165 | self._obj = obj |
166 | @@ -1078,6 +1141,12 @@ |
167 | if self._fnct_inv: |
168 | self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context) |
169 | |
170 | + @classmethod |
171 | + def pg_type_for(cls, field): |
172 | + if field._type == 'selection': |
173 | + return 'varchar', 'varchar' |
174 | + return globals()[field._type].pg_type_for(field) |
175 | + |
176 | # --------------------------------------------------------- |
177 | # Related fields |
178 | # --------------------------------------------------------- |
179 | @@ -1303,7 +1372,7 @@ |
180 | |
181 | def __init__(self, serialization_field, **kwargs): |
182 | self.serialization_field = serialization_field |
183 | - return super(sparse, self).__init__(self._fnct_read, fnct_inv=self._fnct_write, multi='__sparse_multi', **kwargs) |
184 | + super(sparse, self).__init__(self._fnct_read, fnct_inv=self._fnct_write, multi='__sparse_multi', **kwargs) |
185 | |
186 | |
187 | |
188 | @@ -1350,6 +1419,8 @@ |
189 | _symbol_set = (_symbol_c, _symbol_f) |
190 | _symbol_get = _symbol_get_struct |
191 | |
192 | + _pg_type = ('text', 'text') |
193 | + |
194 | # TODO: review completly this class for speed improvement |
195 | class property(function): |
196 | |
197 | |
198 | === modified file 'openerp/osv/orm.py' |
199 | --- openerp/osv/orm.py 2012-06-06 14:19:08 +0000 |
200 | +++ openerp/osv/orm.py 2012-06-07 12:55:24 +0000 |
201 | @@ -528,71 +528,6 @@ |
202 | self._cache[model].clear() |
203 | self._cache[model].update(cached_ids) |
204 | |
205 | -def pg_varchar(size=0): |
206 | - """ Returns the VARCHAR declaration for the provided size: |
207 | - |
208 | - * If no size (or an empty or negative size is provided) return an |
209 | - 'infinite' VARCHAR |
210 | - * Otherwise return a VARCHAR(n) |
211 | - |
212 | - :type int size: varchar size, optional |
213 | - :rtype: str |
214 | - """ |
215 | - if size: |
216 | - if not isinstance(size, int): |
217 | - raise TypeError("VARCHAR parameter should be an int, got %s" |
218 | - % type(size)) |
219 | - if size > 0: |
220 | - return 'VARCHAR(%d)' % size |
221 | - return 'VARCHAR' |
222 | - |
223 | -FIELDS_TO_PGTYPES = { |
224 | - fields.boolean: 'bool', |
225 | - fields.integer: 'int4', |
226 | - fields.text: 'text', |
227 | - fields.date: 'date', |
228 | - fields.datetime: 'timestamp', |
229 | - fields.binary: 'bytea', |
230 | - fields.many2one: 'int4', |
231 | - fields.serialized: 'text', |
232 | -} |
233 | - |
234 | -def get_pg_type(f, type_override=None): |
235 | - """ |
236 | - :param fields._column f: field to get a Postgres type for |
237 | - :param type type_override: use the provided type for dispatching instead of the field's own type |
238 | - :returns: (postgres_identification_type, postgres_type_specification) |
239 | - :rtype: (str, str) |
240 | - """ |
241 | - field_type = type_override or type(f) |
242 | - |
243 | - if field_type in FIELDS_TO_PGTYPES: |
244 | - pg_type = (FIELDS_TO_PGTYPES[field_type], FIELDS_TO_PGTYPES[field_type]) |
245 | - elif issubclass(field_type, fields.float): |
246 | - if f.digits: |
247 | - pg_type = ('numeric', 'NUMERIC') |
248 | - else: |
249 | - pg_type = ('float8', 'DOUBLE PRECISION') |
250 | - elif issubclass(field_type, (fields.char, fields.reference)): |
251 | - pg_type = ('varchar', pg_varchar(f.size)) |
252 | - elif issubclass(field_type, fields.selection): |
253 | - if (isinstance(f.selection, list) and isinstance(f.selection[0][0], int))\ |
254 | - or getattr(f, 'size', None) == -1: |
255 | - pg_type = ('int4', 'INTEGER') |
256 | - else: |
257 | - pg_type = ('varchar', pg_varchar(getattr(f, 'size', None))) |
258 | - elif issubclass(field_type, fields.function): |
259 | - if f._type == 'selection': |
260 | - pg_type = ('varchar', pg_varchar()) |
261 | - else: |
262 | - pg_type = get_pg_type(f, getattr(fields, f._type)) |
263 | - else: |
264 | - _logger.warning('%s type not supported!', field_type) |
265 | - pg_type = None |
266 | - |
267 | - return pg_type |
268 | - |
269 | - |
270 | class MetaModel(type): |
271 | """ Metaclass for the Model. |
272 | |
273 | @@ -2906,23 +2841,23 @@ |
274 | self._table, k) |
275 | f_obj_type = None |
276 | else: |
277 | - f_obj_type = get_pg_type(f) and get_pg_type(f)[0] |
278 | + f_obj_type = f.pg_type and f.pg_type[0] |
279 | |
280 | if f_obj_type: |
281 | ok = False |
282 | casts = [ |
283 | - ('text', 'char', pg_varchar(f.size), '::%s' % pg_varchar(f.size)), |
284 | + ('text', 'char', f.pg_type[1], '::%s' % f.pg_type[1]), |
285 | ('varchar', 'text', 'TEXT', ''), |
286 | - ('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), |
287 | + ('int4', 'float', f.pg_type[1], '::'+f.pg_type[1]), |
288 | ('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'), |
289 | ('timestamp', 'date', 'date', '::date'), |
290 | - ('numeric', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), |
291 | - ('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), |
292 | + ('numeric', 'float', f.pg_type[1], '::'+f.pg_type[1]), |
293 | + ('float8', 'float', f.pg_type[1], '::'+f.pg_type[1]), |
294 | ] |
295 | if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size < f.size: |
296 | cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) |
297 | - cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size))) |
298 | - cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size))) |
299 | + cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, f.pg_type[1])) |
300 | + cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, f.pg_type[1])) |
301 | cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,)) |
302 | cr.commit() |
303 | _schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s", |
304 | @@ -2955,7 +2890,7 @@ |
305 | if f_pg_notnull: |
306 | cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k)) |
307 | cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"' % (self._table, k, newname)) |
308 | - cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1])) |
309 | + cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, f.pg_type[1])) |
310 | cr.execute("COMMENT ON COLUMN %s.\"%s\" IS %%s" % (self._table, k), (f.string,)) |
311 | _schema.debug("Table '%s': column '%s' has changed type (DB=%s, def=%s), data moved to column %s !", |
312 | self._table, k, f_pg_type, f._type, newname) |
313 | @@ -3020,10 +2955,10 @@ |
314 | else: |
315 | if not isinstance(f, fields.function) or f.store: |
316 | # add the missing field |
317 | - cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1])) |
318 | + cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, f.pg_type[1])) |
319 | cr.execute("COMMENT ON COLUMN %s.\"%s\" IS %%s" % (self._table, k), (f.string,)) |
320 | _schema.debug("Table '%s': added column '%s' with definition=%s", |
321 | - self._table, k, get_pg_type(f)[1]) |
322 | + self._table, k, f.pg_type[1]) |
323 | |
324 | # initialize it |
325 | if not create and k in self._defaults: |