I'm trying to display a user signed documents (from the "Sign app") on his page, so I added this to the inherited model:
x_signatures_relation = fields.One2many("signature.request.item", "partner_id")
x_signatures = fields.One2many("signature.request", compute="_get_signed_documents")
#api.one
def _get_signed_documents(self):
ids = []
for signature in self.x_signatures_relation:
ids.append(signature.signature_request_id)
self.x_signatures = ids
"signature.request.item" is the table relating the partner (user) with "signature.request" the actual signature.
However this return an empty view even though the current user has two signatures, but if I replace :
self.x_signatures = ids
with :
self.x_signatures = ids[0]
or :
self.x_signatures = ids[1]
It displays the record, so what's going on ?
Odoo has a very specific set of rules about how you are "allowed" to manipulate One2many and Many2Many fields.
See my recent answer, which gives a detailed explanation of all options and when/how to use them. The Odoo documentation also explains it as well.
In your case, you are setting the value in a compute method, so you want to completely replace any existing values.
# Instead of
# self.x_signatures = ids
# Try this, which uses the special number 6 to mean
# "replace any existing ids with these ids"
self.x_signatures = [(6, 0, ids)]
Furthermore, you could simplify your compute method:
#api.one
def _get_signed_documents(self):
self.x_signatures = [(6, 0, self.x_signatures_relation.ids)]
Related
I am trying to create button in Invoice that will update certain field in inovoice lines. I've found how to update field in account.invoice but I am strugling to find right way how to update it in account.invoice.line.
class accountinvoiceext(models.Model):
_inherit = ['account.invoice']
#api.one
def my_button(self,uid):
invoice_id = self.id
#lines = getinvoicelinesbyid(invoice_id)
I am sure there is some proper way how to get invoice.lines related to this invoice, or not ?
I've tried _inherit account.invoice.line but then I cannot define button there.
Second question - what is best way to call some function every time invoice is created ?
if you want to add button to change the line. you need to loops the one2many fields in the invoice, and change #api.one to #api.multi, example:
#api.multi
def my_button(self):
for line in self.invoice_line:
line.write({'your_field': 'your_values'})
and if you want to call this function every invoice is create, you need to modified the create function:
#api.multi
def create_project(self,values):
res = super(SaleOrder, self).create(values)
res.my_button()
return res
I have:
a) given product_template_id (i.e. id 100) and
b) a duplicated product_template_id (i.e. id 200) created using copy() method
copy() method copies only product.template model, so suppliers for that specific product are not copied.
I would like to duplicate all suppliers for that model, but now I am wondering which is the right way to do it in Odoo.
If I understood the model properly suppliers prices for a given product are stored in product_supplierinfo table, where each record that points to a given product_tmpl_id specifices a supplier price/qty for a given product_template.
Which would be the way in Odoo to search for all records that point to a given product_tmpl_id (i.e. 100), duplicate them changing product_tmpl_id to the new one (i.e. 200)?
Excerpt from the ORM Documentation:
copy (bool) -- whether the field value should be copied when the record is duplicated (default: True for normal fields, False for One2many and computed fields, including property fields and related fields)
The field you're referring to is seller_ids, whose field definition is below:
seller_ids = fields.One2many('product.supplierinfo', 'product_tmpl_id', 'Vendors')
The copy attribute is not explicitly defined, so it is False by default (as explained in the documentation above). If you want this field to copy along with the other values during the standard product "Duplicate" (copy method), you can do this:
class ProductTemplate(models.Model):
_inherit = 'product.template'
# This only changes the copy attribute of the existing seller_ids field.
# All other attributes (string, comodel_name, etc.) remain as they are defined in core.
seller_ids = fields.One2many(copy=True)
Alternatively
If you want to only have the field copied sometimes, you can extend the copy method to look for a specific context value and only copy based on that.
# This may take some tweaking, but here's the general idea
#api.multi
def copy(self, vals):
new_product = super(YourClass, self).copy(vals)
if vals.get('copy_sellers'):
new_product.seller_ids = self.seller_ids.copy({'product_id': new_product.id})
return new_product
# Whatever you have calling the copy method will need to include copy_sellers in vals
vals.update({'copy_sellers': True})
product.copy(vals)
I'm developing an app using PyQt with MongoDB as a backend. There is a table (QTableView) in my application that should be populated with data from MongoDB and I would like to use model->view architecture. Since Qt doesn't have a model for MongoDB I need to write a custom one.
This data is currently (can be changed to fit this problem) organized as a list of dictionaries, like this (actual data is more complex):
[{"name":"some name","phone":"283891273218"}, {"name":"some other name","phone":"56958656556"}]
Each dictionary represents a row, and each key of a dictionary is a column. After a few hours of searching I got almost nowhere, and the code is:
class CustomModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None, *args):
super(CustomModel, self).__init__()
self.datatable = None
def update(self, dataIn):
print 'Updating Model'
self.datatable = dataIn
#print 'Datatable : {0}'.format(self.datatable)
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self.datatable)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.datatable[0])
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
i = index.row()
j = index.column()
#print self.datatable
else:
return QtCore.QVariant()
def flags(self, index):
return QtCore.Qt.ItemIsEnabled
If I understand correctly, method data should fill the rows and columns with data but I don't know how to do it. This code is currently generating the correct number of rows and columns without data.
I would appreciate any help or advice.
A good thing to start with is to ensure a consistent column ordering when pulling data from the source. Since the data sources rows are dictionaries in this case, you are not guaranteed a consistent ordering when iterating (indexing is not available anyhow). To ensure consistent ordering, setup an ordered list of column indices that map to the data sources keys:
class CustomModel(QtCore.QAbstractTableModel):
columns = ['name', 'phone']
If you want to re-use this custom model for wrapping other data sets, you probably don't want to couple the column names so tightly with the class. It could easily be made more generic by using a required instance variable passed to the initializer:
class CustomModel(QtCore.QAbstractTableModel):
def __init__(self, columns, parent=None):
super(CustomModel, self).__init__(parent)
self.columns = columns
self.datatable = []
...
model = CustomModel(['name', 'phone'])
The column names can then be used to determine the columnCount which is also more robust because len(self.datatable[0]) will fail with an empty model:
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self.columns)
Furthermore, you can use the ordered columns class variable for use in a default implementation of headerData():
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.columns[section].title()
Finally, use the columns list as a lookup for converting a model column index into a dictionary key for use in the source data and return the source data value from the data() method:
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
row = self.datatable[index.row()]
column_key = self.columns[index.column()]
return row[column_key]
else:
return None
(Note PyQt4 and PySide don't need to use QVariant AFAICT)
I have been looking at the sqlalchemy recipes on their wiki, but don't know which one is best to implement what I am trying to do.
Every row on in my tables have an user_id associated with it. Right now, for every query, I queried by the id of the user that's currently logged in, then query by the criteria I am interested in. My concern is that the developers might forget to add this filter to the query (a huge security risk). Therefore, I would like to set a global filter based on the current user's admin rights to filter what the logged in user could see.
Appreciate your help. Thanks.
Below is simplified redefined query constructor to filter all model queries (including relations). You can pass it to as query_cls parameter to sessionmaker. User ID parameter don't need to be global as far as session is constructed when it's already available.
class HackedQuery(Query):
def get(self, ident):
# Use default implementation when there is no condition
if not self._criterion:
return Query.get(self, ident)
# Copied from Query implementation with some changes.
if hasattr(ident, '__composite_values__'):
ident = ident.__composite_values__()
mapper = self._only_mapper_zero(
"get() can only be used against a single mapped class.")
key = mapper.identity_key_from_primary_key(ident)
if ident is None:
if key is not None:
ident = key[1]
else:
from sqlalchemy import util
ident = util.to_list(ident)
if ident is not None:
columns = list(mapper.primary_key)
if len(columns)!=len(ident):
raise TypeError("Number of values doen't match number "
'of columns in primary key')
params = {}
for column, value in zip(columns, ident):
params[column.key] = value
return self.filter_by(**params).first()
def QueryPublic(entities, session=None):
# It's not directly related to the problem, but is useful too.
query = HackedQuery(entities, session).with_polymorphic('*')
# Version for several entities needs thorough testing, so we
# don't use it yet.
assert len(entities)==1, entities
cls = _class_to_mapper(entities[0]).class_
public_condition = getattr(cls, 'public_condition', None)
if public_condition is not None:
query = query.filter(public_condition)
return query
It works for single model queries only, and there is a lot of work to make it suitable for other cases. I'd like to see an elaborated version since it's MUST HAVE functionality for most web applications. It uses fixed condition stored in each model class, so you have to modify it to your needs.
Here is a very naive implementation that assumes there is the attribute/property self.current_user logged in user has stored.
class YourBaseRequestHandler(object):
#property
def current_user(self):
"""The current user logged in."""
pass
def query(self, session, entities):
"""Use this method instead of :method:`Session.query()
<sqlalchemy.orm.session.Session.query>`.
"""
return session.query(entities).filter_by(user_id=self.current_user.id)
I wrote an SQLAlchemy extension that I think does what you are describing: https://github.com/mwhite/multialchemy
It does this by proxying changes to the Query._from_obj and QueryContext._froms properties, which is where the tables to select from ultimately get set.
How can I interact with objects I've created based on their given attributes in Ruby?
To give some context, I'm parsing a text file that might have several hundred entries like the following:
ASIN: B00137RNIQ
-------------------------Status Info-------------------------
Upload created: 2010-04-09 09:33:45
Upload state: Imported
Upload state id: 3
I can parse the above with regular expressions and use the data to create new objects in a "Product" class:
class Product
attr_reader :asin, :creation_date, :upload_state, :upload_state_id
def initialize(asin, creation_date, upload_state, upload_state_id)
#asin = asin
#creation_date = creation_date
#upload_state = upload_state
#upload_state_id = upload_state_id
end
end
After parsing, the raw text from above will be stored in an object that look like this:
[#<Product:0x00000101006ef8 #asin="B00137RNIQ", #creation_date="2010-04-09 09:33:45 ", #upload_state="Imported ", #upload_state_id="3">]
How can I then interact with the newly created class objects? For example, how might I pull all the creation dates for objects with an upload_state_id of 3? I get the feeling I'm going to have to write class methods, but I'm a bit stuck on where to start.
You would need to store the Product objects in a collection. I'll use an array
product_collection = []
# keep adding parse products into the collection as many as they are
product_collection << parsed_product_obj
#next select the subset where upload_state_ud = 3
state_3_products = product_collection.select{|product| product.upload_state_id == 3}
attr reader is a declarative way of defining properties/attributes on your product class. So you can access each value as obj.attribute like I have done for upload_state_id above.
select selects the elements in the target collection, which meet a specific criteria. Each element is assigned to product, and if the criteria evaluates to true is placed in the output collection.