I have created a new field in sale.order.line
_columns = {
'od_deivered_quantity':fields.float('Delivered
Quantity',trackvisibility = onchange,readonly = False)
}
then I wrote the following onchange function:
#api.depends('product_uom_qty')
def onchange_delivered_order(self, cr, uid, ids,context=None):
res = {}
delivered_qty = self.product_uom_qty
return {'value':{'od_delivered_qty':delivered_qty}}
XML code as follows:
<xpath expr="//field[#name='order_line']/tree//field[#name='product_uom_qty']" position="after">
<field name = "od_delivered_qty"/>
</xpath>
but it does not works
Sorry but your code is a bit messy and buggy; furthermore it looks like you're mixing up old API style (pre-v8), as during fields declaration, and new one, for onchange method.
Let's recap, I wonder whether I understood well your requirements: you need a new field od_deivered_quantity being triggered by product_uom_qty. Is that right?
I suggest something like the following chunks (I'm gonna use new API style):
od_deivered_quantity = fields.Float(
"Delivered Quantity", track_visibility="onchange", readonly=False
)
#api.onchange('product_uom_qty')
def onchange_delivered_order(self):
# if isinstance(self.product_uom_qty, bool):
# return
delivered_qty = self.product_uom_qty
self.od_delivered_qty = delivered_qty
Please, try to inspect to this onchange method behaviour when it'll be up and running in your module: I put a commented boolean check into that, in case (because of any awkward reason) the method will be called passing a False value on product_uom_qty.
Talking about views, your XML may be OK because in new API style https://www.odoo.com/documentation/8.0/reference/orm.html#onchange-updating-ui-on-the-fly
both computed fields and new-API onchanges are automatically called by
the client without having to add them in views
Let me know whether it will work fine or you'll face any problems.
Related
I have a view which allows me to post multiple entries to a model. Currently if I add all new entries, they are added successfully. But if I add any entry for which the pk already exists, it naturally throws a serializer error and nothing gets updated.
I wish to write a method which will let me post multiple entries, but automatically either update an existing one OR add a new one successfully depending the existence of that instance.
The idea of a customized ListSerialzer is the closest thing I came across to achieve this but still not clear if I can do this.
Has anyone ever implemented anything like this ?
In views.py:
def post(self,request,format=None):
data = JSONParser().parse(request)
serializer = PanelSerializer(data=data,many=True)
if serializer.is_valid():
serializer.save()
return JsonResponse({"success":"true","content":serializer.data}, status=201)
return JsonResponse({'success': "false",'errorCode':"1",'errorMessage':serializer.errors}, status=400)
in serializers.py:
class PanelSerializer(serializers.ModelSerializer):
class Meta:
model = Panel
fields = ('pId','pGuid','pName', 'pVoltage', 'pAmperage','pPermission', 'pPresent', 'pSelf','pInfo')
def create(self, validated_data):
logger.info('Information incoming_1!')
print ("Atom")
return Panel.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.pId = validated_data.get('pId', instance.pId)
instance.pGuid = validated_data.get('pId', instance.pGuid)
instance.pName = validated_data.get('pName', instance.pName)
instance.pVoltage = validated_data.get('pVoltage', instance.pVoltage)
instance.pAmperage = validated_data.get('pAmperage', instance.pAmperage)
instance.pPermission = validated_data.get('pPermission', instance.pPermission)
instance.pPresent = validated_data.get('pPresent', instance.pPresent)
instance.pSelf = validated_data.get('pSelf', instance.pSelf)
instance.pInfo = validated_data.get('pInfo', instance.pInfo)
instance.save()
return instance
This is how the code stands as of now. I believe I will mainly need to either work on the update method of my serializer or first change it to a ListSerializer and write some custom logic again in the update method.
I would like to update every record when it is shown in form view. Is there anyway to define some sort of a callback to update the record BEFORE it is shown?
One method that I could think of is by adding a dummy field to form view with an on_change attribute. But it feels kinda hacky.
You can use function field. Function is called when the form loading and at the time of new record creation before and after.
When you create a new record this function field will take value as a William and after saving value will change and become odedra
Here is example of char return type of function :
def _default_get(self, cr, uid, context=None):
print " This function called before new record create "
res = 'William'
return res
def _set_value(self, cr, uid, ids, name, args, context=None):
print " This function called at time of saving record and form view load "
res = {}
for i in self.browse(cr, uid, ids, context=context):
res[i.id] = 'odedra'
return res
_columns = {
'value': fields.function(_set_value, type='char', string='Value'),
}
_defaults = {
'value': _default_get,
}
NOTE:
As per your requirement, you may change function field return type.
Using a functional field seems odd but then making a read not idempotent is also unusual.
I would set a context in the window action of your form so you can test for it and limit the effect of this and then override the default_get method for new records and the read method for existing records on your model.
Its posible change model name in post/get?
I have model with large names, like "VerLargeModelName" and many parameters.
It does not fit in GET (query string limit).
Update:
i need just change generated inputs from CActiveForm (change LongModelName[a] to short[a])
You can just change the name. You can do this like this echo $form->textFieldBlock($model,'name',array('name' => 'x["name"]') or whatever you want. You could also create a class (widget) with does this for your.
class MyActiveForm extends CActiveForm {
public function hiddenField($model, $attribute, $htmlOptions = array()) {
if(isset($htmlOptions['shortName'])) {
$htmlOptions['name'] = $htmlOptions['shortName'] . "[".$attribute."]";
unset($htmlOptions['shortName']);
}
return parent::hiddenField($model, $attribute, $htmlOptions);
}
}
You change CActiveFrom from the widget to MyActiveForm. Then use $form->textFieldBlock($model,'name',array('shortName' => 'x'). You could also change the above code to always change to a shortname without the htmlOptions. So that it is always x. However you could not have two form at once in this case. Benifit is that you would not need to add array('shortName' => 'x') to all of them, but just change CActiveFrom to MyActiveForm. So that would save you time, but cost your flexibility (with you might need later on maybe).
You have to create a function offcourse for every input field you want to use from ActiveRecord. The name of the element would become x['name']
In the controller you could simply do $model->attributes = $_POST['x'].
I am new at django and autocomplete-light. I try to get a different fields of the model from autocomplete-light, but it always return the same field. And the reason is because def in the Model defined one field. So I created another def, but can not make autocomplete-light to call that specific def. Here is my code.
models.py:
class Item(models.Model):
...
serial_number=models.CharField(max_length=100, unique=True)
barcode=models.CharField(max_length=25, unique=True)
def __unicode__(self):
return self.serial_number
def bar(self):
return self.barcode
.......
autocomplete_light_registry.py
autocomplete_light.register(Item,
name='AutocompleteItemserial',
search_fields=['serial_number'],
)
autocomplete_light.register(Item,
name='AutocompleteItembarcode',
search_fields=['barcode'],
)
Here is the issue: when I try to get the barcodes from the autocomplete-light, it returns serial_numbers. No matter what I try to get from the Item model, it always returns the serial number. I really appreciate for the answers. Thank you.
Just in case, here is the form.py
forms.py
class ItemForm(forms.ModelForm):
widgets = {
'serial_number': autocomplete_light.TextWidget('AutocompleteItemserial'),
'barcode': autocomplete_light.TextWidget('AutocompleteItembarcode'),
}
Although this is an old post but as I just faced the same issue therefore I am sharing my solution.
The reason autocomplete is returning serial_number is because django-autocomplete-light uses the __unicode__ method of the model to show the results. In your AutocompleteItembarcode all that is being done is autocomplete-light is searching by barcode field of Item.
Try the following.
In app/autocomplete_light_registry.py
from django.utils.encoding import force_text
class ItemAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['serial_number']
model = Item
choices = Item.objects.all()
def choice_label(self, choice):
"""
Return the human-readable representation of a choice.
"""
barcode = Item.objects.get(pk=self.choice_value(choice)).barcode
return force_text(barcode)
autocomplete_light.register(ItemAutocomplete)
For more help you can have a look at the source code.
I am trying to filter purchase orders based on a property of its line items. So, for example, I would like to see all purchase orders who have any line item with product_type = 'X'.
<record id="po_x" model="ir.actions.act_window">
<field name="name">X Purchase Orders</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.order</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,graph,gantt</field>
<field name="domain">[('has_x_line','=',True)]</field>
<field name="context">{}</field>
</record>
product_type is a custom field that I added to an inherited version of order line.
class purchase_order_custom(osv.osv):
_name = 'purchase.order'
_inherit = 'purchase.order'
def linehasproducttype(self, cr, uid, ids, name, arg, context=None):
cur_obj={}
cur_obj=self.browse(cr, uid, ids, context=context)
for line in cur_obj.order_line:
if line.product_type == 'X':
return True
return False
_columns={
'has_x_line': fields.function(linehasproducttype, string="Has X Line", type='boolean', context={'pt':'X'})
}
purchase_order_custom()
2 questions:
Q1. The above code is not working. That is, it is not really filtering as desired. What could be wrong?
Q2. I have many product types and I am not inclined to make a function for each type. Can I pass an argument somehow or use the context for this purpose as I am trying to do? If so, how do I use it in the code?
Thanks
EDIT: I have also tried the following code with no luck:
def linehasproducttype(self, cr, uid, ids, name, arg, context=None):
res = dict.fromkeys(ids, False)
for cur_obj in self.browse(cr, uid, ids, context=context):
res[cur_obj.id] = False
for line in cur_obj.order_line:
if line.product_type == 'X':
res[cur_obj.id] = True
return res
You have one possibly two problems in your code:
If this code is intended to run on OpenERP 6.1 or prior, you need to provide the argument "method" in your field definition and set it to true, else your method won't be found.
Your function is not generic. It needs to return a dictionary, with the ids given to your function in the "ids" param as keys. You have to remember that almost every function you pass to the OpenERP core needs to work on a set of records and not a single record. Look at the core code for examples.
Regarding your second question, you can always add data to the context in your view XML which should then be available in the code handling these views. This will only work if you have distinct views for every product type.
Another possibility would be to define a generic tree view for all products and define a filter, which will only show the desired products. The developer book describes how to do that.
Edit: You should be able to just use a domain expression like the following in your action or filter:
<field name="domain">[('order_line.product_type','=','X')]</field>