~crass/tryton/server

« back to all changes in this revision

Viewing changes to trytond/model/fields/many2one.py

  • Committer: Mathias Behrle
  • Date: 2013-11-24 16:28:54 UTC
  • Revision ID: git-v1:182d6cce169eab1682eeacbad4323efa1136a1a0
MergingĀ upstreamĀ versionĀ 3.0.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#This file is part of Tryton.  The COPYRIGHT file at the top level of
2
2
#this repository contains the full copyright notices and license terms.
3
3
from types import NoneType
 
4
from sql import Column, Query, Expression
 
5
from sql.operators import Or
4
6
 
5
 
from trytond.model.fields.field import Field
6
 
from trytond.pool import Pool
 
7
from .field import Field, SQLType
 
8
from ...pool import Pool
 
9
from ...config import CONFIG
 
10
from ...tools import reduce_ids
 
11
from ...transaction import Transaction
7
12
 
8
13
 
9
14
class Many2One(Field):
16
21
            ondelete='SET NULL', datetime_field=None, help='', required=False,
17
22
            readonly=False, domain=None, states=None, select=False,
18
23
            on_change=None, on_change_with=None, depends=None,
19
 
            order_field=None, context=None, loading='eager'):
 
24
            context=None, loading='eager'):
20
25
        '''
21
26
        :param model_name: The name of the target model.
22
27
        :param left: The name of the field to store the left value for
41
46
        super(Many2One, self).__init__(string=string, help=help,
42
47
            required=required, readonly=readonly, domain=domain, states=states,
43
48
            select=select, on_change=on_change, on_change_with=on_change_with,
44
 
            depends=depends, order_field=order_field, context=context,
45
 
            loading=loading)
 
49
            depends=depends, context=context, loading=loading)
46
50
        self.model_name = model_name
47
51
        self.left = left
48
52
        self.right = right
71
75
            value = Target(value)
72
76
        assert isinstance(value, (Target, NoneType))
73
77
        super(Many2One, self).__set__(inst, value)
 
78
 
 
79
    @staticmethod
 
80
    def sql_format(value):
 
81
        if isinstance(value, (Query, Expression)):
 
82
            return value
 
83
        if value is None:
 
84
            return None
 
85
        assert value is not False
 
86
        return int(value)
 
87
 
 
88
    def sql_type(self):
 
89
        db_type = CONFIG['db_type']
 
90
        if db_type == 'postgresql':
 
91
            return SQLType('INT4', 'INT4')
 
92
        elif db_type == 'mysql':
 
93
            return SQLType('SIGNED INTEGER', 'BIGINT')
 
94
        else:
 
95
            return SQLType('INTEGER', 'INTEGER')
 
96
 
 
97
    def convert_domain_child_mptt(self, domain, tables):
 
98
        cursor = Transaction().cursor
 
99
        table, _ = tables[None]
 
100
        name, operator, ids = domain
 
101
        red_sql = reduce_ids(table.id, ids)
 
102
        left = Column(table, self.left)
 
103
        right = Column(table, self.right)
 
104
        cursor.execute(*table.select(left, right, where=red_sql))
 
105
        where = Or()
 
106
        for l, r in cursor.fetchall():
 
107
            where.append((left >= l) & (right <= r))
 
108
        expression = table.id.in_(table.select(table.id, where=where))
 
109
        if operator == 'not child_of':
 
110
            return ~expression
 
111
        return expression
 
112
 
 
113
    def convert_domain_child(self, domain, tables):
 
114
        Target = self.get_target()
 
115
        table, _ = tables[None]
 
116
        name, operator, ids = domain
 
117
 
 
118
        def get_child(ids):
 
119
            if not ids:
 
120
                return []
 
121
            children = Target.search([
 
122
                    (name, 'in', ids),
 
123
                    (name, '!=', None),
 
124
                    ], order=[])
 
125
            child_ids = get_child([c.id for c in children])
 
126
            return ids + child_ids
 
127
        expression = table.id.in_(ids + get_child(ids))
 
128
        if operator == 'not child_of':
 
129
            return ~expression
 
130
        return expression
 
131
 
 
132
    def convert_domain(self, domain, tables, Model):
 
133
        Target = self.get_target()
 
134
        table, _ = tables[None]
 
135
        name, operator, value = domain[:3]
 
136
        column = Column(table, name.split('.', 1)[0])
 
137
        if '.' not in name:
 
138
            if operator in ('child_of', 'not child_of'):
 
139
                if Target != Model:
 
140
                    query = Target.search([(domain[3], 'child_of', value)],
 
141
                        order=[], query=True)
 
142
                    expression = column.in_(query)
 
143
                    if operator == 'not child_of':
 
144
                        return ~expression
 
145
                    return expression
 
146
 
 
147
                if isinstance(value, basestring):
 
148
                    targets = Target.search([('rec_name', 'ilike', value)],
 
149
                        order=[])
 
150
                    ids = [t.id for t in targets]
 
151
                elif not isinstance(value, (list, tuple)):
 
152
                    ids = [value]
 
153
                else:
 
154
                    ids = value
 
155
                if not ids:
 
156
                    expression = column.in_([None])
 
157
                    if operator == 'not child_of':
 
158
                        return ~expression
 
159
                    return expression
 
160
                elif self.left and self.right:
 
161
                    return self.convert_domain_child_mptt(
 
162
                        (name, operator, ids), tables)
 
163
                else:
 
164
                    return self.convert_domain_child(
 
165
                        (name, operator, ids), tables)
 
166
 
 
167
            if not isinstance(value, basestring):
 
168
                return super(Many2One, self).convert_domain(domain, tables,
 
169
                    Model)
 
170
            else:
 
171
                target_name = 'rec_name'
 
172
        else:
 
173
            _, target_name = name.split('.', 1)
 
174
        target_domain = [(target_name,) + tuple(domain[1:])]
 
175
        if 'active' in Target._fields:
 
176
            target_domain.append(('active', 'in', [True, False]))
 
177
        query = Target.search(target_domain, order=[], query=True)
 
178
        return column.in_(query)
 
179
 
 
180
    def convert_order(self, name, tables, Model):
 
181
        if getattr(Model, 'order_%s' % name, None):
 
182
            return super(Many2One, self).convert_order(name, tables, Model)
 
183
 
 
184
        Target = self.get_target()
 
185
 
 
186
        oname = 'id'
 
187
        if Target._rec_name in Target._fields:
 
188
            oname = Target._rec_name
 
189
        if Target._order_name in Target._fields:
 
190
            oname = Target._order_name
 
191
 
 
192
        ofield = Target._fields[oname]
 
193
        table, _ = tables[None]
 
194
        target_tables = tables.get(name)
 
195
        if target_tables is None:
 
196
            target = Target.__table__()
 
197
            target_tables = {
 
198
                None: (target, target.id == Column(table, name)),
 
199
                }
 
200
            tables[name] = target_tables
 
201
        return ofield.convert_order(oname, target_tables, Target)