How can I set the attribute of a field to required=True or required=False based on the value of another field? - odoo-8

I have a selection field in the res.partner model which is employmentstatus and the options there are employed
or unemployed. I want another field employmenttype have the attribute required=True if the employmentstatus='employed'
or required=False if the employmentstatus='unemployed'. The field right now sets required to True whether Partner is
employed or not (See attached image here).
Here is my code:
from openerp.osv import osv, fields
from openerp import tools
class custom_fields_partner(osv.Model):
_inherit = 'res.partner'
_columns = {
'employmentstatus' : fields.selection([
('employed','Employed'),
('unemployed','Unemployed')
],'Employment status', required=True, default='unemployed'),
'employmenttype' : fields.selection([
('0','Public'),
('1','Private'),
('2','Mission')],'Nature of employment', required="fieldproperty"),
}
#api.one
def fieldproperty(self):
if self.employmentstatus == 'employed':
return True
else:
return False

The required attribute is expected to be stored in the database and is not meant to be calculated on the fly. Best bet is to do it client side. If you look in the model ir.model.fields you will notice the the required field is stored in the db and is not meant to be computed.
In your xml use the attrs attribute. Here is an example.
<field name="field_name" attrs="{'required':[('other_field','=','other_value')]}"/>
So in this example the field called field_name is required only if the field other_field has a value of other_value but your can create a domain criteria that is more complex or less complex depending on your needs.
The field other_field mush be present in your view in order for this to work because the evaluation takes place client side. If you need to include a field for evaluation but do not want to display it you can make it invisible. Like this.
<field name="other_field" invisible="1"/>

Related

Django rest framework mongoengine update new field with default value

I'm using Django rest framework mongoengine after created few documents, if i want add a new field with default value. Is there any way to do that orelse i need to update with few custom function.
Note: I want to fetch the data with filter having a new field name. That time the field is not there. So i'm getting empty.
From what I understand, you are modifying a MongoEngine model (adding a field with a default value) after documents were inserted. And you are having issue when filtering your collection on that new field.
Basically you have the following confusing situation:
from mongoengine import *
conn = connect()
conn.test.test_person.insert({'age': 5}) # Simulate an old object
class TestPerson(Document):
name = StringField(default='John') # the new field
age = IntField()
person = TestPerson.objects().first()
assert person.name == "John"
assert Test.objects(name='John').count() == 0
In fact, MongoEngine dynamically applies the default value when the field of the underlying pymongo document is empty but it doesn't account for that when filtering.
The only reliable way to guarantee that filtering will work is to migrate your existing documents.
If its only adding a field with a default value, you could do this with MongoEngine: TestPerson.objects().update(name='John')
If you did more important/complicated changes to your document structure, then the best option is to get down to pymongo.
coll = TestPerson._get_collection()
coll.update({}, {'$set': {'name': 'John'}})

Django REST Framework - access verbose_name of fields in ModelSerializer

Say I have the following Model:
class Book(Model):
title = CharField(verbose_name="Book title")
and a ModelSerializer:
class BookSerializer(ModelSerializer):
class Meta:
model = Book
fields = "__all__"
I would like to have a function get_verbose_names which returns verbose names of the fields in the model. This is what I have so far:
def get_verbose_names(serializer):
return [field.label for field in serializer.get_fields().values()]
It seems to work fine but problems occur when I use this for the builtin User model. The only fields which work are ID, E-mail, Active, Superuser status and Staff status. The special thing about those fields is that their verbose name differs from their name. Django REST Framework is probably hiding a super-smart logic which checks this and refuses to set the field label to its verbose name in such cases.
Do Django REST Framework's fields have the verbose names hidden somewhere, or they don't copy them from the original Django model fields at all and I am screwed? Or will the trick be to override this logic? I tried and could not find it.
Django REST Framework really has the mentioned "super-smart logic". It is the function needs_label in utils.field_mapping:
def needs_label(model_field, field_name):
"""
Returns `True` if the label based on the model's verbose name
is not equal to the default label it would have based on it's field name.
"""
default_label = field_name.replace('_', ' ').capitalize()
return capfirst(model_field.verbose_name) != default_label
Probably the easiest way to bypass this annoying feature is to do this:
def get_verbose_names(serializer):
return [field.label or name.replace("_", " ").capitalize()
for name, field in serializer.get_fields().items()]
Explained in words, check the field label and if none was auto-generated for it, use the needs_label logic to determine it.

How to search records with a string which contains some characters of the target field string in Odoo v10?

I am using Odoo v10. While scanning a barcode, a string contains some characters of a char field value. For example,
A field value ('tracknum') = "20171103"
Search the field by entering a string "xxxxxx20171103" or "xxxx20171103yyy"
is there any way to do it?
I have modified the search view :
<field name="tracknum" string="Tracknum" filter_domain="..."/>
How to dig out related records?
You can create an auxiliar computed field like this
custom_name = fields.Char(
string='Custom',
compute='_compute_custom_name',
search='_search_custom_name'
)
#api.multi
#api.depends()
def _compute_custom_name(self):
''' The field has to be a computed field
You do not need to do anything here
'''
pass
def _search_custom_name(self, operator, value):
''' Actually this converts a domain into another one.
With this new domain Odoo can search well
Arguments:
* operator: if you are searchig words it is going to be ilike
* value: the string ro search
The method could return something like this
* [('id', 'in', id_list)]
'''
all_records = self.search([]) # recordset with all the values of the current model
ids = []
if operator == 'ilike':
ids = all_records.filtered(lambda r: r.tracknum in value).mapped('id')
return [('id', 'in', ids)]
Then you can add this field to the search view like this:
<field name="custom_name" string="Tracking Number" />
Keep in mind that it is not a stored field, so it is going to be very inefficient. And you should iterate over all the values each time you want to make a search.
Once you have added the field to the search view it shoul look like this, Tracking Number should appear in the field name

Odoo 10 - Duplicate supplier info for a given product_template

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)

Django Rest Framework "This field is required" only when POSTing JSON, not when POSTing form content

I'm getting a strange result whereby POSTing JSON to a DRF endpoint returns:
{"photos":["This field is required."],"tags":["This field is required."]}'
Whereas when POSTing form data DRF doesn't mind that the fields are empty.
My model is:
class Story(CommonInfo):
user = models.ForeignKey(User)
text = models.TextField(max_length=5000,blank=True)
feature = models.ForeignKey("Feature", blank=True, null=True)
tags = models.ManyToManyField("Tag")
My serializer is:
class StorySerializer(serializers.HyperlinkedModelSerializer):
user = serializers.CharField(read_only=True)
def get_fields(self, *args, **kwargs):
user = self.context['request'].user
fields = super(StorySerializer, self).get_fields(*args, **kwargs)
fields['feature'].queryset = fields['feature'].queryset.filter(user=user)
fields['photos'].child_relation.queryset = fields['photos'].child_relation.queryset.filter(user=user)
return fields
class Meta:
model = Story
fields = ('url', 'user', 'text', 'photos', 'feature', 'tags')
And my api.py is:
class StoryViewSet(viewsets.ModelViewSet):
serializer_class = StorySerializer
def get_queryset(self):
return self.request.user.story_set.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
The results:
# JSON request doesn't work
IN: requests.post("http://localhost:8001/api/stories/",
auth=("user", "password",),
data=json.dumps({'text': 'NEW ONE!'}),
headers={'Content-type': 'application/json'}
).content
OUT: '{"photos":["This field is required."],"tags":["This field is required."]}'
# Form data request does work
IN: requests.post("http://localhost:8001/api/stories/",
auth=("user", "password",),
data={'text': 'NEW ONE!'},
).content
OUT: '{"url":"http://localhost:8001/api/stories/277/","user":"user","text":"NEW ONE!","photos":[],"feature":null,"tags":[]}'
The issue here isn't obvious at first, but it has to do with a shortcoming in form-data and how partial data is handled.
Form data has two special cases that Django REST framework has to handle
There is no concept of "null" or "empty" data for some inputs, including checkboxes and other inputs that allow for multiple selections.
There is no input type that supports multiple values for a single field, checkboxes being the one exception.
Both of these combine together to make it difficult to handle accepting form data within Django REST framework, so it has to handle a few things differently from most parsers.
If a field is not passed in, it is assumed to be None or the default value for the field. This is because inputs with no values are not passed along in the form data, so their key is missing.
If a single value is passed in for a multiple-value field, it will be treated like the one selected value. This is because there is no difference between a single checkbox selected out of many and a single checkbox at all in form data. Both of them are passed in as a single key.
But the same doesn't apply to JSON. Because you are not passing an empty list in for the photos and tags keys, DRF does not know what to give it for a default value and does not pass it along to the serializer. Because of this, the serializer sees that there is nothing passed in and triggers the validation error because the required field was not provided.
So the solution is to always provide all keys when using JSON (not including PATCH requests, which can be partial), even if they contain no data.

Resources