如何在网站中上传多个文件,并在Odoo 15中将其作为附件添加到新记录中?



我试图从Odoo网站上传多个文件,然后调用带有post方法的控制器来创建记录并保存附件。

这是我的模块student。py

ref = fields.Char(string="Ref")
first_name = fields.Char(string="Firstname")
last_name = fields.Char(string="Lastname")
task_attachment = fields.Many2many(comodel_name="ir.attachment",
relation="m2m_ir_identity_card_rel",
column1="m2m_id",
column2="attachment_id",
string="Identity Card")

这是我的表单和输入web_form。xml

<form role="form" action="/create/webstudent" enctype="multipart/form-data" method="POST">
<div class="form-group">
<label for="student_file" class="control-label">File Of Student :</label>
<input type="file" name="task_attachment" multiple="true"/>
</div>
<div class="clearfix oe_login_buttons">
<button type="submit" class="btn btn-primary pull-left">Submit</button>
</div>
</form>

这是控制器

@http.route('/create/webstudent', type="http", auth="user", website=True)
def create_webpatient(self, **kw):
if request.httprequest.method == 'POST':
new_task = request.env['university.student'].sudo().create(kw)
if 'task_attachment' in request.params:
attached_files = request.httprequest.files.getlist('task_attachment')
for attachment in attached_files:
attached_file = attachment.read()
request.env['ir.attachment'].sudo().create({
'name': attachment.filename,
'res_model': 'university.student',
'res_id': new_task.id,
'type': 'binary',
'datas_fname': attachment.filename,
'datas': attached_file.encode('base64'),
})
return request.render("om_university.student_thanks", {})

当我点击提交按钮时,出现了这个错误:

500: Internal Server Error
Traceback
Traceback (most recent call last):
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooaddonsbasemodelsir_http.py", line 237, in _dispatch
result = request.dispatch()
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 811, in dispatch
r = self._call_function(**self.params)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 360, in _call_function
return checked_call(self.db, *args, **kwargs)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooservicemodel.py", line 94, in wrapper
return f(dbname, *args, **kwargs)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 349, in checked_call
result = self.endpoint(*a, **kw)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 917, in __call__
return self.method(*args, **kw)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 536, in response_wrap
response = f(*args, **kw)
File "c:usersnacerdesktoplearning_odooodooworkspaceodoo- 
15custom_addonsom_universitycontrollerscontrollers.py", line 29, 
in create_webpatient
new_task = request.env['university.student'].sudo().create(kw)
File "<decorator-gen-131>", line 2, in create
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooapi.py", line 412, in _model_create_multi
return create(self, [arg])
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooaddonsmailmodelsmail_thread.py", line 265, in create
threads = super(MailThread, self).create(vals_list)
File "<decorator-gen-67>", line 2, in create
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooapi.py", line 413, in _model_create_multi
return create(self, arg)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooaddonsbasemodelsir_fields.py", line 613, in create
recs = super().create(vals_list)
File "<decorator-gen-13>", line 2, in create
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooapi.py", line 413, in _model_create_multi
return create(self, arg)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoomodels.py", line 4070, in create
records = self._create(data_list)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoomodels.py", line 4220, in _create
field.create([
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoofields.py", line 3283, in create
self.write_batch(record_values, True)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoofields.py", line 3304, in write_batch
raise ValueError("Wrong value for %s: %s" % (self, value))
Exception
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooaddonsbasemodelsir_http.py", line 237, in _dispatch
result = request.dispatch()
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 811, in dispatch
r = self._call_function(**self.params)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 360, in _call_function
return checked_call(self.db, *args, **kwargs)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooservicemodel.py", line 94, in wrapper
return f(dbname, *args, **kwargs)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 349, in checked_call
result = self.endpoint(*a, **kw)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 917, in __call__
return self.method(*args, **kw)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoohttp.py", line 536, in response_wrap
response = f(*args, **kw)
File "c:usersnacerdesktoplearning_odooodooworkspaceodoo- 
15custom_addonsom_universitycontrollerscontrollers.py", line 29, 
in create_webpatient
new_task = request.env['university.student'].sudo().create(kw)
File "<decorator-gen-131>", line 2, in create
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooapi.py", line 412, in _model_create_multi
return create(self, [arg])
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooaddonsmailmodelsmail_thread.py", line 265, in create
threads = super(MailThread, self).create(vals_list)
File "<decorator-gen-67>", line 2, in create
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooapi.py", line 413, in _model_create_multi
return create(self, arg)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooaddonsbasemodelsir_fields.py", line 613, in create
recs = super().create(vals_list)
File "<decorator-gen-13>", line 2, in create
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodooapi.py", line 413, in _model_create_multi
return create(self, arg)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoomodels.py", line 4070, in create
records = self._create(data_list)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoomodels.py", line 4220, in _create
field.create([
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoofields.py", line 3283, in create
self.write_batch(record_values, True)
File "C:UsersnacerDesktoplearning_odooodooWorkspaceodoo- 
15odooodoofields.py", line 3304, in write_batch
raise ValueError("Wrong value for %s: %s" % (self, value))
ValueError: Wrong value for university.student.image_1920: 
<FileStorage: '888j06068.jpg' ('image/jpeg')>
有谁能帮帮我吗?

第一个例子:多个文件上传

web/静态/src/xml/base.xml

<t t-name="web.CustomFileInput" owl="1">
<span class="o_file_input" aria-atomic="true">
<span class="o_file_input_trigger" t-on-click.prevent="_onTriggerClicked">
<t t-slot="default">
<button class="btn btn-primary">Choose File</button>
</t>
</span>
<input type="file" name="ufile" class="o_input_file d-none"
t-att="{multiple: props.multi_upload, accept: props.accepted_file_extensions}"
t-ref="file-input"
t-on-change="_onFileInputChange"
/>
</span>
</t>

async _uploadFiles(files, params={}) {
if (!files || !files.length) { return; }
await new Promise(resolve => {
const xhr = this._createXhr();
xhr.open('POST', this._getFileUploadRoute());
//...
//...
_getFileUploadRoute() {
return '/documents/upload_attachment';
},

控制器/main.py

@http.route('/documents/upload_attachment', type='http', methods=['POST'], auth="user")
def upload_document(self, folder_id, ufile, document_id=False, partner_id=False, owner_id=False):
files = request.httprequest.files.getlist('ufile')
result = {'success': _("All files uploaded")}
tag_ids = request.params.pop('tag_ids', None)
tag_ids = tag_ids.split(',') if tag_ids else []
if document_id:
document = request.env['documents.document'].browse(int(document_id))
ufile = files[0]
try:
data = base64.encodebytes(ufile.read())
mimetype = ufile.content_type
document.write({
'name': ufile.filename,
'datas': data,
'mimetype': mimetype,
})
except Exception as e:
logger.exception("Fail to upload document %s" % ufile.filename)
result = {'error': str(e)}
else:
vals_list = []
for ufile in files:
try:
mimetype = ufile.content_type
datas = base64.encodebytes(ufile.read())
vals = {
'name': ufile.filename,
'mimetype': mimetype,
'datas': datas,
'folder_id': int(folder_id),
'tag_ids': tag_ids,
'partner_id': int(partner_id)
}
if owner_id:
vals['owner_id'] = int(owner_id)
vals_list.append(vals)
except Exception as e:
logger.exception("Fail to upload document %s" % ufile.filename)
result = {'error': str(e)}
documents = request.env['documents.document'].create(vals_list)
result['ids'] = documents.ids
return json.dumps(result)

模型/document.py

class Document(models.Model):
_name = 'documents.document'
@api.model
def create(self, vals):
keys = [key for key in vals if
self._fields[key].related and self._fields[key].related[0] == 'attachment_id']
attachment_dict = {key: vals.pop(key) for key in keys if key in vals}
attachment = self.env['ir.attachment'].browse(vals.get('attachment_id'))
if attachment and attachment_dict:
attachment.write(attachment_dict)
elif attachment_dict:
attachment_dict.setdefault('name', vals.get('name', 'unnamed'))
attachment = self.env['ir.attachment'].create(attachment_dict)
vals['attachment_id'] = attachment.id
new_record = super(Document, self).create(vals)
# this condition takes precedence during forward-port.
if (attachment and not attachment.res_id and (not attachment.res_model or attachment.res_model == 'documents.document')):
attachment.with_context(no_document=True).write({'res_model': 'documents.document', 'res_id': new_record.id})
return new_record

第二个例子:在web_editor/static/src/xml/wysiwyg.xml

<input type="file" class="d-none o_file_input" name="upload" t-att-accept="widget.options.accept" t-att-multiple="widget.options.multiImages &amp;&amp; 'multiple'"/>

web_editor/静态/src/js/wysiwyg/widget/media.js

/**
* Let users choose a file, including uploading a new file in odoo.
*/
var FileWidget = SearchableMediaWidget.extend({
events: _.extend({}, SearchableMediaWidget.prototype.events || {}, {
'click .o_upload_media_button': '_onUploadButtonClick',
'change .o_file_input': '_onFileInputChange',
/**
* Handles change of the file input: create attachments with the new files
* and open the Preview dialog for each of them. Locks the save button until
* all new files have been processed.
*
* @private
* @returns {Promise}
*/
_onFileInputChange: function () {
return this._mutex.exec(this._addData.bind(this));
},
/**
* Uploads the files that are currently selected on the file input, which
* creates new attachments. Then inserts them on the media dialog and
* selects them. If multiImages is not set, also triggers up the
* save_request event to insert the attachment in the DOM.
*
* @private
* @returns {Promise}
*/
async _addData() {
let files = this.$fileInput[0].files;
if (!files.length) {
// Case if the input is emptied, return resolved promise
return;
}
var self = this;
var uploadMutex = new concurrency.Mutex();
// Upload the smallest file first to block the user the least possible.
files = _.sortBy(files, 'size');
_.each(files, function (file) {
// Upload one file at a time: no need to parallel as upload is
// limited by bandwidth.
uploadMutex.exec(function () {
return utils.getDataURLFromFile(file).then(function (result) {
return self._rpc({
route: '/web_editor/attachment/add_data',
params: {
'name': file.name,
'data': result.split(',')[1],
'res_id': self.options.res_id,
'res_model': self.options.res_model,
'width': 0,
'quality': 0,
},
}).then(function (attachment) {
self._handleNewAttachment(attachment);
});
});
});
});

在/web_editor/main.py

@http.route('/web_editor/attachment/add_data', type='json', auth='user', methods=['POST'], website=True)
def add_data(self, name, data, quality=0, width=0, height=0, res_id=False, res_model='ir.ui.view', **kwargs):
try:
data = tools.image_process(data, size=(width, height), quality=quality, verify_resolution=True)
except UserError:
pass  # not an image
self._clean_context()
attachment = self._attachment_create(name=name, data=data, res_id=res_id, res_model=res_model)
return attachment._get_media_info()

def _attachment_create(self, name='', data=False, url=False, res_id=False, res_model='ir.ui.view'):
"""Create and return a new attachment."""
if name.lower().endswith('.bmp'):
# Avoid mismatch between content type and mimetype, see commit msg
name = name[:-4]
if not name and url:
name = url.split("/").pop()
if res_model != 'ir.ui.view' and res_id:
res_id = int(res_id)
else:
res_id = False
attachment_data = {
'name': name,
'public': res_model == 'ir.ui.view',
'res_id': res_id,
'res_model': res_model,
}
if data:
attachment_data['datas'] = data
elif url:
attachment_data.update({
'type': 'url',
'url': url,
})
else:
raise UserError(_("You need to specify either data or url to create an attachment."))
attachment = request.env['ir.attachment'].create(attachment_data)
return attachment

最新更新