Allow to store empty files in Binary fields

Closes #14476
This commit is contained in:
Nicolas Évrard
2026-01-08 01:06:16 +01:00
parent 20f5089837
commit ec11c9e83b
8 changed files with 40 additions and 21 deletions
+1 -1
View File
@@ -2863,7 +2863,7 @@
return previous != value;
},
get_size: function(record) {
var data = record._values[this.name] || 0;
var data = record._values[this.name];
if ((data instanceof Uint8Array) ||
(typeof(data) == 'string')) {
return data.length;
+8 -3
View File
@@ -4768,17 +4768,22 @@ function eval_pyson(value){
} else {
size = field.get(record).length;
}
this.size.val(Sao.common.humanize(size, 'B'));
let is_empty = ((size === undefined) || (size === null));
if (!is_empty) {
this.size.val(Sao.common.humanize(size, 'B'));
} else {
this.size.val('');
}
if (this.text) {
this.text.val(this.filename_field.get(record) || '');
if (size) {
if (!is_empty) {
this.but_open.parent().sao_show();
} else {
this.but_open.parent().sao_hide();
}
}
this.update_buttons(Boolean(size));
this.update_buttons(!is_empty);
},
key_press: function(evt) {
var editable = !this.text.prop('readonly');
+2 -1
View File
@@ -3086,7 +3086,8 @@
} else {
size = this.field.get(record).length;
}
return size? Sao.common.humanize(size, 'B') : '';
let file_exist = !((size === undefined) || (size === null));
return file_exist ? Sao.common.humanize(size, 'B') : '';
},
update_text: function(cell, record) {
var text = this.get_textual_value(record);
@@ -1140,13 +1140,13 @@ class BinaryField(Field):
return self.get(record)
def set_client(self, record, value, force_change=False):
self._set_file_cache(record, value or b'')
self._set_file_cache(record, value)
self.sig_changed(record)
record.validate(softvalidation=True)
record.set_modified(self.name)
def get_size(self, record):
result = record.value.get(self.name) or 0
result = record.value.get(self.name)
if isinstance(result, _FileCache):
result = os.stat(result.path).st_size
elif isinstance(result, (str, bytes)):
@@ -233,12 +233,13 @@ class Binary(BinaryMixin, Widget):
size = self.field.get_size(self.record)
else:
size = len(self.field.get(self.record))
self.wid_size.set_text(common.humanize(size or 0, 'B'))
self.wid_size.set_text(
common.humanize(size, 'B') if size is not None else '')
reset_position(self.wid_size)
if self.wid_text:
self.wid_text.set_text(self.filename_field.get(self.record) or '')
reset_position(self.wid_text)
if size:
if size is not None:
icon, tooltip = 'tryton-open', _("Open...")
else:
icon, tooltip = None, ''
@@ -250,7 +251,7 @@ class Binary(BinaryMixin, Widget):
pixbuf = None
self.wid_text.set_icon_from_pixbuf(pos, pixbuf)
self.wid_text.set_icon_tooltip_text(pos, tooltip)
self.update_buttons(bool(size))
self.update_buttons(size is not None)
return True
def set_value(self):
@@ -604,7 +604,7 @@ class Binary(GenericText):
size = field.get_size(record)
else:
size = len(field.get(record))
return common.humanize(size, 'B') if size else ''
return common.humanize(size, 'B') if size is not None else ''
def value_from_text(self, record, text, callback=None):
if callback:
@@ -694,7 +694,7 @@ class _BinarySave(_BinaryIcon):
size = len(field.get(record))
field.state_set(record, states=['invisible'])
invisible = field.get_state_attrs(record).get('invisible', False)
cell.set_property('visible', not invisible and size)
cell.set_property('visible', not invisible and size is not None)
self._set_visual(cell, record)
@@ -763,7 +763,7 @@ class _BinarySelect(_BinaryIcon):
invisible = field.get_state_attrs(record).get('invisible', False)
readonly = self.attrs.get('readonly',
field.get_state_attrs(record).get('readonly', False))
if readonly or size:
if readonly or size is not None:
cell.set_property('visible', False)
else:
cell.set_property('visible', not invisible)
+6 -4
View File
@@ -70,7 +70,6 @@ class Binary(Field):
'%s.%s' % (model.__name__, name), '')
if format_ == 'size':
converter = len
default = 0
result = defaultdict(type(default))
if self.file_id:
@@ -104,7 +103,7 @@ class Binary(Field):
if i['id'] in result:
continue
value = i[name]
if value:
if value is not None:
value = converter(value)
else:
value = default
@@ -152,9 +151,12 @@ class Binary(Field):
for ids, value in zip(args, args):
self.queue_for_removal(Model, name, ids)
if self.file_id:
if value is not None:
file_name = filestore.set(value, prefix)
else:
file_name = None
columns = [Column(table, self.file_id), Column(table, name)]
values = [
filestore.set(value, prefix) if value else None, None]
values = [file_name, None]
else:
columns = [Column(table, name)]
values = [self.sql_format(value)]
+14 -4
View File
@@ -90,10 +90,9 @@ class FieldBinaryTestCase(TestCase):
"Test create binary with empty"
Binary = Pool().get('test.binary_required')
with self.assertRaises(RequiredValidationError):
binary, = Binary.create([{
'binary': cast(b''),
}])
binary, = Binary.create([{
'binary': cast(b''),
}])
@with_transaction()
def test_create_required_with_invalid_sql_constraint(self):
@@ -156,6 +155,17 @@ class FieldBinaryTestCase(TestCase):
with self.assertRaises(ValueError):
binary.binary = Literal('foo')
@with_transaction()
def test_read_empty(self):
"Test reading an empty binary field"
Binary = Pool().get('test.binary')
binary, = Binary.create([{
'binary': cast(b''),
}])
self.assertEqual(binary.binary, b'')
@with_transaction()
def test_read_size(self):
"Test read binary size"