I have a one2many which stores some data.
In python, when I need to update the object with .write method; the new data is stored but the old stuff remain there.
How can I empty the many2many before using .write method ??
Maybe using .browse and .search ?? please help !!!
It would be great if you have posted some example of what you are trying to do. Any way, you have 2 solutions:
use unlink()
understand how the write() ORM method works on one2many fields.
Let take the example of account.invoice and account.invoice.line.
The first approach - unlink():
def delete_lines(self, cr, uid, ids, context=None):
invoice_pool = self.pool.get('account.invoice')
line_pool = self.pool.get('account.invoice.line')
for invoice in invoice_pool.browse(cr, uid, ids, context=context):
line_ids = [line.id for line in invoice.invoice_line]
line_pool.unlink(cr, uid, line_ids, context=context)
The second approach - write()
Looking at the OpenERP docs (https://doc.openerp.com/6.0/developer/2_5_Objects_Fields_Methods/methods/#osv.osv.osv.write):
write(cr, user, ids, vals, context=None)
...
Note: The type of field values to pass in vals for relationship fields is specific:
For a one2many field, a lits of tuples is expected. Here is the list of tuple that are accepted, with the corresponding semantics
(2, ID) remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
So for the vals parameter we need a list of tuples in the following format:
[
(2, line1_id),
(2, line2_id),
(2, line3_id),
...
]
The following code illustrates the use of the write() method.
def delete_lines(self, cr, uid, ids, context=None):
invoice_pool = self.pool.get('account.invoice')
for invoice in invoice_pool.browse(cr, uid, ids, context=context):
vals = [(2, line.id) for line in invoice.invoice_line]
invoice.write(vals)
I didn't test the examples so let me know if they do the job.
Here is how I solved it:
my_object = self.pool.get('my.main.object')
props = self.pool.get('table.related')
prop_id = props.search(cr, uid, [('id_1', '=', id_2)])
del_a = []
for p_id in prop_id:
del_a.append([2, p_id])
my_object.write(cr, uid, line_id, {'many2one_field': del_a}, context=context)
Where:
del_a.append([2, p_id]) creates the string of tuples with code "2" (delete)
and my_object is where I need to make the changes.
Related
I have a hash in which id is the key and name is the value. Both id and value are unique.
Something like this:
h[1] = "ABC"
h[3] = "DEF"
So, if I am given the key of 1, I can easily return a value "ABC".
I need to do a reverse lookup as well, which means if I am given a value of "DEF", I should return 3.
Also, instead of a single value or single key to do the lookup,
I may be provided with an array of values or array of keys instead.
Should I implement two hashes, one for each, or is there any other way in ruby or rails to achieve that?
Edit: This question is not related to finding a key by its value in a hash. It is related to doing a two way lookup not in O(n) time with a better method other than creating two separate hashes.
You can use Hash#invert as below,
reversed_h = h.invert
reversed_h['DEF']
# => 3
You can get your key this way:
hash.key(value) => key
Hash#key
h = { 1 => 'ABC', 3 => 'DEF' }
puts h.key('DEF')
#=> 3
So I have a model Item that has a huge postgresql JSON field called properties. Most of the time this field does not need to be queried on or changed, however we store price in this field.
I'm currently writing a script that updates this price, but there's only a few unique prices and thousands of Items so in order to save time I have a list of Items for each unique price and I'm attempting to do an update all:
Item.where(id: items).update_all("properties->>'price' = #{unique_price}")
But this gives me:
syntax error at or near "->>"
Is there a way to use update all to update a field in a postgres JSON field?
You need to use jsonb_set() function, here is an example:
Item.where(id: items).
update_all(
"properties = jsonb_set(properties, '{price}', to_json(#{unique_price}::int)::jsonb)"
)
This would preserve all values and update only one key.
Read documentation
You can also do this
Item.where(id: items).each do |item|
properties = item.properties
item.update(properties: properties.merge({
price: unique_price
}))
end
The keyword merge will override the value of the key provided with the new value ie unique_price
Merge documentation is here
What I came up with based on #Philidor's suggestion is very similar but with dynamic bindings:
assignment = ["field = jsonb_set(field, '{ name_of_the_key }', ?)", value.to_json]
scope.update_all(scope.model.sanitize_sql_for_assignment(assignment))
I need to find a way to display all Vacancies from my Vacancy model except the ones that a user already applied for.
I keep the IDs of the vacancies a certain user applied for in a seperate model AppliedVacancies.
I was thinking something line the lines of:
#applied = AppliedVacancies.where(employee_id: current_employee)
#appliedvacancies_id = []
#applied.each do |appliedvacancy|
#appliedvacancies_id << appliedvacancy.id
end
#notyetappliedvacancies = Vacancy.where("id != ?", #appliedvacancy_id)
But it does not seem to like getting an array of IDs. How would I go about fixing this?
I get following error:
PG::DatatypeMismatch: ERROR: argument of WHERE must be type boolean, not type record
LINE 1: SELECT "vacancies".* FROM "vacancies" WHERE (id != 13,14)
^
: SELECT "vacancies".* FROM "vacancies" WHERE (id != 13,14)
This is purely an SQL problem.
You cannot use != to compare a value to a set of values. You need to use the IN operator.
#notyetappliedvacancies = Vacancy.where("id NOT IN (?)", #appliedvacancy_id)
As an aside, you can drastically improve the code you've written so far. You are needlessly instantiating complete ActiveRecord models for every record found in your applied_vacancies table, when all you need are the IDs.
A first pass at improvement would be to use pluck to skip the entire process and go straight to the list of IDs:
ids = AppliedVacancies.where(employee_id: current_employee).pluck(:id)
#notyetappliedvacancies = Vacancy.where("id NOT IN (?)", ids)
Next, you can go a step further and eliminate the first query all together (or rather, combine it with the last query as a sub-query) by leaving it as an AREL projection which can be subbed into the second query directly:
ids = AppliedVacancies.select(:id).where(employee_id: current_employee)
#notyetappliedvacancies = Vacancy.where("id NOT IN (?)",App)
This will generate a single query:
select * from vacancies where id not in (select id from applied_vacancies where employee_id = <value>)
Answer like #meagar, but Rails 4 way:
#notyetappliedvacancies = Vacancy.where.not(id: #appliedvacancy_id)
Let's say I have a PairRDD, students (id, name). I would like to only keep rows where id is in another RDD, activeStudents (id).
The solution I have is to create a PairDD from activeStudents, (id, id), and the do a join with students.
Is there a more elegant way of doing this?
Thats a pretty good solution to start with. If active students is small enough you could collect the ids as a map and then filter with the id presence (this avoids having to a do a shuffle).
Much like you thought, you can do an outer join if both RDDs contain keys and values.
val students: RDD[(Long, String)]
val activeStudents: RDD[Long]
val activeMap: RDD[(Long, Unit)] = activeStudents.map(_ -> ())
val activeWithName: RDD[(Long, String)] =
students.leftOuterJoin(activeMap).flatMapValues {
case (name, Some(())) => Some(name)
case (name, None) => None
}
If you don't have to join those two data sets then you should definitely avoid it.
I had a similar problem recently and I successfully solved it using a broadcasted Set, which I used in UDF to check whether each RDD row (rather value from one of its columns) is in that Set. That UDF is than used as the basis for the filter transformation.
More here: whats-the-most-efficient-way-to-filter-a-dataframe.
Hope this helps. Ask if it's not clear.
I'm seeing some genuinely bizarre behavior w/ ActiveRecord as it relates to assignment. I have an ActiveRecord model named Venue that includes the measurements of the Venue, all integers less than 1K. We add Venues via an XML feed. On the model itself, I have a Venue.from_xml_feed method takes the XML, parses, and creates Venues.
The problem comes from the measurements. Using Nokogiri, I'm parsing out the measurements like so:
elems = xml.xpath("//*[#id]")
elems.each do |node|
distance = node.css("distances")
rs = distance.attr("rs")
// get the rest of the sides
# using new instead of create to print right_side, behavior is the same
venue = Venue.new right_side: rs # etc
venue.save
puts venue.right_side
end
The problem is that venue.right_side ALWAYS evaluates to nil, even though distance.attr("rs") contains a legal value, say 400. So this code:
rs = distance.attr("rs")
puts rs
Venue.new right_side: rs
Will print 400, then save rs as nil. If I try any type of Type Conversions, like so:
content = distance.attr("rs").content
str = content.to_s
int = Integer(str)
puts "Is int and Integer? #{int.is_a? Integer}"
Venue.new right_side: int
It will print Is int an Integer? true, then again save again save Venue.right_side as nil.
However, if I just explicitly create a random integer like so:
int = 400
Venue.new right_side: int
It will save Venue.right_side as 400. Can anyone tell me what's going on with this?
Well, you failed to include the prerequisite sample XML to confirm this, so you get a fairly generic answer.
In your code you're using:
distance = node.css("distances")
rs = distance.attr("rs")
css doesn't return what you think it does. It returns a NodeSet, which is similar to an Array. When you try to use attr on a NodeSet, you're going to set the value, not retrieve it. From the documentation:
#attr(key, value = nil, &blk) ⇒ Object (also: #set, #attribute)
Set the attribute key to value or the return value of blk on all Node objects in the NodeSet.
Because you're not using a value, the resulting action is to remove the attribute from the tag, which will then return nil and Ruby will assign nil to rs.
If you want to get the attribute of a node, you need to point to the node itself, so use at, or at_css, either of which returns a Node. Once you have the node, you can use attribute to retrieve the value, or use the [] shortcut similar to this untested code:
rs = node.at('distances')['rs']
Again though, because you didn't supply XML it's not possible to tell what else you might be trying to do, or whether this code is entirely accurate.