Passed
Push — master ( 2e9b83...b9a4d3 )
by Swen
01:53
created

LocalizedUniqueSlugField.deconstruct()   A

Complexity

Conditions 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 1
rs 9.4285
1 1
from datetime import datetime
2
3 1
from django.conf import settings
4 1
from django.utils.text import slugify
5 1
from django.core.exceptions import ImproperlyConfigured
6
7 1
from ..util import get_language_codes
8 1
from ..mixins import AtomicSlugRetryMixin
9 1
from ..localized_value import LocalizedValue
10 1
from .localized_autoslug_field import LocalizedAutoSlugField
11
12
13 1
class LocalizedUniqueSlugField(LocalizedAutoSlugField):
14
    """Automatically provides slugs for a localized
15
    field upon saving."
16
17
    An improved version of :see:LocalizedAutoSlugField,
18
    which adds:
19
20
        - Concurrency safety
21
        - Improved performance
22
23
    When in doubt, use this over :see:LocalizedAutoSlugField.
24
    Inherit from :see:AtomicSlugRetryMixin in your model to
25
    make this field work properly.
26
    """
27
28 1
    def __init__(self, *args, **kwargs):
29
        """Initializes a new instance of :see:LocalizedUniqueSlugField."""
30
31 1
        kwargs['uniqueness'] = kwargs.pop('uniqueness', get_language_codes())
32
33 1
        super(LocalizedUniqueSlugField, self).__init__(
34
            *args,
35
            **kwargs
36
        )
37
38 1
        self.populate_from = kwargs.pop('populate_from')
39 1
        self.include_time = kwargs.pop('include_time', False)
40
41 1
    def deconstruct(self):
42
        """Deconstructs the field into something the database
43
        can store."""
44
45 1
        name, path, args, kwargs = super(
46
            LocalizedUniqueSlugField, self).deconstruct()
47
48 1
        kwargs['populate_from'] = self.populate_from
49 1
        kwargs['include_time'] = self.include_time
50 1
        return name, path, args, kwargs
51
52 1
    def pre_save(self, instance, add: bool):
53
        """Ran just before the model is saved, allows us to built
54
        the slug.
55
56
        Arguments:
57
            instance:
58
                The model that is being saved.
59
60
            add:
61
                Indicates whether this is a new entry
62
                to the database or an update.
63
64
        Returns:
65
            The localized slug that was generated.
66
        """
67
68 1
        if not isinstance(instance, AtomicSlugRetryMixin):
69
            raise ImproperlyConfigured((
70
                'Model \'%s\' does not inherit from AtomicSlugRetryMixin. '
71
                'Without this, the LocalizedUniqueSlugField will not work.'
72
            ) % type(instance).__name__)
73
74 1
        slugs = LocalizedValue()
75
76 1
        for lang_code, _ in settings.LANGUAGES:
77 1
            value = self._get_populate_from_value(
78
                instance,
79
                self.populate_from,
80
                lang_code
81
            )
82
83 1
            if not value:
84 1
                continue
85
86 1
            slug = slugify(value, allow_unicode=True)
87
88
            # verify whether it's needed to re-generate a slug,
89
            # if not, re-use the same slug
90 1
            if instance.pk is not None:
91 1
                current_slug = getattr(instance, self.name).get(lang_code)
92 1
                if current_slug is not None:
93 1
                    stripped_slug = current_slug[0:current_slug.rfind('-')]
94 1
                    if slug == stripped_slug:
95 1
                        slugs.set(lang_code, current_slug)
96 1
                        continue
97
98 1
            if self.include_time:
99 1
                slug += '-%d' % datetime.now().microsecond
100
101 1
            if instance.retries > 0:
102
                # do not add another - if we already added time
103 1
                if not self.include_time:
104 1
                    slug += '-'
105 1
                slug += '%d' % instance.retries
106
107 1
            slugs.set(lang_code, slug)
108
109 1
        setattr(instance, self.name, slugs)
110
        return slugs
111