What I need to do is access values of the columns in a certain module before it's saved.
For example, if we have a module defined like this:
_columns = {
'name': fields.char('Department Name', size=64, required=True),
'complete_name': fields.function(_dept_name_get_fnc, type="char", string='Name'),
'parent_id': fields.many2one('hr.department', 'Parent Department', select=True),
'child_ids': fields.one2many('hr.department', 'parent_id', 'Child Departments'),
'note': fields.text('Note'),
}
All those columns are represented by controls such as textbox, combobox, checkbox, etc.
When we open the form view in the create mode, a new record is being created but not saved and won't take an id in the database until we click (save).
The question is how to access the values of those fields (controls) before they are saved and before the current record takes a new id.
I know about self.browse and self.search but they need an ID which is not available because the record is not saved yet.
Also, can we assign a global variable that could be accessed from any class in the module (a session variable in web-development terms)?
Better override create method, and use the way you want. Try this in you object, Its an example,
def create(self, cr, uid, vals, context=None):
if vals.get('name','/')=='/':
vals['name'] = self.pool.get('ir.sequence').get(cr, uid, 'sale.order') or '/'
return super(sale_order, self).create(cr, uid, vals, context=context)
likewise you can do your changes from here itself, see the value of "vals" and "context",
this would be helpful to you.
If you want to access this field than you need to override the create method of orm. For Example:
def create(self, cr, uid, vals, context=None):
name = vals.get('name')
complete_name = vals.get('complete_name') # like all others field if one2many field comes than this field access using for loop.
vals.update({'your_field_name': your_value_what_you_want_to_update_before_creating})
#vals.update({'name': 'Your Good Name'})
return super(your_class_name, self).create(cr, uid, vals, context)
def write(self, cr, uid, ids, vals, context=None):
print "\n\n\nCurrent Values =>", vals
name = vals.get('name')
print "\nCurrent Name => ", name
complete_name = vals.get('complete_name') # like all others field if one2many field comes than this field access using for loop.
#do all calculation as you want and store in local variable and that variable pass as a value. for example.
name = 'Your Good Name'
vals.update({'name': name})
print "\n\nUpdated Values => ", vals
return super(your_class_name, self).write(cr, uid, ids, vals, context)
Hope this will help you.
Related
I am trying to create a post request for a game api. The game implies that a user can label a picture. A label entered once is a tagging, a label entered twice for the same resource is a tag.
This is how I am trying to create a Tagging so far:
saved_tagging = Tagging.objects.create(user_id=current_user_id,
gameround=gameround,
resource=random_resource,
tag='tag newwww',
created=datetime.now(),
score=score,
origin=origin
)
tagging_serializer = TaggingSerializer(saved_tagging)
At the moment I am getting the ValueError: Cannot assign "'tag newwww'": "Tagging.tag" must be a "Tag" instance.
Is there any way that I can avoid this?
Here are also my models and the relevant serializer.
models.py
class Tag(models.Model):
name = models.CharField(max_length=256)
language = models.CharField(max_length=256)
objects = models.Manager()
def create(self, validated_data):
tag_data = validated_data.pop('tag')
Tag.objects.create(**tag_data)
return tag_data
def __str__(self):
return self.name or ''
class Tagging(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
tag = models.ForeignKey(Tag, on_delete=models.CASCADE, related_name='tagging')
created = models.DateTimeField(editable=False)
score = models.PositiveIntegerField(default=0)
origin = models.URLField(max_length=256, blank=True, default='')
objects = models.Manager()
def create(self, validated_data):
tag_data = validated_data.pop('tag')
tagging = Tagging.objects.create(**validated_data)
Tag.objects.create(name=tagging, **tag_data)
return tagging
def __str__(self):
return str(self.tag) or ''
serializers.py
class TaggingSerializer(serializers.ModelSerializer):
tag = StringRelatedField()
resource = ResourceSerializer(read_only=True)
gameround = GameroundSerializer(read_only=True)
class Meta:
model = Tagging
fields = ('id', 'tag', 'gameround', 'created', 'score', 'resource', 'origin')
def create(self, validated_data):
return Tagging.objects.create(**validated_data)
def to_representation(self, data):
data = super().to_representation(data)
return data
tag must be a Tag Instance !
So... you can do this in two ways (in my opinion).
First you can create a Tag object in your view and than pass this object to tag value in your Tagging create method.
Or create a service layer on your app, and create a custom create method for your model. This in my opinion is superior because your a centralizing your rules in one method instead of in one view.
Ex.:
services/tag_service.py
def create(user_id,gameround,resource,tag,...origin):
if not isinstance(tag, Tag):
#create your tag model based on the String passed
your_new_tag_object = Tag.objects.create(tag=tag, ...)
# Here your create others rules too for model creation
# Return your model .create method
return Tagging.objects.create(user_id=user_id,...tag=your_new_tag_object,...)
And Than use this new create method inside your POST serializer.
from services import tag_service
class TaggingSerializer(serializers.ModelSerializer):
# your normal serializer here
def create(self, validated_data):
return tag_service.create(**validated_data)
This is the original method but I want to call it with super and ad my context to it, but it's old API and I'm kinda confused here.
After move_scrap write method should be called, but nothing happens and write is not called
and with_context of course not working
class stock_move_scrap(osv.osv_memory):
_name = "stock.move.scrap"
_description = "Scrap Products"
def move_scrap(self, cr, uid, ids, context=None):
""" To move scrapped products
#param self: The object pointer.
#param cr: A database cursor
#param uid: ID of the user currently logged in
#param ids: the ID or list of IDs if we want more than one
#param context: A standard dictionary
#return:
"""
if context is None:
context = {}
move_obj = self.pool.get('stock.move')
move_ids = context['active_ids']
for data in self.browse(cr, uid, ids):
move_obj.action_scrap(cr, uid, move_ids,
data.product_qty, data.location_id.id, restrict_lot_id=data.restrict_lot_id.id,
context=context)
if context.get('active_id'):
move = self.pool.get('stock.move').browse(cr, uid, context['active_id'], context=context)
if move.picking_id:
return {
'view_type': 'form',
'view_mode': 'form',
'res_model': 'stock.picking',
'type': 'ir.actions.act_window',
'res_id': move.picking_id.id,
'context': context
}
return {'type': 'ir.actions.act_window_close'}
i did try something like this
def move_scrap(self, cr, uid, ids, context=None):
if context is None:
context = {}
ctx = context.copy()
ctx['allow_scrap'] = True
super(stock_move_scrap,self).move_scrap(cr, uid, [], context=ctx)
Your issue is that you are removing the ids from the super call by replacing the expected ids arg with [].
Change you last line from:
super(stock_move_scrap,self).move_scrap(cr, uid, [], context=ctx)
to:
super(stock_move_scrap,self).move_scrap(cr, uid, ids, context=ctx)
I am trying to reference form_serial_no field of formdownload model in my inherited model(plot.allocate). Instead of the dropdown to show the computed value of form_serial_no which is company_short_code padded with sequence e.g CCK0000006, it's showing "CCK" which is the value of company_short_code excluding the sequence. In actual fact, it's referencing company_short_code of companyname model instead of the computed value of form_serial_no of formdownload model. And in the database, form_serial_no field is showing the right record which is something like these......ABC0000008, CDG0000003 and so on. This is my question, is there any reason why form_serial_no_id Many2one field of plot.allocate model is not picking the value of form_serial_no Char field of formdownload, instead, it's picking the company_short_code Char field of companyname model?
Secondly, how can i change the status field default value of companyname model record to true when a value is picked in form_serial_no_id Many2one field of plot.allocate model?
Kindly help me look into these.
Below are my code snippet
# -*- coding: utf-8 -*-
from openerp import models, fields, api
class CompanyName(models.Model):
_name = 'companyname'
_rec_name = 'company_short_code'
company_name = fields.Char(string="Company Name", required=True)
company_short_code = fields.Char(string="Company short code", required=True)
class FormDownload(models.Model):
_name = 'formdownload'
_rec_name = 'form_serial_no'
name = fields.Many2one('companyname', string="Company Name", ondelete='cascade',
required=True)
form_serial_no = fields.Char(string="Form Serial No", readonly=True)
status = fields.Boolean(string="Status", default=False)
#api.model
def create(self, vals):
serial_no = self.env['ir.sequence'].get('formdownload.form_serial_no')
code = self.env['companyname'].browse(vals['name']).company_short_code
# capitalize company short code values
code = code.upper()
# merge code and serial number
vals['form_serial_no'] = code + serial_no
return super(FormDownload, self).create(vals)
class PlotAllocation(models.Model):
_inherit = 'plot.allocate'
form_serial_no_id = fields.Many2one('formdownload',
domain=[('status', '=', False)],
ondelete='cascade', string="Form Serial No")
#api.model
def create(self, vals):
record = self.env['formdownload'].search([('name', 'like', vals.get('name', False))]).form_serial_no
status = self.env['companyname'].search([('name', 'like', vals.get('name', False))]).status
if vals.get('form_serial_no_id') == record.id:
self.env['companyname'].write({status : True})
return super(PlotAllocation, self).create(vals)
in the view the many2one is always showing the value of the field specified in the Model class by _rec_name :
by default _rec_name = 'name'
so in reality your model is like this :
class FormDownload(models.Model):
_name = 'formdownload'
_rec_name = 'name'
so he will retreive the value of field name name is also a many2one so the value retrieved is the _rec_name of model companyname with is company_short_code:
so i you want to change the value of many2one you have to choice:
if one field is enough than specify the _rec_name = 'form_serial_no'
if you want to combined value like first && last name then override name_get method
#api.multi
def name_get(self):
"""
change the displayed value on m2o
"""
result = []
for record in self:
result.append((record.id, _(" %s %s") % (record.first_name, record.last_name)))
return result
EDITS
in create method is easy just remove all the code in create method and put this two line
rec_id = super(PlotAllocation, self).create(vals)
# create return a record you can acces it's value
rec_id.form_serial_no_id.name.status = True
return rec_id # you need to return the record
I have created a custom header menu called 'HM-1'
And i have created a Menu called 'Menu 1'
Inside 'Menu 1' i have 3 fields created, called field_1 fileds_2 filed_3.
I created a 'User' and i have two users now
1. Admin
2. User
My question is 'How to make one field(field_3) 'User' and 'Admin' editable in Openerp-7'
Remaining fields in 'User' should be readonly only field_3 should be editable.
How to do this?
The first thing that come into my mind is to override fields_view_get and change the readonly and modifiers attribute of field_1 and field_2 based on groups of self.env.user. Of course you need to assign User to a specific group, different to the one of Admin.
class example_class(models.Model):
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
res = super(example_class, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
group = self.pool['ir.model.data'].xmlid_to_object(cr, uid, 'your_group', raise_if_not_found=True, context=context)
if view_type == 'form' and group in self.env.user.groups_id:
doc = etree.XML(res['arch'])
#this for cycle must be repeated for each field you need to make readonly
for node in doc.xpath("//field[#name='field_2']"):
if 'modifiers' in node.attrib:
text = node.attrib['modifiers']
j = json.loads(text)
j['readonly'] = '1'
else:
j = {'readonly': '1'}
node.attrib['modifiers'] = json.dumps(j)
res['arch'] = etree.tostring(doc)
return res
In Odoo/Openerp i created a kanban view to display some data. Now i managed to change this view with the fields_view_get based on context data.
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
if context is None:
context = {}
res = super(esfyt_subscription_template,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
if context.get('partner_id', False):
partner_id = context.get('partner_id', False)
doc = etree.XML(res['arch'])
if view_type == 'kanban':
for node in doc.xpath("//div[#class='oe_module_vignette']"):
new_addition = etree.SubElement(node, 'button', {'string' : 'Subscribe', 'type' : 'object', 'name' : 'action_subscribe'})
new_addition.text = 'Subscribe'
res['arch'] = etree.tostring(doc)
return res
But i need to do the same with the data. i need to limit some data based on context, o certain models(or rows) arent loaded in the view.
How can i do that?
To filter records based on the context, you have to override the search() method
To filter data based on the context, you have to override the read()/browse() methods