Search code examples
pythonpython-3.xodooattachmentodoo-11

How to create new attachments in Odoo? How do they work?


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?


Solution

  • Those fields should be filled automatically:

    • The store_fnameis the folder and the name that the file uses when it is store in the filestore folder
    • The checksum concides with the file name as well. It is the result of applying the sha1 algorythm to the file data

    An 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