Passed
Push — master ( 69718c...20d9e6 )
by Swen
02:10
created

LocalizedAutoSlugField.formfield()   A

Complexity

Conditions 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 17
rs 9.4285
ccs 6
cts 6
cp 1
crap 1
1 1
from typing import Callable
2
3 1
from django import forms
4 1
from django.conf import settings
5 1
from django.utils.text import slugify
6
7 1
from ..forms import LocalizedFieldForm
8 1
from .localized_field import LocalizedField
9 1
from .localized_value import LocalizedValue
10
11
12 1
class LocalizedAutoSlugField(LocalizedField):
13
    """Custom version of :see:AutoSlugField that
14
    can operate on :see:LocalizedField and provides
15
    unique slugs for every language."""
16
17 1
    def __init__(self, *args, **kwargs):
18
        """Initializes a new instance of :see:LocalizedAutoSlugField."""
19
20 1
        self.populate_from = kwargs.pop('populate_from', None)
21 1
        super(LocalizedAutoSlugField, self).__init__(*args, **kwargs)
22
23 1
    def deconstruct(self):
24
        """Deconstructs the field into something the database
25
        can store."""
26
27 1
        name, path, args, kwargs = super(
28
            LocalizedAutoSlugField, self).deconstruct()
29 1
        kwargs['populate_from'] = self.populate_from
30
31 1
        return name, path, args, kwargs
32
33 1
    def formfield(self, **kwargs):
34
        """Gets the form field associated with this field.
35
36
        Because this is a slug field which is automatically
37
        populated, it should be hidden from the form.
38
        """
39
40 1
        defaults = {
41
            'form_class': LocalizedFieldForm
42
        }
43
44 1
        defaults.update(kwargs)
45
46 1
        form_field = super().formfield(**defaults)
47 1
        form_field.widget = forms.HiddenInput()
48
49 1
        return form_field
50
51 1
    def pre_save(self, instance, add: bool):
52
        """Ran just before the model is saved, allows us to built
53
        the slug.
54
55
        Arguments:
56
            instance:
57
                The model that is being saved.
58
59
            add:
60
                Indicates whether this is a new entry
61
                to the database or an update.
62
        """
63
64 1
        slugs = LocalizedValue()
65
66 1
        for lang_code, _ in settings.LANGUAGES:
67 1
            value = self._get_populate_from_value(
68
                instance,
69
                self.populate_from,
70
                lang_code
71
            )
72
73 1
            if not value:
74 1
                continue
75
76 1
            def is_unique(slug: str, language: str) -> bool:
77
                """Gets whether the specified slug is unique."""
78
79 1
                unique_filter = {
80
                    '%s__%s__contains' % (self.name, language): slug
81
                }
82
83 1
                return not type(instance).objects.filter(**unique_filter).exists()
84
85 1
            slug = self._make_unique_slug(slugify(value), lang_code, is_unique)
86 1
            slugs.set(lang_code, slug)
87
88 1
        setattr(instance, self.name, slugs)
89 1
        return slugs
90
91 1
    @staticmethod
92 1
    def _make_unique_slug(slug: str, language: str, is_unique: Callable[[str], bool]) -> str:
93
        """Guarentees that the specified slug is unique by appending
94
        a number until it is unique.
95
96
        Arguments:
97
            slug:
98
                The slug to make unique.
99
100
            is_unique:
101
                Function that can be called to verify
102
                whether the generate slug is unique.
103
104
        Returns:
105
            A guarenteed unique slug.
106
        """
107
108 1
        index = 1
109 1
        unique_slug = slug
110
111 1
        while not is_unique(unique_slug, language):
112 1
            unique_slug = '%s-%d' % (slug, index)
113 1
            index += 1
114
115 1
        return unique_slug
116
117 1
    @staticmethod
118 1
    def _get_populate_from_value(instance, field_name: str, language: str):
119
        """Gets the value to create a slug from in the specified language.
120
121
        Arguments:
122
            instance:
123
                The model that the field resides on.
124
125
            field_name:
126
                The name of the field to generate a slug for.
127
128
            language:
129
                The language to generate the slug for.
130
131
        Returns:
132
            The text to generate a slug for.
133
        """
134
135 1
        value = getattr(instance, field_name, None)
136
        return value.get(language)
137