diff options
| author | Nicolas Lempereur <nle@odoo.com> | 2017-03-15 17:11:16 +0100 |
|---|---|---|
| committer | Nicolas Lempereur <nle@odoo.com> | 2017-03-16 11:57:04 +0100 |
| commit | cc1c15327cddf2b512e01e0b7b31e2aa6a1d34fe (patch) | |
| tree | cfb1d840e285c63ec461fd450070dfdeb09ec142 /odoo/addons/test_new_api/tests | |
| parent | 27f656a23e92df52b2f78fdd7c31b8c045b1f39b (diff) | |
[FIX] expression: improve o2m with m2o integer
We sometimes use in Odoo a One2many field with an inverse Integer
field instead of a usual Many2one.
This allow for example in several instances to have a "Many2one" which
can be reference from several models, eg:
Model Ranking:
name = String field
res_id = Integer field
res_model = String field
Model Toy:
rank = One2many [inverse: Ranking -> res_id]
[domain: res_model == Toy]
Model Tool:
rank = One2many [inverse: Ranking -> res_id]
[domain: res_model == Tool]
This enable us to have a shared feature between otherwise unrelated models.
But there was several issue when searching on these One2many:
1) if the Integer Many2one was not stored (eg. it came from an inherits) on
the searched model, this could lead to an error.
2) when we searched:
- by IDs (rank in ['55']) with at least one id not respecting the domain
- by IDs with a negative operator
- with a negative operator on unfound string (rank != "no rank has this")
- with a false value (rank = False)
we would not apply the One2many domain (eg. res_model == Toy) and thus
possibly getting Toy 3 errenously because a Tool 3 was found without
the domain being applied.
This fix modify the search on One2many and for:
1. if the inverse is an Integer not stored field instead of Many2one
manage it.
2. if the field is an Integer field instead of Many2one and there is a
domain on the One2many: apply the domain on the inverse model found.
So only some search on One2many with a domain whose inverse field is an
Integer could be impacted.
This would also be nice to have for all One2many with a domain but the
probability of it being useful versus risk for performance is not judged
high enough.
opw-710508
Diffstat (limited to 'odoo/addons/test_new_api/tests')
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_one2many.py | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/odoo/addons/test_new_api/tests/test_one2many.py b/odoo/addons/test_new_api/tests/test_one2many.py index 38dee13ab54..9ade2c0fe19 100644 --- a/odoo/addons/test_new_api/tests/test_one2many.py +++ b/odoo/addons/test_new_api/tests/test_one2many.py @@ -10,6 +10,36 @@ class One2manyCase(TransactionCase): "name": "What is up?" }) + # data for One2many with inverse field Integer + self.Edition = self.env["test_new_api.creativework.edition"] + self.Book = self.env["test_new_api.creativework.book"] + self.Movie = self.env["test_new_api.creativework.movie"] + + book_model_id = self.env['ir.model'].search([('model', '=', self.Book._name)]).id + movie_model_id = self.env['ir.model'].search([('model', '=', self.Movie._name)]).id + + books_data = ( + ('Imaginary book', ()), + ('Another imaginary book', ()), + ('Nineteen Eighty Four', ('First edition', 'Fourth Edition')) + ) + + movies_data = ( + ('The Gold Rush', ('1925 (silent)', '1942')), + ('Imaginary movie', ()), + ('Another imaginary movie', ()) + ) + + for name, editions in books_data: + book_id = self.Book.create({'name': name}).id + for edition in editions: + self.Edition.create({'res_model_id': book_model_id, 'name': edition, 'res_id': book_id}) + + for name, editions in movies_data: + movie_id = self.Movie.create({'name': name}).id + for edition in editions: + self.Edition.create({'res_model_id': movie_model_id, 'name': edition, 'res_id': movie_id}) + def operations(self): """Run operations on o2m fields to check all works fine.""" # Check the lines first @@ -63,3 +93,40 @@ class One2manyCase(TransactionCase): """Check lines created with RPC style and added in one step""" self.multi.lines = [(0, 0, {'name': str(name)}) for name in range(10)] self.operations() + + def test_many2one_integer(self): + """Test several models one2many with same inverse Integer field""" + # utility function to convert records to tuples with id,name + t = lambda records: records.mapped(lambda r: (r.id, r.name)) + + books = self.Book.search([]) + books_with_edition = books.filtered(lambda r: r.editions) + movies = self.Movie.search([]) + movies_without_edition = movies.filtered(lambda r: not r.editions) + movies_with_edition = movies.filtered(lambda r: r.editions) + movie_editions = movies_with_edition.mapped('editions') + one_movie_edition = movie_editions[0] + + res_movies_without_edition = self.Movie.search([('editions', '=', False)]) + self.assertItemsEqual(t(res_movies_without_edition), t(movies_without_edition)) + + res_movies_with_edition = self.Movie.search([('editions', '!=', False)]) + self.assertItemsEqual(t(res_movies_with_edition), t(movies_with_edition)) + + res_books_with_movie_edition = self.Book.search([('editions', 'in', movie_editions.ids)]) + self.assertFalse(t(res_books_with_movie_edition)) + + res_books_without_movie_edition = self.Book.search([('editions', 'not in', movie_editions.ids)]) + self.assertItemsEqual(t(res_books_without_movie_edition), t(books_with_edition)) + + res_books_without_one_movie_edition = self.Book.search([('editions', 'not in', movie_editions[:1].ids)]) + self.assertItemsEqual(t(res_books_without_one_movie_edition), t(books_with_edition)) + + res_books_with_one_movie_edition_name = self.Book.search([('editions', '=', movie_editions[:1].name)]) + self.assertFalse(t(res_books_with_one_movie_edition_name)) + + res_books_without_one_movie_edition_name = self.Book.search([('editions', '!=', movie_editions[:1].name)]) + self.assertItemsEqual(t(res_books_without_one_movie_edition_name), t(books_with_edition)) + + res_movies_not_of_edition_name = self.Movie.search([('editions', '!=', one_movie_edition.name)]) + self.assertItemsEqual(t(res_movies_not_of_edition_name), t(movies.filtered(lambda r: one_movie_edition not in r.editions))) |
