Passed
Push — master ( 3b28a5...96ddc7 )
by Swen
02:25
created

LocalizedAutoSlugField.get_field_value()   A

Complexity

Conditions 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 4
cts 4
cp 1
rs 10
cc 2
crap 2
1 1
from typing import Callable, Tuple, Union
0 ignored issues
show
Configuration introduced by
The import typing 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 1
from datetime import datetime
3
4 1
from django import forms
0 ignored issues
show
Configuration introduced by
The import django 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...
5 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...
6 1
from django.utils import 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...
7 1
from django.utils.text import slugify
0 ignored issues
show
Configuration introduced by
The import django.utils.text 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...
8
9 1
from .field import LocalizedField
10 1
from ..value import LocalizedValue
11 1
from ..util import resolve_object_property
12
13
14 1
class LocalizedAutoSlugField(LocalizedField):
15
    """Automatically provides slugs for a localized
16
    field upon saving."""
17
18 1
    def __init__(self, *args, **kwargs):
19
        """Initializes a new instance of :see:LocalizedAutoSlugField."""
20
21 1
        self.populate_from = kwargs.pop('populate_from', None)
22 1
        self.include_time = kwargs.pop('include_time', False)
23
24 1
        super(LocalizedAutoSlugField, self).__init__(
25
            *args,
26
            **kwargs
27
        )
28
29 1
    def deconstruct(self):
30
        """Deconstructs the field into something the database
31
        can store."""
32
33 1
        name, path, args, kwargs = super(
34
            LocalizedAutoSlugField, self).deconstruct()
35
36 1
        kwargs['populate_from'] = self.populate_from
37 1
        kwargs['include_time'] = self.include_time
38 1
        return name, path, args, kwargs
39
40 1
    def formfield(self, **kwargs):
41
        """Gets the form field associated with this field.
42
43
        Because this is a slug field which is automatically
44
        populated, it should be hidden from the form.
45
        """
46
47 1
        defaults = {
48
            'form_class': forms.CharField,
49
            'required': False
50
        }
51
52 1
        defaults.update(kwargs)
53
54 1
        form_field = 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...
55 1
        form_field.widget = forms.HiddenInput()
56
57 1
        return form_field
58
59 1
    def pre_save(self, instance, add: bool):
0 ignored issues
show
Unused Code introduced by
The argument add seems to be unused.
Loading history...
60
        """Ran just before the model is saved, allows us to built
61
        the slug.
62
63
        Arguments:
64
            instance:
65
                The model that is being saved.
66
67
            add:
68
                Indicates whether this is a new entry
69
                to the database or an update.
70
        """
71
72 1
        slugs = LocalizedValue()
73
74 1
        for lang_code, value in self._get_populate_values(instance):
75 1
            if not value:
76
                continue
77
78 1
            if self.include_time:
79
                value += '-%s' % datetime.now().microsecond
80
81 1
            def is_unique(slug: str, language: str) -> bool:
82
                """Gets whether the specified slug is unique."""
83
84 1
                unique_filter = {
85
                    '%s__%s' % (self.name, language): slug
0 ignored issues
show
Bug introduced by
The Instance of LocalizedAutoSlugField 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...
86
                }
87
88 1
                return not type(instance).objects.filter(**unique_filter).exists()
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...
89
90 1
            slug = self._make_unique_slug(
91
                slugify(value, allow_unicode=True),
92
                lang_code,
93
                is_unique
94
            )
95
96 1
            slugs.set(lang_code, slug)
97
98 1
        setattr(instance, self.name, slugs)
0 ignored issues
show
Bug introduced by
The Instance of LocalizedAutoSlugField 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...
99 1
        return slugs
100
101 1
    @staticmethod
102 1
    def _make_unique_slug(slug: str, language: str, is_unique: Callable[[str], bool]) -> str:
103
        """Guarentees that the specified slug is unique by appending
104
        a number until it is unique.
105
106
        Arguments:
107
            slug:
108
                The slug to make unique.
109
110
            is_unique:
111
                Function that can be called to verify
112
                whether the generate slug is unique.
113
114
        Returns:
115
            A guarenteed unique slug.
116
        """
117
118 1
        index = 1
119 1
        unique_slug = slug
120
121 1
        while not is_unique(unique_slug, language):
122 1
            unique_slug = '%s-%d' % (slug, index)
123 1
            index += 1
124
125 1
        return unique_slug
126
127 1
    def _get_populate_values(self, instance) -> Tuple[str, str]:
128
        """Gets all values (for each language) from the
129
        specified's instance's `populate_from` field.
130
131
        Arguments:
132
            instance:
133
                The instance to get the values from.
134
135
        Returns:
136
            A list of (lang_code, value) tuples.
137
        """
138
139 1
        return [
140
            (
141
                lang_code,
142
                self._get_populate_from_value(
143
                    instance,
144
                    self.populate_from,
145
                    lang_code
146
                ),
147
            )
148
            for lang_code, _ in settings.LANGUAGES
149
        ]
150
151 1
    @staticmethod
152 1
    def _get_populate_from_value(instance, field_name: Union[str, Tuple[str]], language: str):
153
        """Gets the value to create a slug from in the specified language.
154
155
        Arguments:
156
            instance:
157
                The model that the field resides on.
158
159
            field_name:
160
                The name of the field to generate a slug for.
161
162
            language:
163
                The language to generate the slug for.
164
165
        Returns:
166
            The text to generate a slug for.
167
        """
168
169 1
        def get_field_value(name):
170 1
            value = resolve_object_property(instance, name)
171 1
            with translation.override(language):
172 1
                return str(value)
173
174 1
        if isinstance(field_name, tuple) or isinstance(field_name, list):
175 1
            value = '-'.join([
176
                value
177
                for value in [get_field_value(name) for name in field_name]
178
                if value
179
            ])
180 1
            return value
181
182
        return get_field_value(field_name)
183