I have an integer field in which i just display a value calculated with an 'on_change' function.
Is there anyway to not store that value in my Database(another way than using a function field)?
I also tried to put store=False but i guess it doesn't work with an integer field
here's my integer field:
'quantite_op': fields.integer('Quantité produite par opération',store=False,readonly=True),
And the onchange function:
def onchange_num_of(self,cr,uid,ids,of_num,operation_nom,quantite,context=None):
res = {}
if of_num:
ordre_f = self.pool.get('ordres_fabrication').browse(cr, uid,of_num,context=context)
res['delai_of'] =ordre_f.delai
res['quantite_of'] =ordre_f.quantite
if operation_nom:
record =self.search(cr, uid, [('of_num','=',of_num),('operation_nom','=',operation_nom)])
qte=0
for item in record:
prod = self.browse(cr, uid,item,context=context)
qte += prod.quantite
res['quantite_op'] = qte+quantite
return {'value': res}
Thanks
you will always have that field in your db, except it's a non-stored functional field. but you can manipulate the create or write methods of the model. you can set your integer field on None resp. False in that methods, so no value will be stored in that db column.
Related
I have extended the product.template with following fields:
length = fields.Integer("Length", default=0)
length_float = fields.Float("Float", default=0.0)
Additionally I have extended the product.packaging model with this fields and an onchange method:
product_id_ref = fields.Many2one('product.product', string="Product Reference")
length = fields.Integer('Length')
length_float = fields.Float('Length_Float')
#api.onchange("product_id_ref")
def _onchange_product(self):
if self.product_id_ref:
self.length_float = self.product_id_ref.length_float
self.length = self.product_id_ref.length
Interestingly the FLOAT field length_float is changed as expected and shown in the view. The INTEGER field is not changed.
Something more suprising is that if I change the order of the onchange method
#api.onchange("product_id_ref")
def _onchange_product(self):
if self.product_id_ref:
self.length = self.product_id_ref.length
self.length_float = self.product_id_ref.length_float
NO value is changed!
How could that be?? Does anyone have an idea?
Thanks!
It came out that the problem was the 'length' field itself. This name leads to problems on the javascript side. Renaming leads to the desired result.
I have a model, where I want to access a field, given by a string. Example:
def test(self):
field = 'name'
name = getattr(self, field)
This works fine - name is set to self.name. But then I want to access a related field:
def test2(self):
field = 'partner_id.name'
name = getattr(self, field)
That doesn't work (because 'partner_id.name' does not exist on self). Any idea how to do it right?
getattr doesn't support the dot notation, only simple attribute names. You can however create a simple function that does:
def getfield(model, field_name):
value = model
for part in field_name.split('.'):
value = getattr(value, part)
return value
You would use it like this:
def test2(self):
field = 'partner_id.name'
name = getfield(self, field)
You need to use the object that contain partner_id.name
def test2(self):
field = 'name'
object = self.pool.get('res.partner').browse(cr, uid, self.partner_id.id)#v7
#object = self.env['res.partner'].browse(self.partner_id.id)#v8
name = getattr(object, field)
I also came across another solution, inspired by the mail template system:
from openerp.tools.safe_eval import safe_eval as eval
def test2(self):
field = 'partner_id.name'
field = 'object.' + field
name = eval(field, {'object': self})
I have a function field, but I don't know what should the function return.
Here's my code:
the function:
def _property_expense_preset_expenses(self, cr, uid, ids, expenses, arg, context):
spus = self.browse(cr, uid, ids)
_spu = False
for spu in spus:
_spu = spu
if(_spu):
expenses_acc = {}
property_expense_presets = _spu.property_expense_presets
for property_expense_preset in property_expense_presets:
expenses = property_expense_preset.expense_preset.expenses
for expense in expenses:
expenses_acc[expense.id] = expense
return expenses_acc
else:
return {}
The field definition:
'expenses' : fields.function(
_property_expense_preset_expenses,
type='one2many',
obj="property.expense",
method=True,
string='Expenses'
),
The code above doesn't work, it raises an error : KeyError: 788
Like all function fields, it must return a dictionary with an entry and value for every ID you get passed in ids, although your value can be False, None, []
In your case your functional field is declared as a one2many type which means your functional field must return a dictionary with an entry per id and the value, a list of integers that represent the ids of the related table, in your case, property.expense.
A very common pattern is:
def _property_expense_preset_expenses(self, cr, uid, ids, field, arg, context = None):
res = {}
for spu in self.browse(cr, uid, ids, context = context):
res[spu.id] = []
for preset in spu.property_expense_presets:
res[spu.id].extend([x.id for x in preset.expense_preset.expenses])
return res
Assuming ids contains 1,2,3 you will get a result of
{1: [...], 2: [...], 3: []}
Where each list contains the integer ids of the expenses or an empty list if there are none.
As a general comment, I note your code doesn't default the context argument to None or pass the context as a named argument to the browse method - it is important to do both.
What is Function Field? What return this functional field function?
In OpenERP functional field is a field that return the calculated/logical value that value store into table. That value you can not get directly. that's why we want to use function field and return some value.
When you inserting data into object model functional field call every time defined function and that function logic code whatever you made and return that functional field value.
Example
class me.branch(osv.osv):
_name = "me.branch"
_order = 'name'
def _get_branch_name(self, cr, uid, ids, field_name, arg, context=None):
r = {}
for branch in self.browse(cr, uid, ids, context=context):
r[branch.id] = branch.name.split('/')[-1]
return r
_columns = {
'name': fields.char('Name', required=True),
'branch_name': fields.function(_get_branch_name, type='char', string='Branch', readonly=1, store=True),
}
Above example code branch name (eg. saas/demo/rarone ) already exist in another table.
But I want to get that branch name only last slash (/) after string (rarone) and store into this table only.
Here's is what i've tried to do:
<field name="of_num" domain="[('etat','=','Terminé')]"/>
where 'of_num' is a many2one field and 'etat' is a function field of char type.
But it seems not working.I still get all records in my dropdown list.
I have also tried with some other text with no unicode chars but still the same.
I tried also to use 'ilike' operator and tried to put domain in python code with the field definition but with no chance.
EDITED
I've figured out the source of my problem :
the field 'etat' is computed but not stored since I'am using 'store=false'.
it's working with store=True.
Still, I don't wan't to store it because my value needs to be computed every time a view is loaded.
Could anyone please help me to do that without having to store my value ? thank you
The only solution I've found to get around my problem is to use a Boolean field that is stored and updated every time my function is computed (of the function field 'etat').
Use fnct_search. For functional fields there is an argument called 'fnct_search' which returns a search domain condition.
For example
_columns = {
'a':fields.float('A'),
'b':fields.float('B'),
'total_fn': fields.function(_total, fnct_search=_total_search, string='Total'),
}
def _total(self, cr, uid, ids, name, arg, context=None):
res = {}
for obj in self.browse(cr, uid, ids, context):
res[obj.id] = obj.a + obj.b
return res
def _total_search(self, cursor, user, obj, name, args, domain=None, context=None):
ids = set()
for cond in args:
amount = cond[2]
cr.execute("select id from your_table having sum(a,b) %s %%s" % (cond[1]),(amount,))
res_ids = set(id[0] for id in cr.fetchall())
ids = ids and (ids & res_ids) or res_ids
if ids:
return [('id', 'in', tuple(ids))]
return [('id', '=', '0')]
Here _total returns the value to display for the field total_fn, fnct_search returns the list of tuple need for searching. So whenever we are giving the argument [('total_fn','=',1500)]
I have the following model:
class UptimeManager(models.Manager):
def with_length(self):
"""Get querySet of uptimes sorted by length including the current one. """
extra_length = Uptime.objects.extra(select={'length':
"""
SELECT
IF (end is null,
timestampdiff(second,begin,now()),
timestampdiff(second,begin,end))
FROM content_uptime c
WHERE content_uptime.id = c.id
"""
})
return extra_length
class Uptime(models.Model):
begin = models.DateTimeField('beginning')
end = models.DateTimeField('end', null=True) I call
host = models.ForeignKey("Host")
objects = UptimeManager()
...
then I call Uptime.objects.with_length().order_by('-length')[:10] to get list of longest uptimes.
But the length in template is of integer type. How to modify my code as the length of object returned by manager would be accessible in template as timedelta object?
I almost could do it by returning a list and converting number of seconds to timedelta objects, but then I have to do sorting, filtering etc. in my Python code which is rather ineffective in comparison to one well done SQL query.
Add a property to the model that looks at the actual field and converts it to the appropriate type.
My solution is to create a filter that determines type of length var and returns timedelta in case it's some integer type
from django import template
import datetime
register = template.Library()
def timedelta(value):
if isinstance(value, (long,int)):
return datetime.timedelta(seconds=value)
elif isinstance(value, datetime.timedelta):
return value
else: raise UnsupportedOperation
register.filter('timedelta',timedelta)
and use in template it's trivial
{{ uptime.length|timedelta }}