Move low level handling of the SQL type to the backend

This changeset defines on the field the (somehow) abstract SQL type a field
should use letting the duty of specifying the real SQL type the database
supports and the conversion to/from the python types to the backend.

issue6351
review33951002
This commit is contained in:
Nicolas Évrard
2017-03-31 12:44:22 +02:00
parent 95e000c9e8
commit 5cee718423
23 changed files with 219 additions and 215 deletions
+1
View File
@@ -1,3 +1,4 @@
* Use generic SQL type in field and let backend determine the SQLType
* Add filter to xxx2Many fields
* Add NULLS ordering
* Add context domain on ir.action.act_window
+6
View File
@@ -174,6 +174,12 @@ Instance methods:
use for creation and casting. Or `None` if the field is not stored in the
database.
sql_type is using the `_sql_type` attribute to compute its return value.
The backend is responsible for the computation.
For the list of supported types by Tryton see
:ref:`backend types <topics-backend_types>`.
.. method:: Field.sql_column(table)
Return the Column instance based on table.
+68
View File
@@ -0,0 +1,68 @@
.. _topics-backend_types:
Backend Types supported
=======================
This table give a comprehensive list of the SQL Types that are expected to be
supported by the database backends. If the type is not supported then the
backend will have to emulate the behavior described here.
The columns are in the following order:
* The SQL type [#]_ representing the field
* The python type expected on input
* The python type received on output
.. [#] Corresponding to the `SQL 92`_ standard or to a `PostgreSQL type`_.
.. _`SQL 92`: http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
.. _`PostgreSQL type`: https://www.postgresql.org/docs/current/static/datatype.html
`None` will represent the `NULL` value and vice versa, it can be used as input
or output for any SQL type.
+--------------------+----------------------+----------------------+
| SQL Type | Python input type | Python output type |
+====================+======================+======================+
| `BOOL` | bool | bool |
+--------------------+----------------------+----------------------+
| `INTEGER` | int | int |
+--------------------+----------------------+----------------------+
| `BIGINT` | int / long | int / long |
| | [#pyver_int]_ | [#pyver_int]_ |
+--------------------+----------------------+----------------------+
| `FLOAT` | float / int / long | float |
| | [#pyver_int]_ | |
+--------------------+----------------------+----------------------+
| `NUMERIC` | decimal.Decimal_ | decimal.Decimal_ |
+--------------------+----------------------+----------------------+
| `VARCHAR` / | str / unicode | str / unicode |
| `VARCHAR(length)` | [#pyver_str]_ | [#pyver_str]_ |
+--------------------+----------------------+----------------------+
| `TEXT` | str / unicode | str / unicode |
| | [#pyver_str]_ | [#pyver_str]_ |
+--------------------+----------------------+----------------------+
| `TIMESTAMP` | datetime.datetime_ | datetime.datetime_ |
+--------------------+----------------------+----------------------+
| `DATETIME` | datetime.datetime_ | datetime.datetime_ |
| | without microseconds | without microseconds |
| | [#utc_tz]_ | [#utc_tz]_ |
+--------------------+----------------------+----------------------+
| `DATE` | datetime.date_ | datetime.date_ |
+--------------------+----------------------+----------------------+
| `TIME` | datetime.time_ | datetime.time_ |
+--------------------+----------------------+----------------------+
| `INTERVAL` | datetime.timedelta_ | datetime.timedelta_ |
+--------------------+----------------------+----------------------+
| `BLOB` | bytes | bytes |
+--------------------+----------------------+----------------------+
.. [#pyver_int] in python 2 integers over *sys.maxint* are represented by the
`long` type
.. [#pyver_str] str when using python 3 ; unicode when using python 2
.. [#utc_tz] Datetime objects are not localized to any timezone
.. _datetime.date: https://docs.python.org/library/datetime.html#date-objects
.. _datetime.datetime: https://docs.python.org/library/datetime.html#datetime-objects
.. _datetime.time: https://docs.python.org/library/datetime.html#time-objects
.. _datetime.timedelta: https://docs.python.org/library/datetime.html#timedelta-objects
.. _decimal.Decimal: https://docs.python.org/library/decimal.html#decimal-objects
+1
View File
@@ -15,6 +15,7 @@ Introduction to all the key parts of trytond:
logs
start_server
models/index
backend-types
models/fields_default_value
models/fields_on_change
domain
+12
View File
@@ -1,8 +1,12 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import namedtuple
DatabaseIntegrityError = None
DatabaseOperationalError = None
SQLType = namedtuple('SQLType', 'base type')
class DatabaseInterface(object):
'''
@@ -159,3 +163,11 @@ class DatabaseInterface(object):
def has_window_functions(self):
"Return if database supports window functions."
return False
def sql_type(self, type_):
'Return the SQLType tuple corresponding to the SQL type'
pass
def sql_format(self, type_, value):
'Return value correctly casted into type_'
pass
+23 -1
View File
@@ -1,6 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.backend.database import DatabaseInterface
from trytond.backend.database import DatabaseInterface, SQLType
from trytond.config import config, parse_uri
import MySQLdb
import MySQLdb.cursors
@@ -79,6 +79,16 @@ class Database(DatabaseInterface):
max_limit=18446744073709551610, function_mapping=MAPPING,
null_ordering=False)
TYPES_MAPPING = {
'BLOB': SQLType('LONGBLOB', 'LONGBLOB'),
'DATETIME': SQLType('TIMESTAMP', 'TIMESTAMP NULL'),
'TIMESTAMP': SQLType('TIMESTAMP', 'TIMESTAMP NULL'),
'FLOAT': SQLType('DOUBLE', 'DOUBLE(255, 15)'),
'INTEGER': SQLType('SIGNED INTEGER', 'BIGINT'),
'BIGINTEGER': SQLType('SIGNED INTEGER', 'BIGINT'),
'NUMERIC': SQLType('DECIMAL', 'DECIMAL(65, 30)'),
}
def connect(self):
return self
@@ -224,3 +234,15 @@ class Database(DatabaseInterface):
cursor = connection.cursor()
cursor.execute('ALTER TABLE `%s` AUTO_INCREMENT = %%s' % table,
(value,))
def sql_type(self, type_):
if type_ in self.TYPES_MAPPING:
return self.TYPES_MAPPING[type_]
if type_.startswith('VARCHAR'):
return SQLType('CHAR', 'VARCHAR(255)')
return SQLType(type_, type_)
def sql_format(self, type_, value):
if type_ == 'INTERVAL':
return value.total_seconds()
return value
+21 -2
View File
@@ -13,7 +13,7 @@ try:
compat.register()
except ImportError:
pass
from psycopg2 import connect
from psycopg2 import connect, Binary
from psycopg2.pool import ThreadedConnectionPool, PoolError
from psycopg2.extensions import cursor
from psycopg2.extensions import ISOLATION_LEVEL_REPEATABLE_READ
@@ -29,7 +29,7 @@ from psycopg2 import OperationalError as DatabaseOperationalError
from sql import Flavor
from trytond.backend.database import DatabaseInterface
from trytond.backend.database import DatabaseInterface, SQLType
from trytond.config import config, parse_uri
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
@@ -73,6 +73,12 @@ class Database(DatabaseInterface):
_has_returning = None
flavor = Flavor(ilike=True)
TYPES_MAPPING = {
'BLOB': SQLType('BYTEA', 'BYTEA'),
'DATETIME': SQLType('TIMESTAMP', 'TIMESTAMP(0)'),
'TIMESTAMP': SQLType('TIMESTAMP', 'TIMESTAMP(6)'),
}
def __new__(cls, name='template1'):
with cls._lock:
if name in cls._databases:
@@ -307,6 +313,19 @@ class Database(DatabaseInterface):
def has_window_functions(self):
return True
def sql_type(self, type_):
if type_ in self.TYPES_MAPPING:
return self.TYPES_MAPPING[type_]
if type_.startswith('VARCHAR'):
return SQLType('VARCHAR', type_)
return SQLType(type_, type_)
def sql_format(self, type_, value):
if type_ == 'BLOB':
if value is not None:
return Binary(value)
return value
register_type(UNICODE)
if PYDATE:
register_type(PYDATE)
+21 -3
View File
@@ -1,6 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.backend.database import DatabaseInterface
from trytond.backend.database import DatabaseInterface, SQLType
from trytond.config import config
import os
from decimal import Decimal
@@ -21,12 +21,11 @@ except ImportError:
import sqlite3 as sqlite
from sqlite3 import IntegrityError as DatabaseIntegrityError
from sqlite3 import OperationalError as DatabaseOperationalError
from sql import Flavor, Table
from sql import Flavor, Table, Query, Expression
from sql.functions import (Function, Extract, Position, Substring,
Overlay, CharLength, CurrentTimestamp, Trim)
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
logger = logging.getLogger(__name__)
@@ -225,6 +224,11 @@ class Database(DatabaseInterface):
paramstyle='qmark', function_mapping=MAPPING, null_ordering=False)
IN_MAX = 200
TYPES_MAPPING = {
'DATETIME': SQLType('TIMESTAMP', 'TIMESTAMP'),
'BIGINT': SQLType('INTEGER', 'INTEGER'),
}
def __new__(cls, name=':memory:'):
if (name == ':memory:'
and getattr(cls._local, 'memory_database', None)):
@@ -402,6 +406,20 @@ class Database(DatabaseInterface):
def has_multirow_insert(self):
return True
def sql_type(self, type_):
if type_ in self.TYPES_MAPPING:
return self.TYPES_MAPPING[type_]
if type_.startswith('VARCHAR'):
return SQLType('VARCHAR', 'VARCHAR')
return SQLType(type_, type_)
def sql_format(self, type_, value):
if type_ in ('INTEGER', 'BIGINT'):
if (value is not None
and not isinstance(value, (Query, Expression))):
value = int(value)
return value
sqlite.register_converter('NUMERIC', lambda val: Decimal(val.decode('utf-8')))
if sys.version_info[0] == 2:
sqlite.register_adapter(Decimal, lambda val: buffer(str(val)))
+3 -21
View File
@@ -1,10 +1,9 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Query, Expression, Column, Null
from sql import Column, Null
from .field import Field, SQLType
from .field import Field
from ...transaction import Transaction
from ... import backend
from ...tools import grouped_slice, reduce_ids
from ...filestore import filestore
@@ -14,6 +13,7 @@ class Binary(Field):
Define a binary field (``bytes``).
'''
_type = 'binary'
_sql_type = 'BLOB'
cast = bytearray if bytes == str else bytes
def __init__(self, string='', help='', required=False, readonly=False,
@@ -116,21 +116,3 @@ class Binary(Field):
values = [self.sql_format(value)]
cursor.execute(*table.update(columns, values,
where=reduce_ids(table.id, ids)))
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
db_type = backend.name()
if db_type == 'postgresql' and value is not None:
import psycopg2
return psycopg2.Binary(value)
return value
def sql_type(self):
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('BYTEA', 'BYTEA')
elif db_type == 'mysql':
return SQLType('LONGBLOB', 'LONGBLOB')
return SQLType('BLOB', 'BLOB')
+2 -4
View File
@@ -1,7 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from .field import Field, SQLType
from .field import Field
class Boolean(Field):
@@ -9,6 +9,7 @@ class Boolean(Field):
Define a boolean field (``True`` or ``False``).
'''
_type = 'boolean'
_sql_type = 'BOOL'
def __init__(self, string='', help='', readonly=False, domain=None,
states=None, select=False, on_change=None, on_change_with=None,
@@ -20,9 +21,6 @@ class Boolean(Field):
__init__.__doc__ = Field.__init__.__doc__
def sql_type(self):
return SQLType('BOOL', 'BOOL')
def _domain_add_null(self, column, operator, value, expression):
expression = super(Boolean, self)._domain_add_null(
column, operator, value, expression)
+5 -15
View File
@@ -3,10 +3,7 @@
import sys
import warnings
from sql import Query, Expression
from ... import backend
from .field import Field, FieldTranslate, size_validate, SQLType
from .field import Field, FieldTranslate, size_validate
from ...rpc import RPC
@@ -49,10 +46,7 @@ class Char(FieldTranslate):
size = property(_get_size, _set_size)
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
elif isinstance(value, str) and sys.version_info < (3,):
@@ -60,13 +54,9 @@ class Char(FieldTranslate):
assert isinstance(value, unicode)
return value
def sql_type(self):
db_type = backend.name()
if self.size and db_type != 'sqlite':
return SQLType('VARCHAR', 'VARCHAR(%s)' % self.size)
elif db_type == 'mysql':
return SQLType('CHAR', 'VARCHAR(255)')
return SQLType('VARCHAR', 'VARCHAR')
@property
def _sql_type(self):
return 'VARCHAR(%s)' % self.size if self.size else 'VARCHAR'
def set_rpc(self, model):
super(Char, self).set_rpc(model)
+12 -55
View File
@@ -1,10 +1,8 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime
from sql import Query, Expression
from ... import backend
from .field import Field, SQLType
from .field import Field
class Date(Field):
@@ -12,11 +10,9 @@ class Date(Field):
Define a date field (``date``).
'''
_type = 'date'
_sql_type = 'DATE'
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
if isinstance(value, basestring):
@@ -31,15 +27,13 @@ class Date(Field):
or value.time() == datetime.time())
return value
def sql_type(self):
return SQLType('DATE', 'DATE')
class DateTime(Field):
'''
Define a datetime field (``datetime``).
'''
_type = 'datetime'
_sql_type = 'DATETIME'
def __init__(self, string='', format='%H:%M:%S', help='', required=False,
readonly=False, domain=None, states=None, select=False,
@@ -56,10 +50,7 @@ class DateTime(Field):
__init__.__doc__ += Field.__init__.__doc__
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if not value:
return None
if isinstance(value, basestring):
@@ -70,25 +61,15 @@ class DateTime(Field):
assert(isinstance(value, datetime.datetime))
return value.replace(microsecond=0)
def sql_type(self):
db_type = backend.name()
if db_type == 'sqlite':
return SQLType('TIMESTAMP', 'TIMESTAMP')
elif db_type == 'mysql':
return SQLType('TIMESTAMP', 'TIMESTAMP NULL')
return SQLType('TIMESTAMP', 'TIMESTAMP(0)')
class Timestamp(Field):
'''
Define a timestamp field (``datetime``).
'''
_type = 'timestamp'
_sql_type = 'TIMESTAMP'
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
if isinstance(value, basestring):
@@ -105,25 +86,15 @@ class Timestamp(Field):
assert(isinstance(value, datetime.datetime))
return value
def sql_type(self):
db_type = backend.name()
if db_type == 'sqlite':
return SQLType('TIMESTAMP', 'TIMESTAMP')
elif db_type == 'mysql':
return SQLType('TIMESTAMP', 'TIMESTAMP NULL')
return SQLType('TIMESTAMP', 'TIMESTAMP(6)')
class Time(DateTime):
'''
Define a time field (``time``).
'''
_type = 'time'
_sql_type = 'TIME'
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
if isinstance(value, basestring):
@@ -132,15 +103,13 @@ class Time(DateTime):
assert(isinstance(value, datetime.time))
return value.replace(microsecond=0)
def sql_type(self):
return SQLType('TIME', 'TIME')
class TimeDelta(Field):
'''
Define a timedelta field (``timedelta``).
'''
_type = 'timedelta'
_sql_type = 'INTERVAL'
def __init__(self, string='', converter=None, help='', required=False,
readonly=False, domain=None, states=None, select=False,
@@ -156,23 +125,11 @@ class TimeDelta(Field):
depends=depends, context=context, loading=loading)
self.converter = converter
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
assert(isinstance(value, datetime.timedelta))
db_type = backend.name()
if db_type == 'mysql':
return value.total_seconds()
return value
def sql_type(self):
db_type = backend.name()
if db_type == 'mysql':
return SQLType('DOUBLE', 'DOUBLE(255, 6)')
return SQLType('INTERVAL', 'INTERVAL')
return super(TimeDelta, self).sql_format(value)
@classmethod
def get(cls, ids, model, name, values=None):
+3 -9
View File
@@ -1,9 +1,8 @@
# This file is part of Tryton. The COPYRIGHT file at the toplevel of this
# repository contains the full copyright notices and license terms.
import json
from sql import Query, Expression
from .field import Field, SQLType
from .field import Field
from ...protocols.jsonrpc import JSONDecoder, JSONEncoder
from ...pool import Pool
from ...tools import grouped_slice
@@ -12,6 +11,7 @@ from ...tools import grouped_slice
class Dict(Field):
'Define dict field.'
_type = 'dict'
_sql_type = 'TEXT'
def __init__(self, schema_model, string='', help='', required=False,
readonly=False, domain=None, states=None, select=False,
@@ -30,18 +30,12 @@ class Dict(Field):
object_hook=JSONDecoder())
return dicts
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
assert isinstance(value, dict)
return json.dumps(value, cls=JSONEncoder, separators=(',', ':'))
def sql_type(self):
return SQLType('TEXT', 'TEXT')
def translated(self, name=None, type_='values'):
"Return a descriptor for the translated value of the field"
if name is None:
+15 -8
View File
@@ -1,13 +1,14 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from collections import namedtuple
import warnings
from functools import wraps
from sql import operators, Column, Literal, Select, CombiningQuery, Null
from sql import (operators, Column, Literal, Select, CombiningQuery, Null,
Query, Expression)
from sql.conditionals import Coalesce, NullIf
from sql.operators import Concat
from trytond import backend
from trytond.pyson import PYSON, PYSONEncoder, Eval
from trytond.const import OPERATORS
from trytond.transaction import Transaction
@@ -16,6 +17,8 @@ from trytond.cache import LRUDictTransaction
from ...rpc import RPC
Database = backend.get('Database')
def domain_validate(value):
assert isinstance(value, list), 'domain must be a list'
@@ -161,6 +164,7 @@ SQL_OPERATORS = {
class Field(object):
_type = None
_sql_type = None
def __init__(self, string='', help='', required=False, readonly=False,
domain=None, states=None, select=False, on_change=None,
@@ -272,12 +276,17 @@ class Field(object):
inst._values = {}
inst._values[self.name] = value
@staticmethod
def sql_format(value):
return value
def sql_format(self, value):
if isinstance(value, (Query, Expression)):
return value
assert self._sql_type is not None
database = Transaction().database
return database.sql_format(self._sql_type, value)
def sql_type(self):
return None
database = Transaction().database
return database.sql_type(self._sql_type)
def sql_column(self, table):
return Column(table, self.name)
@@ -438,5 +447,3 @@ class FieldTranslate(Field):
return [Coalesce(NullIf(translation.value, ''),
self.sql_column(table))]
SQLType = namedtuple('SQLType', 'base type')
+3 -17
View File
@@ -1,9 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Query, Expression
from ... import backend
from .field import Field, SQLType
from .field import Field
from ...pyson import PYSON
@@ -23,6 +20,7 @@ class Float(Field):
Define a float field (``float``).
'''
_type = 'float'
_sql_type = 'FLOAT'
def __init__(self, string='', digits=None, help='', required=False,
readonly=False, domain=None, states=None, select=False,
@@ -50,19 +48,7 @@ class Float(Field):
digits = property(_get_digits, _set_digits)
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
return float(value)
def sql_type(self):
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('FLOAT8', 'FLOAT8')
elif db_type == 'mysql':
return SQLType('DOUBLE', 'DOUBLE(255, 15)')
else:
return SQLType('FLOAT', 'FLOAT')
+3 -27
View File
@@ -1,9 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Query, Expression
from ... import backend
from .field import Field, SQLType
from .field import Field
class Integer(Field):
@@ -11,23 +8,7 @@ class Integer(Field):
Define an integer field (``int``).
'''
_type = 'integer'
def sql_type(self):
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('INT4', 'INT4')
elif db_type == 'mysql':
return SQLType('SIGNED INTEGER', 'BIGINT')
else:
return SQLType('INTEGER', 'INTEGER')
def sql_format(self, value):
db_type = backend.name()
if (db_type == 'sqlite'
and value is not None
and not isinstance(value, (Query, Expression))):
value = int(value)
return super(Integer, self).sql_format(value)
_sql_type = 'INTEGER'
class BigInteger(Integer):
@@ -35,9 +16,4 @@ class BigInteger(Integer):
Define an integer field (``long``).
'''
_type = 'biginteger'
def sql_type(self):
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('INT8', 'INT8')
return super(BigInteger, self).sql_type()
_sql_type = 'BIGINT'
+3
View File
@@ -79,6 +79,9 @@ class Many2Many(Field):
def add_remove(self):
return self.domain
def sql_type(self):
return None
def get(self, ids, model, name, values=None):
'''
Return target records ordered.
+4 -16
View File
@@ -1,11 +1,10 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Query, Expression, Literal
from sql import Literal
from sql.operators import Or
from .field import Field, SQLType
from .field import Field
from ...pool import Pool
from ... import backend
from ...tools import reduce_ids
from ...transaction import Transaction
@@ -15,6 +14,7 @@ class Many2One(Field):
Define many2one field (``int``).
'''
_type = 'many2one'
_sql_type = 'INTEGER'
def __init__(self, model_name, string='', left=None, right=None,
ondelete='SET NULL', datetime_field=None, target_search='join',
@@ -78,24 +78,12 @@ class Many2One(Field):
assert isinstance(value, (Target, type(None)))
super(Many2One, self).__set__(inst, value)
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
assert value is not False
return int(value)
def sql_type(self):
db_type = backend.name()
if db_type == 'postgresql':
return SQLType('INT4', 'INT4')
elif db_type == 'mysql':
return SQLType('SIGNED INTEGER', 'BIGINT')
else:
return SQLType('INTEGER', 'INTEGER')
def convert_domain_mptt(self, domain, tables):
cursor = Transaction().connection.cursor()
table, _ = tables[None]
+3 -12
View File
@@ -1,10 +1,9 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from sql import Query, Expression, Cast, Literal, Select, CombiningQuery, As
from sql import Cast, Literal, Select, CombiningQuery, As
from ... import backend
from .field import SQLType
from .float import Float
@@ -20,11 +19,9 @@ class Numeric(Float):
Define a numeric field (``decimal``).
'''
_type = 'numeric'
_sql_type = 'NUMERIC'
@staticmethod
def sql_format(value):
if isinstance(value, (Query, Expression)):
return value
def sql_format(self, value):
if value is None:
return None
if isinstance(value, (int, long)):
@@ -32,12 +29,6 @@ class Numeric(Float):
assert isinstance(value, Decimal)
return value
def sql_type(self):
db_type = backend.name()
if db_type == 'mysql':
return SQLType('DECIMAL', 'DECIMAL(65, 30)')
return SQLType('NUMERIC', 'NUMERIC')
def sql_column(self, table):
column = super(Numeric, self).sql_column(table)
db_type = backend.name()
+3
View File
@@ -76,6 +76,9 @@ class One2Many(Field):
size = property(_get_size, _set_size)
def sql_type(self):
return None
@property
def filter(self):
return self.__filter
+4 -12
View File
@@ -5,11 +5,9 @@ import warnings
from sql import Cast, Literal, Query, Expression
from sql.functions import Substring, Position
from .field import Field, SQLType
from .char import Char
from .field import Field
from ...transaction import Transaction
from ...pool import Pool
from ... import backend
from ...rpc import RPC
@@ -18,6 +16,7 @@ class Reference(Field):
Define a reference field (``str``).
'''
_type = 'reference'
_sql_type = 'VARCHAR'
def __init__(self, string='', selection=None, selection_change_with=None,
help='', required=False, readonly=False, domain=None, states=None,
@@ -122,20 +121,13 @@ class Reference(Field):
value = Target(value)
super(Reference, self).__set__(inst, value)
@staticmethod
def sql_format(value):
def sql_format(self, value):
if not isinstance(value, (basestring, Query, Expression)):
try:
value = '%s,%s' % tuple(value)
except TypeError:
pass
return Char.sql_format(value)
def sql_type(self):
db_type = backend.name()
if db_type == 'mysql':
return SQLType('CHAR', 'VARCHAR(255)')
return SQLType('VARCHAR', 'VARCHAR')
return super(Reference, self).sql_format(value)
def convert_domain(self, domain, tables, Model):
if '.' not in domain[0]:
+2 -8
View File
@@ -4,10 +4,9 @@ import warnings
from sql.conditionals import Case
from ... import backend
from ...transaction import Transaction
from ...tools import is_instance_method
from .field import Field, SQLType
from .field import Field
from ...rpc import RPC
@@ -16,6 +15,7 @@ class Selection(Field):
Define a selection field (``str``).
'''
_type = 'selection'
_sql_type = 'VARCHAR'
def __init__(self, selection, string='', sort=True,
selection_change_with=None, translate=True, help='',
@@ -55,12 +55,6 @@ class Selection(Field):
model.__rpc__.setdefault(
self.selection, RPC(instantiate=instantiate))
def sql_type(self):
db_type = backend.name()
if db_type == 'mysql':
return SQLType('CHAR', 'VARCHAR(255)')
return SQLType('VARCHAR', 'VARCHAR')
def convert_order(self, name, tables, Model):
if getattr(Model, 'order_%s' % name, None):
return super(Selection, self).convert_order(name, tables, Model)
+1 -5
View File
@@ -1,7 +1,5 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from .field import SQLType
from .char import Char
@@ -10,6 +8,4 @@ class Text(Char):
Define a text field (``unicode``).
'''
_type = 'text'
def sql_type(self):
return SQLType('TEXT', 'TEXT')
_sql_type = 'TEXT'