Passed
Push — master ( 69fc36...a0d1a5 )
by Swen
02:02
created

HStoreValue.as_sql()   B

Complexity

Conditions 4

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4.432

Importance

Changes 0
Metric Value
cc 4
c 0
b 0
f 0
dl 0
loc 28
rs 8.5806
ccs 7
cts 10
cp 0.7
crap 4.432
1 1
from django.db.models import expressions, CharField
0 ignored issues
show
Configuration introduced by
The import django.db.models could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
2
3
4 1
class HStoreValue(expressions.Expression):
5
    """Represents a HStore value.
6
7
    The base PostgreSQL implementation Django provides,
8
    always represents HStore values as dictionaries,
9
    but this doesn't work if you want to use expressions
10
    inside hstore values."""
11
12 1
    def __init__(self, value):
13
        """Initializes a new instance."""
14
15 1
        self.value = value
16
17 1
    def resolve_expression(self, *args, **kwargs):
18
        """Resolves expressions inside the dictionary."""
19
20 1
        result = dict()
21 1
        for key, value in self.value.items():
22 1
            if hasattr(value, 'resolve_expression'):
23 1
                result[key] = value.resolve_expression(
24
                    *args, **kwargs)
25
            else:
26
                result[key] = value
27
28 1
        return HStoreValue(result)
29
30 1
    def as_sql(self, compiler, connection):
31
        """Compiles the HStore value into SQL.
32
33
        Compiles expressions contained in the values
34
        of HStore entries as well.
35
36
        Given a dictionary like:
37
38
            dict(key1='val1', key2='val2')
39
40
        The resulting SQL will be:
41
42
            hstore(ARRAY['key1', 'val1'], ARRAY['key2', 'val2'])
43
        """
44
45 1
        result = []
46 1
        for key, value in self.value.items():
47 1
            if hasattr(value, 'as_sql'):
48 1
                sql, params = value.as_sql(compiler, connection)
49 1
                result.append('ARRAY[\'%s\', %s]' % (
50
                    key, sql % params))
51
            elif value is not None:
52
                result.append('ARRAY[\'%s\', \'%s\']' % ((
53
                    key, value)))
54
            else:
55
                result.append('ARRAY[\'%s\', NULL]' % key)
56
57 1
        return 'hstore(%s)' % ','.join(result), []
58
59
60 1
class HStoreColumn(expressions.Col):
61
    """HStoreColumn expression.
62
63
    Generates expressions like:
64
65
    [db table].[column]->'[hstore key]'
66
    """
67
68 1
    contains_column_references = True
69
70 1
    def __init__(self, alias, target, hstore_key):
71
        """Initializes a new instance of :see:HStoreColumn.
72
73
        Arguments:
74
            alias:
75
                The table name.
76
77
            target:
78
                The field instance.
79
80
            hstore_key
81
                The name of the hstore key to include
82
                in the epxression.
83
        """
84
85 1
        super().__init__(alias, target, output_field=target)
86 1
        self.alias, self.target, self.hstore_key = alias, target, hstore_key
87
88 1
    def __repr__(self):
89
        """Gets a textual representation of this expresion."""
90
91
        return "{}({}, {}->'{}')".format(
92
            self.__class__.__name__,
93
            self.alias,
94
            self.target,
95
            self.hstore_key
96
        )
97
98 1
    def as_sql(self, compiler, connection):
0 ignored issues
show
Unused Code introduced by
The argument connection seems to be unused.
Loading history...
99
        """Compiles this expression into SQL."""
100
101 1
        qn = compiler.quote_name_unless_alias
102 1
        return "%s.%s->'%s'" % (qn(self.alias), qn(self.target.column), self.hstore_key), []
103
104 1
    def relabeled_clone(self, relabels):
105
        """Gets a re-labeled clone of this expression."""
106
107
        return self.__class__(
0 ignored issues
show
Bug introduced by
There seem to be too many positional arguments for this constructor call.
Loading history...
108
            relabels.get(self.alias, self.alias),
109
            self.target,
110
            self.hstore_key,
111
            self.output_field
0 ignored issues
show
Bug introduced by
The Instance of HStoreColumn does not seem to have a member named output_field.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
112
        )
113
114
115 1
class HStoreRef(expressions.F):
116
    """Inline reference to a HStore key.
117
118
    Allows selecting individual keys in annotations.
119
    """
120
121 1
    def __init__(self, name: str, key: str):
122
        """Initializes a new instance of :see:HStoreRef.
123
124
        Arguments:
125
            name:
126
                The name of the column/field to resolve.
127
128
            key:
129
                The name of the HStore key to select.
130
        """
131
132 1
        super().__init__(name)
133 1
        self.key = key
134
135 1
    def resolve_expression(self, *args, **kwargs) -> HStoreColumn:
136
        """Resolves the expression into a :see:HStoreColumn expression."""
137
138 1
        original_expression = super().resolve_expression(*args, **kwargs)
139 1
        expression = HStoreColumn(
140
            original_expression.alias,
141
            original_expression.target,
142
            self.key
143
        )
144 1
        return expression
145
146
147 1
class NonGroupableFunc(expressions.Func):
0 ignored issues
show
Coding Style introduced by
This class has no __init__ method.
Loading history...
148
    """A version of Django's :see:Func expression that
149
    is _never_ included in the GROUP BY clause."""
150
151 1
    def get_group_by_cols(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
152
        """Gets the columns to be included in the GROUP BY clause.
153
154
        We have to override this because Django's default behavior
155
        is to include function calls in GROUP by clauses."""
156
        return []
157
158
159 1
class Min(NonGroupableFunc):
160
    """Exposes PostgreSQL's MIN(..) func."""
161
162 1
    def __init__(self, expression):
163
        super().__init__(expression, function='MIN')
164
165
166 1
class Max(NonGroupableFunc):
167
    """Exposes PostgreSQL's Max(..) func."""
168
169 1
    def __init__(self, expression):
170
        super().__init__(expression, function='Max')
171
172
173 1
class DateTimeEpochColumn(expressions.Col):
0 ignored issues
show
Coding Style introduced by
This class has no __init__ method.
Loading history...
174
    """Gets the date/time column as a UNIX epoch timestamp."""
175
176 1
    contains_column_references = True
177
178 1
    def as_sql(self, compiler, connection):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
179
        """Compiles this expression into SQL."""
180
181
        sql, params = super().as_sql(compiler, connection)
182
        return 'EXTRACT(epoch FROM {})'.format(sql), params
183
184 1
    def get_group_by_cols(self):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
185
        return []
186
187
188 1
class DateTimeEpoch(expressions.F):
0 ignored issues
show
Coding Style introduced by
This class has no __init__ method.
Loading history...
189
    """Gets the date/time column as a UNIX epoch timestamp."""
190
191 1
    contains_aggregate = False
192
193 1
    def resolve_expression(self, *args, **kwargs):
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
194
        original_expression = super().resolve_expression(*args, **kwargs)
195
        expression = DateTimeEpochColumn(
196
            original_expression.alias,
197
            original_expression.target,
198
        )
199
        return expression
200
201
202 1
def IsNotNone(*fields, default=None):
203
    """Selects whichever field is not None, in the specified order.
204
205
    Arguments:
206
        fields:
207
            The fields to attempt to get a value from,
208
            in order.
209
210
        default:
211
            The value to return in case all values are None.
212
213
    Returns:
214
        A Case-When expression that tries each field and
215
        returns the specified default value when all of
216
        them are None.
217
    """
218
219
    when_clauses = [
220
        expressions.When(
221
            ~expressions.Q(**{field: None}),
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
222
            then=expressions.F(field)
223
        )
224
        for field in reversed(fields)
225
    ]
226
227
    return expressions.Case(
0 ignored issues
show
Coding Style introduced by
Usage of * or ** arguments should usually be done with care.

Generally, there is nothing wrong with usage of * or ** arguments. For readability of the code base, we suggest to not over-use these language constructs though.

For more information, we can recommend this blog post from Ned Batchelder including its comments which also touches this aspect.

Loading history...
228
        *when_clauses,
229
        default=expressions.Value(default),
230
        output_field=CharField()
231
    )
232