Odoo - Understanding recordsets in the new api - api

Please look at the following code block:
class hr_payslip:
...
#api.multi # same behavior with or without method decorators
def do_something(self):
print "self------------------------------->",self
for r in self:
print "r------------------------------->",r
As you can see I'm overriding a 'hr.payslip' model and I need to access some field inside this method. The problem is that it doesn't make sense to me what gets printed:
self-------------------------------> hr.payslip(hr.payslip(1,),)
r-------------------------------> hr.payslip(hr.payslip(1,),)
Why is it the same thing inside and outside of for loop. If it's always a 'recordset', how would one access one record's field.
My lack of understanding is probably connected to this question also:
Odoo - Cannot loop trough model records

Working on RecordSets always means working on RecordSets. When you loop over one RecordSet you will get RecordSets as looping variable. But you can only access fields directly when the length of a RecordSet is 1 or 0. You can test it fairly easy (more then one payslip in database!):
slips = self.env['hr.payslip'].search([])
# Exception because you cannot access the field directly on multi entries
print slips.id
# Working
print slips.ids
for slip in slips:
print slip.id

Related

How can I access value in sequence type?

There are the following attributes in client_output
weights_delta = attr.ib()
client_weight = attr.ib()
model_output = attr.ib()
client_loss = attr.ib()
After that, I made the client_output in the form of a sequence through
a = tff.federated_collect(client_output) and round_model_delta = tff.federated_map(selecting_fn,a)in here . and I declared
`
#tff.tf_computation() # append
def selecting_fn(a):
#TODO
return round_model_delta
in here. In the process of averaging on the server, I want to average the weights_delta by selecting some of the clients with a small loss value. So I try to access it via a.weights_delta but it doesn't work.
The tff.federated_collect returns a tff.SequenceType placed at tff.SERVER which you can manipulate the same way as for example client dataset is usually handled in a method decorated by tff.tf_computation.
Note that you have to use the tff.federated_collect operator in the scope of a tff.federated_computation. What you probably want to do[*] is pass it into a tff.tf_computation, using the tff.federated_map operator. Once inside the tff.tf_computation, you can think of it as a tf.data.Dataset object and everything in the tf.data module is available.
[*] I am guessing. More detailed explanation of what you would like to achieve would be helpful.

error with rec.env['book.tickets'].search()

#api.depends('totalbook')
def _computebook(self):
sum_a = 0
for rec in self:
for l in rec.env['book.tickets'].search([('status', 'in', ('sold', 'rent'))]):
if l:
sum_a += 1
rec.currentbook = rec.totalbook - sum_a
I use this compute to calculate current book in library.
But when I run this code, the problem calculate of my each book base on all books.
When you are write any compute method in odoo, in self you will get all the list of browsable objects.
And then you are trying to search data from that browsable object. That's why you get a error.
You have to use self.env instead of rec.env.
Because you can't search data from browsable object you can access only data of that browsable object.
You have to add limit 1 on when you are searching a record of multiple record are than you will get another expected Singleton error.
Either you can you use another loop after searching record.
Let me know if you face any errors again.
Thanks
this error occured because
whenever you are trying to make a object of another model at that time you should have to use with self.env instead of rec.env because in your method rec is just a record-set of your instance
so please update your method as per the following snippet.
for l in self.env['book.tickets'].search([('status', 'in', ('sold','rent'))]):

Expected singleton odoo 9

After enter 2 and more new row in tree view and click on save get error
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: my.model(2116, 2117)
My source code:
#api.depends('start', 'finish','stop')
def total_fun(self):
time1 = datetime.strptime(self.start, "%Y-%m-%d %H:%M:%S")
time2 = datetime.strptime(self.finish, "%Y-%m-%d %H:%M:%S")
self.total = round(((time2 - time1).seconds / float(60*60) - self.stop))
Error message says -> expected singleton this means: you are using recordset instead of record.
To fix this use
for rec in self:
in the begining of function, and then use rec instead of self
As you can see in the error message Expected singleton: my.model(2116, 2117)
By default in odoo the self is always a recordSet (means it can contain more then one record.) so when you do self.getSomeField here odoo will be confused wich record you want to get the value from.
if you don't tell odoo that make sure that the self will always contain one record when you acces an attribute if the recordSet contains more than one record this error is raised.
Now how to tell odoo make sure there is always one record is by adding #api.one decorator to the method. but is not recommended because odoo in your case there is two record so he will loop and call the method for each record and pass a recordSet that have only that record. imagine that you execute a search or any communication with database.
so don't use #api.one only if you are sure of what you are doing because you can make 10000 method call and interact with database.
like this example using #api.one:
# every call to this method you will execute a search.
self.env['some.model'].search([('m2o_id' , '=', self.id)]
you can do this before the loop:
# one query for all record with one call to the method
result = self.env['some.model'].search([('m2o_id' , 'in', self.ids)]
for rec in self:
# use result here
# or here ..

Create Ransack object without querying DB

I'd like to be able to create a Ransack::Search object to be passed into a search_form_for, but the initial creation of the search object queries the database, wich I don't want.
I want to show an initial blank form with Ransack::Search options to search, without calling the database.
How can I do that ?
thanks,
regards
Arel relations (ie. queries) are lazily-executed on first reference to the results, so you should find that you can create a search object and pass it to the form, without it calling the db, so long as you don't reference the .result method anywhere.
e.g.
// in your controller
my_query = MyModelClass.where{ id.gt(0) }
#q = my_query.search( params[:q] )
// in your view
search_form_for( #q, (...other options...) )
Any of these will trigger the db query to be actually performed:
- #q.results.each do |result|
- for result in #q.results
- #q.results.count
- #q.results.to_a
- #q.results.size
// ....etc
But so long as you only use the search object for your form, it should not get executed.
Of course, if you're testing this from the console, make sure that you put ;nil at the end of the line, otherwise the console will print the last thing evaluated, which will cause the query to be run!

Can I use the the power of Generics to solve my issue

I have a wierd issue. I am loading 1k invoice objects, header first then details in my DAL. I am using VB.NET on this project. I am able to get the invoice headers just fine. When I get to loading the details for each invoice I am getting a timeout on SQL Server. I increased the timeout to 5 minutes but still the same thing. If I reduce the invoice count to 200 it works fine.
Here is what I am doing
//I already loaded the invoice headers. I am now iterating each invoice to get it's detail
For Each invoice As Invoice In invoices
drInvoiceItems = DBSqlHelperFactory.ExecuteReader(CONNECTION_STRING, CommandType.StoredProcedure, "dbo.getinvoiceitem", _
New SqlParameter("#invoicenumber", invoice.InvoiceNumber))
While drInvoiceItems.Read()
invoice.LineItems.Add(New InvoiceLine(drInvoiceItems("id"), drInvoiceItems("inv_id"), drInvoiceItems("prodid"), drInvoiceItems("name"), drInvoiceItems("barcode"), drInvoiceItems("quantity"), drInvoiceItems("costprice")))
End While
Next
Return invoices
I am aware that I am firing 1k connections to the DB due to the iterations. Can't I load all the line items with one select statement and then do something like
For Each invoice As Invoice In invoices
invoice.Items.Add(invoiceItems.Find(Function(i as InvoiceItem),i.InvoiceNumber = invoice.InvoiceNumber))
Next
I get the error whenusing the lambda funcion above
Error 1 Value of type 'System.Collections.Generic.List(Of BizComm.InvoiceLine)' cannot be converted to 'BizComm.InvoiceLine'. C:\Projects\BizComm\InvoiceDAL.vb 75 35 BizComm
One thing I have done when iterating through items in the past is use the same Connection object for all the necessary read activities. It seems to greatly enhance performance.
I'd also look at the database to see whether the dbo.getinvoiceitem procedure can be improved, or if another procedure can be written which will give you all the line items for a group of invoices (perhaps by date or customer/vendor) rather than just one header at a time. Then you can more effectively apply your iteration over the invoice collection and add the lines to the headers.
You can also check to see whether there is an effective index on column that the #invoicenumber parameter references.
From your code, it looks like you are not closing the connections and datareaders. See if you can place your connections and datareaders in a USING statement:
Using con As New SqlConnection(connectionString)
....
End Using
The DBSqlHelperFactory opens a connection, but can't close it since the connection is needed after its return. I'd modify the code, so that you open one connection and pass it to DBSqlHelperFactory as a parameter.
To quickly pick up these issues, I always debug with:
Max Pool Size=1;
added to the end of the connection string. That will quickly throw an error any time you forget to close a connection.
Why load InvoiceItems before hand? Can't you load it on demand?
i.e. when you need to get the Items, call a method on Invoice instance (myInvoice.GetItems)
EDIT: It will be better to understand the full picture of what you are trying to do.
Is it really required to get all Invoices as well?
Why not select all the line items for all the invoices you need in a single query. then split the results up into multiple invoice objects?
Re: how do I map between collections?
One implementation could be: create 1000 anemic Invoice object, chuck them in a Dictionary which goes from Id to Invoice. Then when you select the line items you include the invoice id, look up the anemic invoice and add the line to it.