Add One2One field
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add One2One field
|
||||
* Add AUTOINCREMENT to sqlite primary key
|
||||
|
||||
Version 1.8.0 - 2010-11-01
|
||||
|
||||
@@ -497,6 +497,28 @@ Instance methods:
|
||||
|
||||
Return the target :class:`~trytond.model.Model`.
|
||||
|
||||
One2One
|
||||
-------
|
||||
|
||||
.. class:: One2One(relation_name, origin, target, string[, datetime_field[, \**options]])
|
||||
|
||||
A one-to-one relation field.
|
||||
|
||||
.. warning::
|
||||
It is on the relation_name :class:`~trytond.model.Model` that the
|
||||
unicity of the couple (origin, target) must be checked.
|
||||
..
|
||||
|
||||
.. attribute:: One2One.datetime_field
|
||||
|
||||
Same as :attr:`Many2One.datetime_field`
|
||||
|
||||
Instance methods:
|
||||
|
||||
.. method:: One2One.get_target(pool)
|
||||
|
||||
Return the target :class:`~trytond.model.Model`.
|
||||
|
||||
Function
|
||||
--------
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ class BrowseRecord(object):
|
||||
model = self._model.pool.get(j.model_name)
|
||||
elif hasattr(j, 'get_target'):
|
||||
model = j.get_target(self._model.pool)
|
||||
if model and j._type in ('many2one',):
|
||||
if model and j._type in ('many2one', 'one2one'):
|
||||
if (not data[i]
|
||||
and not (isinstance(data[i], (int, long))
|
||||
and not isinstance(data[i],
|
||||
|
||||
@@ -18,3 +18,4 @@ from one2many import *
|
||||
from many2many import *
|
||||
from function import *
|
||||
from property import *
|
||||
from one2one import *
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.model.fields.many2many import Many2Many
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class One2One(Many2Many):
|
||||
'''
|
||||
Define one2one field (``int``).
|
||||
'''
|
||||
_type = 'one2one'
|
||||
|
||||
def get(self, ids, model, name, values=None):
|
||||
'''
|
||||
Return target record.
|
||||
|
||||
:param ids: a list of ids
|
||||
:param model: a string with the name of the model
|
||||
:param name: a string with the name of the field
|
||||
:param values: a dictionary with the read values
|
||||
:return: a dictionary with ids as key and target id as value
|
||||
'''
|
||||
res = super(One2One, self).get(ids, model, name, values=values)
|
||||
for i, vals in res.iteritems():
|
||||
res[i] = vals[0] if vals else False
|
||||
return res
|
||||
|
||||
def set(self, ids, model, name, value):
|
||||
'''
|
||||
Set the values.
|
||||
|
||||
:param ids: A list of ids
|
||||
:param model: A string with the name of the model
|
||||
:param name: A string with the name of the field
|
||||
:param value: The id to link
|
||||
'''
|
||||
relation_obj = model.pool.get(self.relation_name)
|
||||
relation_ids = relation_obj.search([
|
||||
(self.origin, 'in', ids),
|
||||
])
|
||||
relation_obj.delete(relation_ids)
|
||||
if value:
|
||||
for record_id in ids:
|
||||
relation_obj.create({
|
||||
self.origin: record_id,
|
||||
self.target: value,
|
||||
})
|
||||
@@ -534,7 +534,7 @@ class Model(object):
|
||||
field = self._columns[field_name]
|
||||
else:
|
||||
field = self._inherit_fields[field_name][2]
|
||||
if field._type in ('many2one',):
|
||||
if field._type in ('many2one', 'one2one'):
|
||||
if not isinstance(field_value, (int, long)):
|
||||
continue
|
||||
obj = self.pool.get(field.model_name)
|
||||
@@ -740,6 +740,7 @@ class Model(object):
|
||||
'one2many',
|
||||
'many2many',
|
||||
'many2one',
|
||||
'one2one',
|
||||
):
|
||||
if hasattr(self._columns[field], 'model_name'):
|
||||
relation = copy.copy(self._columns[field].model_name)
|
||||
|
||||
+35
-13
@@ -632,8 +632,11 @@ class ModelSQL(ModelStorage):
|
||||
if field not in fields_related.keys():
|
||||
continue
|
||||
fields_related2values.setdefault(field, {})
|
||||
if self._columns[field]._type == 'many2one':
|
||||
obj = self.pool.get(self._columns[field].model_name)
|
||||
if self._columns[field]._type in ('many2one', 'one2one'):
|
||||
if hasattr(self._columns[field], 'model_name'):
|
||||
obj = self.pool.get(self._columns[field].model_name)
|
||||
else:
|
||||
obj = self._columns[field].get_target(self.pool)
|
||||
if hasattr(self._columns[field], 'datetime_field') \
|
||||
and self._columns[field].datetime_field:
|
||||
for record in res:
|
||||
@@ -678,7 +681,7 @@ class ModelSQL(ModelStorage):
|
||||
if field not in self._columns:
|
||||
continue
|
||||
for related in fields_related[field]:
|
||||
if self._columns[field]._type == 'many2one':
|
||||
if self._columns[field]._type in ('many2one', 'one2one'):
|
||||
if record[field]:
|
||||
record[field + '.' + related] = \
|
||||
fields_related2values[field]\
|
||||
@@ -1358,18 +1361,37 @@ class ModelSQL(ModelStorage):
|
||||
field = table._columns.get(fargs[0], False)
|
||||
if len(fargs) > 1:
|
||||
if field._type == 'many2one':
|
||||
target_obj = self.pool.get(field.model_name)
|
||||
if hasattr(field, 'search'):
|
||||
domain.extend([(fargs[0], 'in',
|
||||
self.pool.get(field.model_name).search([
|
||||
(fargs[1], domain[i][1], domain[i][2]),
|
||||
], order=[]))])
|
||||
domain.extend([(fargs[0], 'in', target_obj.search([
|
||||
(fargs[1], domain[i][1], domain[i][2]),
|
||||
], order=[]))])
|
||||
domain.pop(i)
|
||||
else:
|
||||
domain[i] = (fargs[0], 'inselect',
|
||||
self.pool.get(field.model_name).search([
|
||||
(fargs[1], domain[i][1], domain[i][2]),
|
||||
], order=[], query_string=True),
|
||||
table)
|
||||
domain[i] = (fargs[0], 'inselect', target_obj.search([
|
||||
(fargs[1], domain[i][1], domain[i][2]),
|
||||
], order=[], query_string=True), table)
|
||||
i += 1
|
||||
continue
|
||||
elif field._type in ('one2one', 'many2many'):
|
||||
if hasattr(field, 'model_name'):
|
||||
target_obj = self.pool.get(field.model_name)
|
||||
else:
|
||||
target_obj = field.get_target(self.pool)
|
||||
relation_obj = self.pool.get(field.relation_name)
|
||||
if hasattr(field, 'search'):
|
||||
domain.extend([(fargs[0], 'in', target_obj.search([
|
||||
(fargs[1], domain[i][1], domain[i][2]),
|
||||
], order=[]))])
|
||||
domain.pop(i)
|
||||
else:
|
||||
query1, query2 = target_obj.search([
|
||||
(fargs[1], domain[i][1], domain[i][2]),
|
||||
], order=[], query_string=True)
|
||||
query1 = ('SELECT "%s" FROM "%s" WHERE "%s" IN (%s)' %
|
||||
(field.origin, relation_obj._table,
|
||||
field.target, query1))
|
||||
domain[i] = ('id', 'inselect', (query1, query2))
|
||||
i += 1
|
||||
continue
|
||||
else:
|
||||
@@ -1433,7 +1455,7 @@ class ModelSQL(ModelStorage):
|
||||
|
||||
domain[i] = ('id', 'in', ids3)
|
||||
i += 1
|
||||
elif field._type == 'many2many':
|
||||
elif field._type in ('many2many', 'one2one'):
|
||||
# XXX must find a solution for long id list
|
||||
if hasattr(field, 'model_name'):
|
||||
target_obj = self.pool.get(field.model_name)
|
||||
|
||||
@@ -278,7 +278,7 @@ class ModelStorage(Model):
|
||||
data[field_name] = default[field_name]
|
||||
elif ftype == 'function':
|
||||
del data[field_name]
|
||||
elif ftype == 'many2one':
|
||||
elif ftype in ('many2one', 'one2one'):
|
||||
try:
|
||||
data[field_name] = data[field_name] and \
|
||||
data[field_name][0]
|
||||
@@ -648,6 +648,9 @@ class ModelStorage(Model):
|
||||
res = [('set', res)]
|
||||
return res
|
||||
|
||||
def get_one2one(relation, value):
|
||||
return ('set', get_many2one(relation, value))
|
||||
|
||||
def get_reference(value):
|
||||
if not value:
|
||||
return False
|
||||
@@ -749,6 +752,8 @@ class ModelStorage(Model):
|
||||
res = get_many2one(this_field_def['relation'], value)
|
||||
elif field_type == 'many2many':
|
||||
res = get_many2many(this_field_def['relation'], value)
|
||||
elif field_type == 'one2one':
|
||||
res = get_one2one(this_field_def['relation'], value)
|
||||
elif field_type == 'reference':
|
||||
res = get_reference(value)
|
||||
else:
|
||||
@@ -969,8 +974,9 @@ class ModelStorage(Model):
|
||||
not field.setter:
|
||||
continue
|
||||
# validate domain
|
||||
if field._type in ('many2one', 'many2many', 'one2many') \
|
||||
and field.domain:
|
||||
if (field._type in
|
||||
('many2one', 'many2many', 'one2many', 'one2one')
|
||||
and field.domain):
|
||||
if field._type in ('many2one', 'one2many'):
|
||||
relation_obj = self.pool.get(field.model_name)
|
||||
else:
|
||||
@@ -1095,7 +1101,7 @@ class ModelStorage(Model):
|
||||
for field in defaults.keys():
|
||||
fld_def = (field in self._columns) and self._columns[field] \
|
||||
or self._inherit_fields[field][2]
|
||||
if fld_def._type in ('many2one',):
|
||||
if fld_def._type in ('many2one', 'one2one'):
|
||||
if isinstance(defaults[field], (list, tuple)):
|
||||
vals[field] = defaults[field][0]
|
||||
else:
|
||||
|
||||
@@ -354,3 +354,72 @@ class DateTimeRequired(ModelSQL):
|
||||
required=True)
|
||||
|
||||
DateTimeRequired()
|
||||
|
||||
|
||||
class One2One(ModelSQL):
|
||||
'One2One'
|
||||
_name = 'test.one2one'
|
||||
_description = __doc__
|
||||
name = fields.Char('Name', required=True)
|
||||
one2one = fields.One2One('test.one2one.relation', 'origin', 'target',
|
||||
string='One2One', help='Test one2one', required=False)
|
||||
|
||||
One2One()
|
||||
|
||||
|
||||
class One2OneTarget(ModelSQL):
|
||||
'One2One Target'
|
||||
_name = 'test.one2one.target'
|
||||
name = fields.Char('Name', required=True)
|
||||
|
||||
One2OneTarget()
|
||||
|
||||
|
||||
class One2OneRelation(ModelSQL):
|
||||
'One2One Relation'
|
||||
_name = 'test.one2one.relation'
|
||||
_description = __doc__
|
||||
origin = fields.Many2One('test.one2one', 'Origin')
|
||||
target = fields.Many2One('test.one2one.target', 'Target')
|
||||
|
||||
def __init__(self):
|
||||
super(One2OneRelation, self).__init__()
|
||||
self._sql_constraints += [
|
||||
('origin_unique', 'UNIQUE(origin)',
|
||||
'Origin must be unique'),
|
||||
('target_unique', 'UNIQUE(target)',
|
||||
'Target must be unique'),
|
||||
]
|
||||
|
||||
One2OneRelation()
|
||||
|
||||
|
||||
class One2OneRequired(ModelSQL):
|
||||
'One2One'
|
||||
_name = 'test.one2one_required'
|
||||
_description = __doc__
|
||||
name = fields.Char('Name', required=True)
|
||||
one2one = fields.One2One('test.one2one_required.relation', 'origin', 'target',
|
||||
string='One2One', help='Test one2one', required=True)
|
||||
|
||||
One2OneRequired()
|
||||
|
||||
|
||||
class One2OneRequiredRelation(ModelSQL):
|
||||
'One2One Relation'
|
||||
_name = 'test.one2one_required.relation'
|
||||
_description = __doc__
|
||||
origin = fields.Many2One('test.one2one_required', 'Origin')
|
||||
target = fields.Many2One('test.one2one.target', 'Target')
|
||||
|
||||
def __init__(self):
|
||||
super(One2OneRequiredRelation, self).__init__()
|
||||
self._sql_constraints += [
|
||||
('origin_unique', 'UNIQUE(origin)',
|
||||
'Origin must be unique'),
|
||||
('target_unique', 'UNIQUE(target)',
|
||||
'Target must be unique'),
|
||||
]
|
||||
|
||||
One2OneRequiredRelation()
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ class FieldsTestCase(unittest.TestCase):
|
||||
self.datetime_default = POOL.get('test.datetime_default')
|
||||
self.datetime_required = POOL.get('test.datetime_required')
|
||||
|
||||
self.one2one = POOL.get('test.one2one')
|
||||
self.one2one_target = POOL.get('test.one2one.target')
|
||||
self.one2one_required = POOL.get('test.one2one_required')
|
||||
|
||||
def test0010boolean(self):
|
||||
'''
|
||||
Test Boolean.
|
||||
@@ -2072,6 +2076,127 @@ class FieldsTestCase(unittest.TestCase):
|
||||
|
||||
transaction.cursor.rollback()
|
||||
|
||||
def test0100one2one(self):
|
||||
'''
|
||||
Test One2One.
|
||||
'''
|
||||
with Transaction().start(DB_NAME, USER, CONTEXT) as transaction:
|
||||
target1_id = self.one2one_target.create({
|
||||
'name': 'target1',
|
||||
})
|
||||
one2one1_id = self.one2one.create({
|
||||
'name': 'origin1',
|
||||
'one2one': target1_id,
|
||||
})
|
||||
self.assert_(one2one1_id)
|
||||
|
||||
one2one1 = self.one2one.read(one2one1_id, ['one2one',
|
||||
'one2one.name'])
|
||||
self.assert_(one2one1['one2one'] == target1_id)
|
||||
self.assert_(one2one1['one2one.name'] == 'target1')
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', '=', 'target1'),
|
||||
])
|
||||
self.assert_(one2one_ids == [one2one1_id])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', '!=', 'target1'),
|
||||
])
|
||||
self.assert_(one2one_ids == [])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', 'in', [target1_id]),
|
||||
])
|
||||
self.assert_(one2one_ids == [one2one1_id])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', 'in', [0]),
|
||||
])
|
||||
self.assert_(one2one_ids == [])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', 'not in', [target1_id]),
|
||||
])
|
||||
self.assert_(one2one_ids == [])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', 'not in', [0]),
|
||||
])
|
||||
self.assert_(one2one_ids == [one2one1_id])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one.name', '=', 'target1'),
|
||||
])
|
||||
self.assert_(one2one_ids == [one2one1_id])
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one.name', '!=', 'target1'),
|
||||
])
|
||||
self.assert_(one2one_ids == [])
|
||||
|
||||
one2one = self.one2one.browse(one2one1_id)
|
||||
self.assert_(one2one.one2one.name == 'target1')
|
||||
|
||||
one2one2_id = self.one2one.create({
|
||||
'name': 'origin2',
|
||||
})
|
||||
self.assert_(one2one2_id)
|
||||
|
||||
one2one2 = self.one2one.read(one2one2_id, ['one2one'])
|
||||
self.assert_(one2one2['one2one'] == False)
|
||||
|
||||
one2one_ids = self.one2one.search([
|
||||
('one2one', '=', False),
|
||||
])
|
||||
self.assert_(one2one_ids == [one2one2_id])
|
||||
|
||||
target2_id = self.one2one_target.create({
|
||||
'name': 'target2',
|
||||
})
|
||||
self.one2one.write(one2one2_id, {
|
||||
'one2one': target2_id,
|
||||
})
|
||||
target2_id = self.one2one_target.search([
|
||||
('name', '=', 'target2'),
|
||||
])[0]
|
||||
one2one2 = self.one2one.read(one2one2_id, ['one2one'])
|
||||
self.assert_(one2one2['one2one'] == target2_id)
|
||||
|
||||
self.one2one.write(one2one2_id, {
|
||||
'one2one': False,
|
||||
})
|
||||
one2one2 = self.one2one.read(one2one2_id, ['one2one'])
|
||||
self.assert_(one2one2['one2one'] == False)
|
||||
|
||||
one2one2 = self.one2one.browse(one2one2_id)
|
||||
self.assert_(not one2one2.one2one)
|
||||
|
||||
self.failUnlessRaises(Exception, self.one2one.create, {
|
||||
'name': 'one2one3',
|
||||
'one2one': target1_id,
|
||||
})
|
||||
|
||||
self.failUnlessRaises(Exception, self.one2one.write, one2one2_id, {
|
||||
'one2one': target1_id,
|
||||
})
|
||||
|
||||
self.failUnlessRaises(Exception, self.one2one_required.create, {
|
||||
'name': 'one2one3',
|
||||
})
|
||||
|
||||
target3_id = self.one2one_target.create({
|
||||
'name': 'target3_id',
|
||||
})
|
||||
|
||||
one2one3_id = self.one2one_required.create({
|
||||
'name': 'one2one3',
|
||||
'one2one': target3_id,
|
||||
})
|
||||
self.assert_(one2one3_id)
|
||||
|
||||
transaction.cursor.rollback()
|
||||
|
||||
def suite():
|
||||
return unittest.TestLoader().loadTestsFromTestCase(FieldsTestCase)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user