1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
# 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 Column, Null
from trytond.filestore import filestore
from trytond.tools import cached_property, grouped_slice, reduce_ids
from trytond.transaction import Transaction
from .field import Field
def caster(d):
if isinstance(d, bytes):
return d
elif isinstance(d, memoryview):
return bytes(d)
return bytes(d, encoding='utf8')
class Binary(Field):
'''
Define a binary field (``bytes``).
'''
_type = 'binary'
_sql_type = 'BLOB'
cast = staticmethod(caster)
def __init__(self, string='', help='', required=False, readonly=False,
domain=None, states=None, on_change=None,
on_change_with=None, depends=None, context=None, loading='lazy',
filename=None, file_id=None, store_prefix=None):
self.filename = filename
self.file_id = file_id
self.store_prefix = store_prefix
super(Binary, self).__init__(string=string, help=help,
required=required, readonly=readonly, domain=domain, states=states,
on_change=on_change, on_change_with=on_change_with,
depends=depends, context=context, loading=loading)
@cached_property
def display_depends(self):
depends = super().display_depends
if self.filename:
depends.add(self.filename)
return depends
def get(self, ids, model, name, values=None):
'''
Convert the binary value into ``bytes``
:param ids: a list of ids
:param model: a string with the name of the model
:param name: a string with the name of the field
:param values: a dictionary with the read values
:return: a dictionary with ids as key and values as value
'''
if values is None:
values = {}
transaction = Transaction()
res = {}
converter = self.cast
default = None
format_ = Transaction().context.get(
'%s.%s' % (model.__name__, name), '')
if format_ == 'size':
converter = len
default = 0
if self.file_id:
table = model.__table__()
cursor = transaction.connection.cursor()
prefix = self.store_prefix
if prefix is None:
prefix = transaction.database.name
if format_ == 'size':
store_func = filestore.size
else:
def store_func(id, prefix):
return self.cast(filestore.get(id, prefix=prefix))
for sub_ids in grouped_slice(ids):
cursor.execute(*table.select(
table.id, Column(table, self.file_id),
where=reduce_ids(table.id, sub_ids)
& (Column(table, self.file_id) != Null)
& (Column(table, self.file_id) != '')))
for record_id, file_id in cursor:
try:
res[record_id] = store_func(file_id, prefix)
except (IOError, OSError):
pass
for i in values:
if i['id'] in res:
continue
value = i[name]
if value:
value = converter(value)
else:
value = default
res[i['id']] = value
for i in ids:
res.setdefault(i, default)
return res
def set(self, Model, name, ids, value, *args):
transaction = Transaction()
table = Model.__table__()
cursor = transaction.connection.cursor()
prefix = self.store_prefix
if prefix is None:
prefix = transaction.database.name
args = iter((ids, value) + args)
for ids, value in zip(args, args):
if self.file_id:
columns = [Column(table, self.file_id), Column(table, name)]
values = [
filestore.set(value, prefix) if value else None, None]
else:
columns = [Column(table, name)]
values = [self.sql_format(value)]
cursor.execute(*table.update(columns, values,
where=reduce_ids(table.id, ids)))
def definition(self, model, language):
definition = super().definition(model, language)
definition['searchable'] = False
definition['filename'] = self.filename
return definition
|