Passed
Pull Request — master (#4)
by
unknown
02:18
created

LocalizedField.contribute_to_class()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
ccs 3
cts 3
cp 1
crap 1
1 1
from django.conf import settings
0 ignored issues
show
Configuration introduced by
The import django.conf 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...
Bug introduced by
There seems to be a cyclic import (localized_fields -> localized_fields.fields -> localized_fields.fields.localized_field).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (localized_fields -> localized_fields.fields -> localized_fields.fields.localized_uniqueslug_field -> localized_fields.fields.localized_autoslug_field -> localized_fields.fields.localized_field).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (localized_fields -> localized_fields.fields -> localized_fields.fields.localized_autoslug_field -> localized_fields.fields.localized_field).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (localized_fields -> localized_fields.fields -> localized_fields.fields.localized_bleach_field -> localized_fields.fields.localized_field).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
2 1
from django.db.utils import IntegrityError
0 ignored issues
show
Configuration introduced by
The import django.db.utils 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...
3 1
from django.utils import six, translation
0 ignored issues
show
Configuration introduced by
The import django.utils 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...
4
5 1
from localized_fields import LocalizedFieldForm
6 1
from psqlextra.fields import HStoreField
0 ignored issues
show
Configuration introduced by
The import psqlextra.fields 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...
7
8 1
from ..localized_value import LocalizedValue
9
10
11 1
class LocalizedValueDescriptor(object):
12
    """
13
    The descriptor for the localized value attribute on the model instance.
14
    Returns a :see:LocalizedValue when accessed so you can do stuff like::
15
16
        >>> from myapp.models import MyModel
17
        >>> instance = MyModel()
18
        >>> instance.value.en = 'English value'
19
20
    Assigns a strings to active language key in :see:LocalizedValue on
21
    assignment so you can do::
22
23
        >>> from django.utils import translation
24
        >>> from myapp.models import MyModel
25
26
        >>> translation.activate('nl')
27
        >>> instance = MyModel()
28
        >>> instance.title = 'dutch title'
29
        >>> print(instance.title.nl) # prints 'dutch title'
30
    """
31 1
    def __init__(self, field):
32 1
        self.field = field
33
34 1
    def __get__(self, instance, cls=None):
35 1
        if instance is None:
36
            return self
37
38
        # This is slightly complicated, so worth an explanation.
39
        # `instance.localizedvalue` needs to ultimately return some instance of
40
        # `LocalizedValue`, probably a subclass.
41
42
        # The instance dict contains whatever was originally assigned
43
        # in __set__.
44 1
        if self.field.name in instance.__dict__:
45 1
            value = instance.__dict__[self.field.name]
46
        else:
47
            instance.refresh_from_db(fields=[self.field.name])
48
            value = getattr(instance, self.field.name)
49
50 1
        if value is None:
51 1
            attr = self.field.attr_class()
52 1
            instance.__dict__[self.field.name] = attr
53
54 1
        if isinstance(value, dict):
55 1
            attr = self.field.attr_class(value)
56 1
            instance.__dict__[self.field.name] = attr
57
58 1
        return instance.__dict__[self.field.name]
59
60 1
    def __set__(self, instance, value):
61 1
        if isinstance(value, six.string_types):
62
            self.__get__(instance).set(translation.get_language() or
0 ignored issues
show
Bug introduced by
The Instance of LocalizedValueDescriptor does not seem to have a member named set.

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...
63
                                       settings.LANGUAGE_CODE, value)
64
        else:
65 1
            instance.__dict__[self.field.name] = value
66
67
68 1
class LocalizedField(HStoreField):
69
    """A field that has the same value in multiple languages.
70
71
    Internally this is stored as a :see:HStoreField where there
72
    is a key for every language."""
73
74 1
    Meta = None
75
76
    # The class to wrap instance attributes in. Accessing to field attribute in
77
    # model instance will always return an instance of attr_class.
78 1
    attr_class = LocalizedValue
79
80
    # The descriptor to use for accessing the attribute off of the class.
81 1
    descriptor_class = LocalizedValueDescriptor
82
83 1
    def __init__(self, *args, **kwargs):
84
        """Initializes a new instance of :see:LocalizedField."""
85
86 1
        super(LocalizedField, self).__init__(*args, **kwargs)
87
88 1
    def contribute_to_class(self, cls, name, **kwargs):
89 1
        super(LocalizedField, self).contribute_to_class(cls, name, **kwargs)
90 1
        setattr(cls, self.name, self.descriptor_class(self))
0 ignored issues
show
Bug introduced by
The Instance of LocalizedField does not seem to have a member named name.

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...
91
92 1
    def from_db_value(self, value, *_):
93
        """Turns the specified database value into its Python
94
        equivalent.
95
96
        Arguments:
97
            value:
98
                The value that is stored in the database and
99
                needs to be converted to its Python equivalent.
100
101
        Returns:
102
            A :see:LocalizedValue instance containing the
103
            data extracted from the database.
104
        """
105
106 1
        if not value:
107 1
            return self.attr_class()
108
109 1
        return self.attr_class(value)
110
111 1
    def to_python(self, value: dict) -> LocalizedValue:
112
        """Turns the specified database value into its Python
113
        equivalent.
114
115
        Arguments:
116
            value:
117
                The value that is stored in the database and
118
                needs to be converted to its Python equivalent.
119
120
        Returns:
121
            A :see:LocalizedValue instance containing the
122
            data extracted from the database.
123
        """
124
125 1
        if not value or not isinstance(value, dict):
126 1
            return self.attr_class()
127
128 1
        return self.attr_class(value)
129
130 1
    def get_prep_value(self, value: LocalizedValue) -> dict:
131
        """Turns the specified value into something the database
132
        can store.
133
134
        If an illegal value (non-LocalizedValue instance) is
135
        specified, we'll treat it as an empty :see:LocalizedValue
136
        instance, on which the validation will fail.
137
138
        Arguments:
139
            value:
140
                The :see:LocalizedValue instance to serialize
141
                into a data type that the database can understand.
142
143
        Returns:
144
            A dictionary containing a key for every language,
145
            extracted from the specified value.
146
        """
147
148
        # default to None if this is an unknown type
149 1
        if not isinstance(value, LocalizedValue) and value:
150 1
            value = None
151
152 1
        if value:
153 1
            cleaned_value = self.clean(value)
154 1
            self.validate(cleaned_value)
155
        else:
156 1
            cleaned_value = value
157
158 1
        return super(LocalizedField, self).get_prep_value(
159
            cleaned_value.__dict__ if cleaned_value else None
160
        )
161
162 1
    def clean(self, value, *_):
163
        """Cleans the specified value into something we
164
        can store in the database.
165
166
        For example, when all the language fields are
167
        left empty, and the field is allows to be null,
168
        we will store None instead of empty keys.
169
170
        Arguments:
171
            value:
172
                The value to clean.
173
174
        Returns:
175
            The cleaned value, ready for database storage.
176
        """
177
178 1
        if not value or not isinstance(value, LocalizedValue):
179 1
            return None
180
181
        # are any of the language fiels None/empty?
182 1
        is_all_null = True
183 1
        for lang_code, _ in settings.LANGUAGES:
184 1
            if value.get(lang_code):
185 1
                is_all_null = False
186 1
                break
187
188
        # all fields have been left empty and we support
189
        # null values, let's return null to represent that
190 1
        if is_all_null and self.null:
0 ignored issues
show
Bug introduced by
The Instance of LocalizedField does not seem to have a member named null.

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...
191 1
            return None
192
193 1
        return value
194
195 1
    def validate(self, value: LocalizedValue, *_):
196
        """Validates that the value for the primary language
197
        has been filled in.
198
199
        Exceptions are raises in order to notify the user
200
        of invalid values.
201
202
        Arguments:
203
            value:
204
                The value to validate.
205
        """
206
207 1
        if self.null:
0 ignored issues
show
Bug introduced by
The Instance of LocalizedField does not seem to have a member named null.

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...
208 1
            return
209
210 1
        primary_lang_val = getattr(value, settings.LANGUAGE_CODE)
211
212 1
        if not primary_lang_val:
213 1
            raise IntegrityError(
214
                'null value in column "%s.%s" violates not-null constraint' % (
215
                    self.name,
0 ignored issues
show
Bug introduced by
The Instance of LocalizedField does not seem to have a member named name.

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...
216
                    settings.LANGUAGE_CODE
217
                )
218
            )
219
220 1
    def formfield(self, **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...
221
        """Gets the form field associated with this field."""
222
223 1
        defaults = {
224
            'form_class': LocalizedFieldForm
225
        }
226
227 1
        defaults.update(kwargs)
228 1
        return super().formfield(**defaults)
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...
229
230 1
    def deconstruct(self):
231
        """Gets the values to pass to :see:__init__ when
232
        re-creating this object."""
233
234 1
        name, path, args, kwargs = super(
235
            LocalizedField, self).deconstruct()
236
237 1
        if self.uniqueness:
0 ignored issues
show
Bug introduced by
The Instance of LocalizedField does not seem to have a member named uniqueness.

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...
238 1
            kwargs['uniqueness'] = self.uniqueness
0 ignored issues
show
Bug introduced by
The Instance of LocalizedField does not seem to have a member named uniqueness.

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...
239
240
        return name, path, args, kwargs
241