How to use related fields (fields.related) in odoo-8? - python-2.7

I am trying to retrieve comment field(customer internal notes) from res_partner to account invoice module.Right now I just want to print it later I will include it in xml code. I tried in three ways like this,
1)comment2 = fields.Char(string='Comment',related='res_partner.comment',compute='_compute_com')
#api.multi
def _compute_com(self):
print self.comment2
2)comment = fields.Many2one('res.partner','Comment',compute='_compute_com')
#api.multi
def _compute_com(self):
print self.comment
3)partner_comment = fields.Char(compute='_compute_com')
#api.multi
def _compute_com(self):
Comment = self.env['res.partner'].browse(partner_id).comment
print Comment

You should use a related field instead:
comment = fields.Char(related='partner_id.comment')
If you need to store it in your account_invoice record you also need to add the parameter store=True
Problem is, this way you can't just print it but if you need to show it you need to put it into your view.
If you really need to print it temporarly you need to do this other way:
comment = fields.Char(compute='_compute_comment')
def _compute_comment(self):
for record in self:
record.comment = partner_id.comment
print record.comment

Related Field
There is not anymore fields.related fields.
Instead you just set the name argument related to your model:
participant_nick = fields.Char(string='Nick name',
related='partner_id.name')
The type kwarg is not needed anymore.
Setting the store kwarg will automatically store the value in database. With new API the value of the related field will be automatically updated, sweet.
participant_nick = fields.Char(string='Nick name',
store=True,
related='partner_id.name')
Note
When updating any related field not all translations of related field
are translated if field is stored!!
Chained related fields modification will trigger invalidation of the cache for all elements of the chain.

in odoo8
if need same object fields to related then you can use related="related field name " use store=True
comment2 = fields.Char(string='comment',related='comment', store=True)
LINK

Be careful, you have to use the same kind of field !!
(I had the problem with Selection and Char ... so I have to use Selection with Selection)

Related

Django: How to filter on inner join based on properties of second table?

My question is simple: in a Django app, I have a table Users and a table StatusUpdates. In the StatusUpdates table, I have a column user which is a foreign key pointing back to Users. How can I do a search expressing something like:
users.filter(latest_status_update.text__contains='Hello')
Edit:
Please excuse my lack of clarity. The query that I would like to make is something like "Give me all the users whose latest status update contains the text 'hello'". In Django code, I would do the following (which is really inefficient and ugly):
hello_users = []
for user in User.objects.all():
latest_status_update = StatusUpdate.objects.filter(user=user).order_by('-creation_date')[0]
if latest_status_update.text.contains('Hello'):
hello_users.append(user)
return hello_users
Edit 2:
I've already found the solution but since I was asked, here are the important parts of my models:
class User(models.Model):
...
class StatusUpdate(models.Model):
user = models.ForeignKey(User)
text = models.CharField(max_length=140)
creation_date = models.DateTimeField(auto_now_add=True, editable=False)
....
Okay, I think I got it:
from django.db.models import Max, F
User.objects\
.annotate(latest_status_update_id=Max('statusupdate__id'))\
.filter(
statusupdate__id=F('latest_status_update_id'),
statusupdate__text__icontains='hello'
)
For more info, see this section of the Django documentation.
Please note: I ended up changing my strategy a bit and settling for the strategy where the highest ID means the latest update. This is the case because I realized that a User could post two updates the same time and that would break my query.
latest_status_updates = filter(lambda x: x.text.contains('hello'),
[
user.statusupdates_set.order_by('-creation_date').first()
for user in User.objects.all()
]
)
users = list(set([status_update.user for status_update in latest_status_updates]))
EDIT:
Now I first get all LATEST status updates of each user into a list which is then filtered by the text field found in StatusUpdate class. In the second line, I extract users out of the filtered status updates and then produce a unique list of users.
I hope this helps!
Not sure I understand, are you trying to do something like
(StatusUpdates
.objects
.select_related("user")
.filter(text__contains = "hello")
.order_by("-updated")
.first())
This will return the StatusUpdate that was modified last (if you have a field called updated that stores the time of the last modification) which contains "Hello" in the text field. If none of the StatusUpdates contains that string, it will return None.
Then you can do:
latest = (StatusUpdates
.objects
.select_related("user")
.filter(text__contains = "hello")
.order_by("-updated")
.first())
#then if you needed the user too
if latest is not None:
user = latest.user #which does not call the DB again since you selected related`
If this isn't what you needed, please provide more details (models) and clarify your need

Django validate data when updating model with primary key

I am having trouble with updating fields of a model instance. The model is as follows:
class commonInfo(models.Model):
mothers_id = models.IntegerField(primary_key=True)
date = models.DateField()
data_collector = models.CharField(max_length=50)
Essentially, I just want to do this, but it won't work because commonInfo has a user defined primary key
commonInfo_form(request.POST or None).is_valid()
Since I am updating, I am overriding date and data_collector, but not mothers_id. So I would want to do something like this, but this specific code is not working
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST)
date = form.cleaned_data['data_collector'] #this line is not working
data_collector = form.cleaned_data['data_collector'] #this line is not working
obj.update(**{'date':date, 'data_collector':data_collector})
any ideas? I feel like it is just those two lines that I need to fix. Or if there is a more pythonic way or built method in Django?
Just validate with isinstance. so like,
if isinstance(request.POST['date'], datetime.date) and isinstance(request.POST['data_collector'], str):
# you might have to use getattr for request.POST here, I'm not sure
# and request.POST['date'] would have to be converted from a string to datetime.date I think
date = request.POST['date']
data_collector = request.POST['data_collector']
obj.update(**{'date':date, 'data_collector':data_collector})
The process for adding a record from a form is different from updating an existing instance. All you need to do differently is indicate which instance to bind the form to when you create it, ex:
obj = commonInfo.objects.get(pk=commonInfo_id)
form = commonInfo_form(request.POST, instance=obj)

Django: Adding objects to a related set without saving to DB

I'm trying to write an internal API in my application without necessarily coupling it with the database.
class Product(models.Model):
name=models.CharField(max_length=4000)
price=models.IntegerField(default=-1)
currency=models.CharField(max_length=3, default='INR')
class Image(models.Model):
# NOTE -- Have changed the table name to products_images
width=models.IntegerField(default=-1)
height=models.IntegerField(default=-1)
url=models.URLField(max_length=1000, verify_exists=False)
product=models.ForeignKey(Product)
def create_product:
p=Product()
i=Image(height=100, widght=100, url='http://something/something')
p.image_set.add(i)
return p
Now, when I call create_product() Django throws up an error:
IntegrityError: products_images.product_id may not be NULL
However, if I call p.save() & i.save() before calling p.image_set.add(i) it works. Is there any way that I can add objects to a related object set without saving both to the DB first?
def create_product():
product_obj = Product.objects.create(name='Foobar')
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something', product=product_obj)
return product_obj
Explanation:
Product object has to be created first and then assign it to the Image object because id and name here is required field.
I am wondering why wouldn't you not require to make a product entry in DB in first case? If there is any specific reason then i may suggest you some work around?
EDIT: Okay! i think i got you, you don't want to assign a product to an image object initially. How about creating a product field as null is equal to true.
product = models.ForeignKey(Product, null=True)
Now, your function becomes something like this:
def create_product():
image_obj = Image.objects.create(height=100, widght=100, url='http://something/something')
return image_obj
Hope it helps you?
I got same issue with #Saurabh Nanda
I am using Django 1.4.2. When I read in django, i see that
# file django/db/models/fields/related.py
def get_query_set(self):
try:
return self.instance._prefetched_objects_cache[rel_field.related_query_name()]
except (AttributeError, KeyError):
db = self._db or router.db_for_read(self.model, instance=self.instance)
return super(RelatedManager,self).get_query_set().using(db).filter(**self.core_filters)
# file django/db/models/query.py
qs = getattr(obj, attname).all()
qs._result_cache = vals
# We don't want the individual qs doing prefetch_related now, since we
# have merged this into the current work.
qs._prefetch_done = True
obj._prefetched_objects_cache[cache_name] = qs
That 's make sese, we only need to set property _prefetched_objects_cache for the object.
p = Product()
image_cached = []
for i in xrange(100):
image=Image(height=100, widght=100, url='http://something/something')
image_cached.append(image)
qs = p.images.all()
qs._result_cache = image_cached
qs._prefetch_done = True
p._prefetched_objects_cache = {'images': qs}
Your problem is that the id isn't set by django, but by the database (it's represented in the database by an auto-incremented field), so until it's saved there's no id. More about this in the documentation.
I can think of three possible solutions:
Set a different field of your Image model as the primary key (documented here).
Set a different field of your Production model as the foreign key (documented here).
Use django's database transactions API (documented here).

django request.POST field name substitution

I am using ajax to send data into a django view with data coming in via request.POST. I am posting the model field that needs to be updated as well as the model value. I just need to know how to use the field name variable I extract from request.POST['field_name'] so I can set the field in the model. Here is my code.
field_name = request.POST["field_name"]
field_value = request.POST["field_value"]
member_id = get_member_session(request).id
try:
member = Members.objects.get(id=member_id)
except:
status="ERROR-USER-DOES-NOT-EXIST"
return json_status(status)
try:
member.field_name=field_value
member.save()
return json_status('OK')
except:
status = "USER_SAVE_ERROR"
return json_status(status)
member.field_name is obviously not right. Do I need to use eval(field_name) or something like that? I would prefer not to if possible.
Many thanks
Rich
Use setattr, which allows you to set a variable attribute on an object:
try:
member._meta.get_field(field_name)
except member.FieldDoesNotExist:
# return something to indicate the field doesn't exist
return json_status('USER_FIELD_ERROR')
setattr(member, field_name, field_value)
member.save()
return json_status('OK')
edit: I updated to use model._meta.get_field, as it's a better approach. Mentioned in this answer for another question.

Django get_FIELD_display

I am trying to access data.get_age_display in my email template. I can't seem to get the display of this. I am not sure what I am doing wrong, I've using get_FIELD_display numerous times before but passed as context to a normal template. Is there something different with forms?
class RequestForm(forms.Form):
ADULT = 1
SENIOR = 2
STUDENT = 3
AGE_GROUP = (
(ADULT, 'Adult'),
(SENIOR, 'Senior'),
(STUDENT, 'Student'),
)
name = forms.CharField(max_length=255)
phone = forms.CharField(max_length=15)
age = forms.ChoiceField(choices=AGE_GROUP)
details = forms.CharField(widget=forms.Textarea())
def save(self):
order = Order(
name = self.cleaned_data['name'],
phone = self.cleaned_data['phone'],
age = self.cleaned_data['age'],
details = self.cleaned_data['details'],
)
order.save()
template = loader.get_template('request_email.txt')
# send over the order object in an email extracted so they can handle the ticket order
context = Context({
'data': order,
})
#import pdb; pdb.set_trace()
email_subject = 'Request Tickets'
mail_managers(email_subject, template.render(context))
in my request_email.txt all I am doing is {{ data.get_age_display }} any ideas?
Jeff
You haven't shown the code for the Order model that you're creating. Are you sure that the age field on the model has choices set?
Any reason you're not using a ModelForm? You're creating an Order object within the form's save() method, but not returning it. A modelform would do that for you, as well as removing the need to redeclare the fields for the form.
I know this is coming WAAAAAY later than the question being posted but here's my answer for completeness and anyone else who might benefit from it :-)
I'm going to assume that in AGE_GROUP, ADULT, SENIOR and STUDENT are integers. Your form cleaning will NOT automatically clean the string contained in the POST and return an integer. So in this code:
context = Context({
'data': order,
})
you would think order.age is referring to an integer but that is, in fact, incorrect. It's burned me a few times before because this will correctly save the integer to the physical table, but the order instance still has the string representation of the age field.
You could do one of two things:
1. Clean this in the field:
clean_age(self):
return int(self.cleaned_data['age'])
or create a new field type:
def MyChoiceField(forms.ChoiceField):
def clean(self, value):
if not value:
if self.required:
raise forms.ValidationError(self.error_messages['required'])
return None
else:
return None
return int(value)
link that to the form field:
age = MyChoiceField(choices=AGE_GROUP)
and then you'll be able to apply this logic to any other such choice field in future. Personally, I find the latter approach the best one and I stick all my custom field types into a form_utils file so that I can use them everywhere. Another gotcha is that forms.charField doesn't automatically strip the entered text and you can use this approach to fix that too.