I want to create an attachment from python code.
So, what I have tried:
self.env['ir.attachment'].create({
'store_fname' : ??,
'checksum' : ??
})
What values should be passed for the column 'store_fname' and 'checksum' in ir_attachment table?
Those fields should be filled automatically:
store_fname
is the folder and the name that the file uses when it is store in the filestore folderchecksum
concides with the file name as well. It is the result of applying the sha1 algorythm to the file dataAn example in the database:
id | store_fname | checksum
-----+---------------------------------------------+------------------------------------------
1 | fc/fc78476ab1658bfedda7dde9b515d1c705472c1f | fc78476ab1658bfedda7dde9b515d1c705472c1f
2 | 97/97d5689a6bd71e33f9439f8235d54855a69134f3 | 97d5689a6bd71e33f9439f8235d54855a69134f3
348 | 54/549f82ae56b7397db7fcd8ca1a179494b0cfda03 | 549f82ae56b7397db7fcd8ca1a179494b0cfda03
Take a look at how they are computed:
@api.depends('store_fname', 'db_datas')
def _compute_datas(self):
bin_size = self._context.get('bin_size')
for attach in self:
if attach.store_fname:
attach.datas = self._file_read(attach.store_fname, bin_size)
else:
attach.datas = attach.db_datas
def _inverse_datas(self):
location = self._storage()
for attach in self:
# compute the fields that depend on datas
value = attach.datas
bin_data = base64.b64decode(value) if value else b''
vals = {
'file_size': len(bin_data),
'checksum': self._compute_checksum(bin_data),
'index_content': self._index(bin_data, attach.datas_fname, attach.mimetype),
'store_fname': False,
'db_datas': value,
}
if value and location != 'db':
# save it to the filestore
vals['store_fname'] = self._file_write(value, vals['checksum'])
vals['db_datas'] = False
# take current location in filestore to possibly garbage-collect it
fname = attach.store_fname
# write as superuser, as user probably does not have write access
super(IrAttachment, attach.sudo()).write(vals)
if fname:
self._file_delete(fname)
def _compute_checksum(self, bin_data):
""" compute the checksum for the given datas
:param bin_data : datas in its binary form
"""
# an empty file has a checksum too (for caching)
return hashlib.sha1(bin_data or b'').hexdigest()
# the field 'datas' is computed and may use the other fields below
datas = fields.Binary(
string='File Content',
compute='_compute_datas',
inverse='_inverse_datas'
)
An example of how to create an attachment taken from BaseImportImport:
@api.model
@api.returns('ir.attachment')
def _create_csv_attachment(self, fields, data, options, file_name):
# write csv
f = StringIO()
writer = csv.writer(f,
delimiter=str(options.get(OPT_SEPARATOR)),
quotechar=str(options.get(OPT_QUOTING)))
encoding = options.get(OPT_ENCODING, 'utf-8')
writer.writerow(fields)
for row in data:
writer.writerow(row)
# create attachment
datas = base64.encodebytes(f.getvalue().encode(encoding))
attachment = self.env['ir.attachment'].create({
'name': file_name,
'datas': datas,
'datas_fname': file_name
})
return attachment
Though the fields res_id
and res_model
can be useful as well if you want to link the attachments to some record in some model
id | store_fname | checksum | res_model | res_id
-----+---------------------------------------------+------------------------------------------+------------------+--------
1 | fc/fc78476ab1658bfedda7dde9b515d1c705472c1f | fc78476ab1658bfedda7dde9b515d1c705472c1f | res.country | 1
2 | 97/97d5689a6bd71e33f9439f8235d54855a69134f3 | 97d5689a6bd71e33f9439f8235d54855a69134f3 | res.country | 2
348 | 54/549f82ae56b7397db7fcd8ca1a179494b0cfda03 | 549f82ae56b7397db7fcd8ca1a179494b0cfda03 | ir.ui.menu | 77
3 | c5/c5fd52fe3cf431f70c6d778c555f027c97a0ac09 | c5fd52fe3cf431f70c6d778c555f027c97a0ac09 | res.country | 3