Odoo 10 Error - I need to send one2many values to another model has the same one2many field - odoo-10

I am receiving an Odoo 10 error.
I need to send one2many values to another model that has the same one2many field.
When I send the one2many field using my workflow, I had this error expected singleton, (1,4,6).
How can I solve this problem?
file_upload_material = fields.One2many('file.decision','param_two')
#api.multi
def confirm(self):
file = self.file_upload_material.file
name = self.file_upload_material.name
if self.name:
media_and_media = {
'name': self.name,
'instructions': self.instructions,
'task_id': self.task_id.id,
'descriptions': self.descriptions,
'date_from_exec': self.date_from_exec,
'date_to_exec': self.date_to_exec,
'date_from': self.date_from,
'task_name': self.task_name,
'file_upload_material': [(0, 0, {
'name': name,
'file': file
})],
}
self.env['my.task.log'].create(media_and_media)
self.state = 'unread'
class LogMydecision(models.Model):
_name = "my.task.log"
file_upload_material = fields.One2many('file.decision','param_one')
class FileUpload(models.Model):
_name = 'file.decision'
name = fields.Char('File Name')
file = fields.Binary('Upload File')
param_one = fields.Many2one('my.task.log)
param_two = fields.Many2one('task.log)

Your error seems related to the value of the field file_upload_material that contains a recordset and you are using it as like if there contains only one record(like m2o), specifically on this lines:
file = self.file_upload_material.file
name = self.file_upload_material.name
Your issue could be solved like this:
media_and_media = {
'name': self.name,
'instructions': self.instructions,
'task_id': self.task_id.id,
'descriptions': self.descriptions,
'date_from_exec': self.date_from_exec,
'date_to_exec': self.date_to_exec,
'date_from': self.date_from,
'task_name': self.task_name,
'file_upload_material': [(0, 0, {
'name': fum.name,
'file': fum.file
}) for fum in self.file_upload_material],
}
Also seems that you are duplicating the value of the file.decision records by copying at the confirm method, you could change the field to a m2m and reuse it or also you could write the relation between the existing record with (4, id) or (6,0,[ids]) instead of using (0,0,{vals}), like this:
media_and_media = {
'name': self.name,
'instructions': self.instructions,
'task_id': self.task_id.id,
'descriptions': self.descriptions,
'date_from_exec': self.date_from_exec,
'date_to_exec': self.date_to_exec,
'date_from': self.date_from,
'task_name': self.task_name,
'file_upload_material': [(6, 0, self.file_upload_material.ids)],
}
That prevent the record duplication. Also I will recommends you to switch to m2m fields for file_upload_material relations

Related

get new value in django admin widget

I am working on creating a widget so clients can easily modify model's Json field in django admin.
Env Info:
django-version: 3.1.14
Before drive into the widget, this is a simplify version of my model:
class Property(PolymorphicModel):
"""Basic information about property"""
...
address = models.JSONField(blank=True, null=True, default=dict,)
...
And this how I am declaring the form:
class PropertyForm(forms.ModelForm):
class Meta:
model = Property
fields = [
"address",
]
widgets = {
'address': JSONEditorWidget(),
}
I've already manage to convert json file into django admin inputs by using the following code:
class JSONEditorWidget(forms.widgets.Input):
def as_field(self, name, key, value):
""" Render key, value as field """
new_name = name + '__' + key
self.attrs = self.build_attrs({new_name:key})
self.attrs['value'] = utils.encoding.force_text(value)
return u'%s: <input%s />' % (new_name, forms.utils.flatatt(self.attrs))
def to_fields(self, name, json_obj):
"""Get list of rendered fields for json object"""
inputs = []
for key, value in json_obj.items():
inputs.append(self.as_field(name, key, value))
return inputs
def value_from_datadict(self, data, files, name):
"""I've been trying to get new values in this function but nothing successful"""
return json.dumps(prev_dict)
def render(self, name, value, attrs=None, renderer = None):
# TODO: handle empty value (render text field?)
if value is None or value == '':
value = '{}'
json_obj = json.loads(value)
inputs = self.to_fields(name, json_obj)
# render json as well
inputs.append(value)
return utils.safestring.mark_safe(u"<br />".join(inputs))
with this I could go from this:
To this:
My problem now is to catch the new values when user clicks on save/save and continue, so I can convert them into json file to save new records on postgres.
Ive tried with the function value_fromdatadict() but couldn't manage a way to get new values in the input box...
If anyone can helps me I will be so glad, I've been dealing with this a while and this is driving me crazy

django elastic search giving TransportError 400 for model id large digit search

I am getting this error while I am searching large digit ,where my model has only interger field
class PurchaseDocument(DocType):
status = fields.IntegerField()
purchase_order_status = fields.IntegerField()
organization = fields.ObjectField(properties={
'pk': fields.IntegerField()
})
sales_return = fields.ObjectField(
properties={
'pk': fields.IntegerField()
}
)
class Meta:
model = Purchase
fields = [
'id',
'remarks',
'vouchar_no'
]
rebuild_from_value_list = True
related_models = [Purchase, Transaction, TransactionPurchase]
I have purchase model like this-
class Purchase(model.Models):
//something

How to check if many-to-many list is empty with django-filter?

I am using django-rest-framework with django-filter to implement filtering.
Let's say I have following result:
{
"id": 13,
"created": "2017-06-21T01:08:49.790254Z",
"updated": "2017-07-21T10:25:51.706730Z",
"toylist": [],
}
How do I implement filtering so I can check if the toylist array is empty? For example, something like: /toys/?toylist__isnull=True
Ok, it was a relatively simple fix:
class ToysFilter(filters.FilterSet):
toylist__isnull = filters.BooleanFilter(name='toylist', method='list_is_empty')
class Meta:
model = Toys
fields = {
'id':['exact'],
'created':'__all__',
'updated':'__all__',
}
def list_is_empty(self, qs, name, value):
isnull = not value
lookup_expr = LOOKUP_SEP.join([name, 'isnull'])
return qs.filter(**{lookup_expr: isnull}).distinct()
It isn't necessary to use a method here. More simply, you could do the following:
class ToysFilter(filters.FilterSet):
toylist__isnull = filters.BooleanFilter(name='toylist', lookup_expr='isnull', distinct=True)

How to Improve Flask Admin performance for data intensive forms / general optimization advice?

I have some models:
class Paper(db.Model):
# Regarding lack of unique constraints -- the legacy database had
# much duplicate data, and I hope to eventually eliminate that duplicate
# data and then add uniqueness constraints on some columns later.
__tablename__ = 'papers'
__searchable__ = ['title', 'abstract', 'keywords']
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(500))
abstract = db.Column(db.Text, nullable=True)
doi = db.Column(db.String(50), nullable=True)
pubmed_id = db.Column(db.String(50), nullable=True)
link = db.Column(db.String(500), nullable=True)
journals = db.relationship(Journal, secondary="journal_paper")
chapters = db.relationship(Chapter, secondary="chapter_paper")
authors = db.relationship(Author, secondary="author_paper")
keywords = db.relationship(Keyword, secondary="keyword_paper")
...
I have a custom model view:
class PaperModelView(MainModelView): # Looked up by advanced search with check if __repr__ is "PaperModelView object"
page_size = 100
column_list = (
'title',
'chapter_paper_assoc',
)
column_exclude_list = (
'keyword_paper_assoc',
'author_paper_assoc',
'link',
'doi',
'pubmed_id',
)
column_details_list = (
'title',
'chapter_paper_assoc',
'journal_paper_assoc',
'authors',
'abstract',
'keywords',
'doi',
'pubmed_id',
'link',
)
column_labels = {
'chapter_paper_assoc': 'Print Information',
'journal_paper_assoc': 'Publication Information',
}
column_filters = [
'chapter_paper_assoc.printed',
'journal_paper_assoc.publication_date',
'chapters.name',
'chapters.number',
'journals.name',
'authors.first_name',
'authors.last_name',
'keywords.keyword',
'doi',
'abstract',
'link',
'title',
]
column_filter_labels = {
'chapter_paper_assoc.printed': 'Printed',
'journal_paper_assoc.publication_date': 'Publication Date',
'chapters.name': 'Chapter Name',
'chapters.number': 'Chapter Number',
'journals.name': 'Journal Name',
'authors.first_name': 'Author First Name',
'authors.last_name': 'Author Last Name',
'keywords.keyword': 'Keyword',
'doi': 'DOI',
'abstract': 'Abstract',
'link': 'Link',
'title': 'Paper Title',
}
column_details_exclude_list = (
'keyword_paper_assoc',
'author_paper_assoc',
)
form_excluded_columns = (
'chapter_paper_assoc',
'journal_paper_assoc',
'keyword_paper_assoc',
'author_paper_assoc',
)
column_formatters = {
'journals': macro('render_journals'),
'chapters': macro('render_chapters'),
'authors': macro('render_authors'),
'keywords': macro('render_keywords'),
'chapter_paper_assoc': macro('render_chapter_papers'),
'journal_paper_assoc': macro('render_journal_papers'),
}
column_formatters_export = {
'journals': formatter,
'chapters': formatter,
'authors': formatter,
'keywords': formatter,
'chapter_paper_assoc': formatter,
'journal_paper_assoc': formatter,
}
column_searchable_list = (
'title',
'keywords.keyword',
)
form_ajax_refs = {
'authors': {
'fields': ['first_name', 'middle_name', 'last_name'],
'page_size': 10,
},
'chapters': {
'fields': ['number', 'name',],
'page_size': 5,
},
'journals': {
'fields': ['name', 'issn'],
'page_size': 2,
},
'keywords': {
'fields': ['keyword',],
'page_size': 10,
}
}
def unique_title(form, field):
p = Paper.query.filter_by(title=field.data).first()
if p:
raise ValidationError("A Paper with this title already exists")
def unique_doi(form, field):
p = Paper.query.filter_by(doi=field.data).first()
if p:
raise ValidationError("A Paper with this doi already exists")
form_args = {
'title': {
'validators': [unique_title, DataRequired()]
},
'doi': {
'validators': [unique_doi],
},
'link': {
'validators': [DataRequired(), URL()],
},
'journals': {
'validators': [Length(0,1)], # Thought it is a many-many field, allow only 0 or 1
} # 0 or 1 Journals assumption is carried to on_model_change (be careful if changing)
}
form_base_class = FlaskForm
list_template = 'auth/model/paper/list.html'
def on_model_change(self, form, model, is_created):
"""
Perform some actions before a model is created or updated.
Called from create_model and update_model in the same transaction (if it has any meaning for a store backend).
By default does nothing.
Parameters:
form – Form used to create/update model
model – Model that will be created/updated
is_created – Will be set to True if model was created and to False if edited
"""
all_chapters = list(set(form.chapters.data + form.chapters_printed.data))
for chapter in all_chapters:
if chapter in form.chapters_printed.data: # if chapter in both, printed takes priority
chapter_paper = ChapterPaper.query.filter_by(chapter_id=chapter.id, paper_id=model.id).first()
if not chapter_paper:
chapter_paper = ChapterPaper(chapter_id=chapter.id, paper_id=model.id)
chapter_paper.printed = True
db.session.add(chapter_paper)
journal = None
if form.journals.data:
journal = form.journals.data[0]
if journal: # Assumes only 1 journal if there are any journals in this field
issue = form.issue.data
volume = form.volume.data
pages = form.pages.data
journal_paper = JournalPaper.query.filter_by(journal_id=journal.id, paper_id=model.id).first()
if not journal_paper:
journal_paper = JournalPaper(journal_id=journal.id, paper_id=model.id)
journal_paper.issue = issue
journal_paper.volume = volume
journal_paper.pages = pages
db.session.add(journal_paper)
db.session.commit()
#expose('/new/', methods=('GET', 'POST'))
def create_view(self):
"""
Create model view
"""
return_url = get_redirect_target() or self.get_url('.index_view')
if not self.can_create:
return redirect(return_url)
form = ExtendedPaperForm()
if not hasattr(form, '_validated_ruleset') or not form._validated_ruleset:
self._validate_form_instance(ruleset=self._form_create_rules, form=form)
if self.validate_form(form):
# in versions 1.1.0 and before, this returns a boolean
# in later versions, this is the model itself
model = self.create_model(form)
if model:
flash('Record was successfully created', 'success')
if '_add_another' in request.form:
return redirect(request.url)
elif '_continue_editing' in request.form:
# if we have a valid model, try to go to the edit view
if model is not True:
url = self.get_url('.edit_view', id=self.get_pk_value(model), url=return_url)
else:
url = return_url
return redirect(url)
else:
# save button
return redirect(self.get_save_return_url(model, is_created=True))
form_opts = FormOpts(widget_args=self.form_widget_args,
form_rules=self._form_create_rules)
if self.create_modal and request.args.get('modal'):
template = self.create_modal_template
else:
template = self.create_template
return self.render(template,
form=form,
form_opts=form_opts,
return_url=return_url)
def on_form_prefill(self, form, id):
"""
Perform additional actions to pre-fill the edit form.
Called from edit_view, if the current action is rendering
the form rather than receiving client side input, after
default pre-filling has been performed.
By default does nothing.
You only need to override this if you have added custom
fields that depend on the database contents in a way that
Flask-admin can't figure out by itself. Fields that were
added by name of a normal column or relationship should
work out of the box.
:param form:
Form instance
:param id:
id of the object that is going to be edited
"""
model = Paper.query.filter_by(id=id).first()
form.title.data = model.title
form.abstract.data = model.abstract
form.pubmed_id.data = model.pubmed_id
form.doi.data = model.doi
form.link.data = model.link
form.chapters.data = model.chapters
form.authors.data = model.authors
form.keywords.data = model.keywords
form.chapters_printed.data = model.get_printed_chapters()
form.journals.data = model.journals
journal = model.journals[0]
if journal:
journal_paper = JournalPaper.query.filter_by(paper_id=model.id, journal_id=journal.id).first()
if journal_paper:
form.issue.data = journal_paper.issue
form.volume.data = journal_paper.volume
form.pages.data = journal_paper.pages
form.publication_date = journal_paper.publication_date
#expose('/edit/', methods=('GET', 'POST'))
def edit_view(self):
"""
Edit model view
"""
return_url = get_redirect_target() or self.get_url('.index_view')
if not self.can_edit:
return redirect(return_url)
id = get_mdict_item_or_list(request.args, 'id')
if id is None:
return redirect(return_url)
model = self.get_one(id)
if model is None:
flash('Record does not exist!', 'error')
return redirect(return_url)
form = ExtendedPaperForm()
if not hasattr(form, '_validated_ruleset') or not form._validated_ruleset:
self._validate_form_instance(ruleset=self._form_edit_rules, form=form)
if self.validate_form(form):
if self.update_model(form, model):
flash('Record was successfully saved', 'success')
if '_add_another' in request.form:
return redirect(self.get_url('.create_view', url=return_url))
elif '_continue_editing' in request.form:
return redirect(request.url)
else:
# save button
return redirect(self.get_save_return_url(model, is_created=False))
if request.method == 'GET':
self.on_form_prefill(form, id)
form_opts = FormOpts(widget_args=self.form_widget_args,
form_rules=self._form_edit_rules)
if self.edit_modal and request.args.get('modal'):
template = self.edit_modal_template
else:
template = self.edit_template
return self.render(template,
model=model,
form=form,
form_opts=form_opts,
return_url=return_url)
#property
def can_create(self):
return current_user.can_edit_papers()
#property
def can_edit(self):
return current_user.can_edit_papers()
#property
def can_delete(self):
return current_user.can_edit_papers()
def is_accessible(self):
if current_user.is_authenticated:
return current_user.can_view_papers()
return False
# Add a batch action to this class using flask admin's syntax
#action('cite', 'Cite', 'Create Citations for the Selected Papers?')
def action_cite(self, ids):
try:
query = Paper.query.filter(Paper.id.in_(ids))
citations = ""
for paper in query.all():
citation = paper.cite('APA')
citations += "{}\n".format(citation)
response = make_response(citations)
response.headers[
"Content-Disposition"
] = "attachment; filename=citations.txt" # Downloadable response
return response
except Exception as ex:
if not self.handle_view_exception(ex):
raise
#action('chapter_add', 'Add to Chapters')
def action_chapter_add(self, ids):
return_url = request.referrer
try:
return redirect(
url_for('addtochapterview.index',
return_url=return_url), 307
)
except Exception as ex:
if not self.handle_view_exception(ex):
raise
#action('chapter_remove', 'Remove From Chapters')
def action_chapter_remove(self, ids):
return_url = request.referrer
try:
return redirect(
url_for('removefromchapterview.index',
return_url=return_url), 307
)
except Exception as ex:
if not self.handle_view_exception(ex):
raise
#action('mark_as_printed', 'Mark As Printed')
def mark_printed(self, ids):
return_url = request.referrer
try:
return redirect(
url_for('markasprintedview.index',
return_url=return_url), 307
)
except Exception as ex:
if not self.handle_view_exception(ex):
raise
#action('mark_as_unprinted', 'Mark As NOT Printed')
def unmark_printed(self, ids):
return_url = request.referrer
try:
return redirect(
url_for('markasunprintedview.index',
return_url=return_url), 307
)
except Exception as ex:
if not self.handle_view_exception(ex):
raise
I have a custom form:
class ExtendedPaperForm(FlaskForm):
title = StringField()
abstract = TextAreaField()
doi = StringField()
pubmed_id = StringField()
link = StringField()
journals = QuerySelectMultipleField(
query_factory=_get_model(Journal),
allow_blank=False,
)
issue = StringField()
volume = StringField()
pages = StringField()
publication_date = DateField(format='%Y-%m-%d')
authors = QuerySelectMultipleField(
query_factory=_get_model(Author),
allow_blank=False,
)
keywords = QuerySelectMultipleField(
query_factory=_get_model(Keyword),
allow_blank=True,
)
chapters_printed = QuerySelectMultipleField(
query_factory=_get_model(Chapter),
allow_blank=True,
label="Chapters (Printed)",
)
chapters = QuerySelectMultipleField(
query_factory=_get_model(Chapter),
allow_blank=True,
label="Chapters (All)",
)
with the custom query factory:
def _get_model(model, order=None):
if order:
return lambda: db.session.query(model).order_by(order)
return lambda: db.session.query(model)
This all works very well, with the exception that it is very slow. From the time that the user clicks the "Create" button to the time that the servers returns a response is, on average, around ~1.1 minutes. That's too long :/.
So, I think my options are:
1) Write a better query factory function
2) Memoize the query factories for each field of the form
3) Some combination of the two
I don't know how to do any of these options. How would one even write a better query factory here. I am pretty sure that there isn't much to do on this end, since I need every option to be available for each field. I have used Flask-Cache before on a simpler project, but don't know how to apply this knowledge here.
I imagine that I could simply memoize the query functions -- but I don't know where in my model view I would clear the cached values and rebuild them -- how often -- why -- etc.
I don't have the strongest web programming vocabulary, so using references and documentation is proving frustrating. Could someone please provide some guidance on how to improve this process? Or like, maybe just the right place to look for inspiration in the flask-admin source code?
P.S
If you have a general guide for scaling and optimizing a flask app that you think would help, please share in comments. Thanks.
No help here, and yes, sorry that this post was so vague.
Here are some things that I did that reduced the average client response time from 95,000 ms to the expected / acceptable ~100 ms:
Integrated Caching queries
Eliminated joins (used dynamic loading of related objects where possible)
Cached query factories and update them on model change
The Flask-DebugToolbar extension is great! You should use it.

wtforms, ndb and blobstore

I have this model:
class Product(ndb.Model):
title = ndb.StringProperty()
description = ndb.TextProperty()
img = ndb.BlobKeyProperty(indexed=False)
I need a html form that reads the values ​​of fields (title and description) and read the image (from a file field), and keeps the values ​​NDB object, the image in the Blobstore and updates the field BlobKeyProperty correctly.
As I work with wtforms, I tried to do it with a form like the following:
class ProductForm(Form):
title = fields.TextField('Title', [validators.Required(), validators.Length(min=4, max=25)])
description = fields.TextAreaField('Description')
img = fields.FileField('img')
The form shows the file field correctly but in the POST, it does not work because I don't know how to read the file, save the file to Blobstore and update the BlobKeyProperty.
My handler is this:
class ProductHandler(BaseHandler):
def new(self):
if self.request.POST:
data = ProductForm(self.request.POST)
if data.validate():
model = Product()
data.populate_obj(model)
model.put()
self.add_message("Product add!", 'success')
return self.redirect_to("product-list")
else:
self.add_message("Product not add!", 'error')
params = {
'form': ProductForm(),
"kind": "product",
}
return self.render_template('admin/new.html', **params)
Error is Expected str, got u'image.jpg'
If someone can help me, I would appreciate it!
The only solution I found is to use a deprecated low-level API described in https://developers.google.com/appengine/docs/python/blobstore/#Python_Writing_files_to_the_Blobstore
I make a wtform validator for the FileField.
I changed:
img = fields.FileField('img')
To:
img = fields.FileField('img', [create_upload_file])
And I write this validator:
def create_upload_file(form, field):
file_name = files.blobstore.create(mime_type=field.data.type, _blobinfo_uploaded_filename=field.data.filename)
with files.open(file_name, 'a') as f:
f.write(field.data.file.read())
files.finalize(file_name)
blob_key = files.blobstore.get_blob_key(file_name)
field.data = blob_key
The validator create the blob in blobstore, and then it change the field data from FieldStorage to blob_key.
I don't think that is the best solution but it works for now.

Resources