PageForm.clean_slug()   F
last analyzed

Complexity

Conditions 19

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 56
cc 19
rs 3.4219

1 Method

Rating   Name   Duplication   Size   Complexity  
A PageForm.is_slug_safe() 0 9 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like PageForm.clean_slug() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
"""Page CMS forms"""
3
from django import forms
4
from django.template.defaultfilters import slugify
5
from django.utils.translation import ugettext_lazy as _
6
from django.conf import settings as global_settings
7
8
from pages import settings
9
from pages.models import Page, Content
10
11
from pages.urlconf_registry import get_choices
12
from pages.widgets import LanguageChoiceWidget
13
import collections
14
15
error_dict = {
16
    'another_page_error': _('Another page with this slug already exists'),
17
    'sibling_position_error': _('A sibling with this slug already exists at the targeted position'),
18
    'child_error': _('A child with this slug already exists at the targeted position'),
19
    'sibling_error': _('A sibling with this slug already exists'),
20
    'sibling_root_error': _('A sibling with this slug already exists at the root level'),
21
}
22
23
def automatic_slug_renaming(slug, is_slug_safe):
24
    """Helper to add numbers to slugs"""
25
26
    if not isinstance(is_slug_safe, collections.Callable):
27
        raise TypeError('is_slug_safe must be callable')
28
29
    if is_slug_safe(slug):
30
       return slug
31
32
    count = 2
33
    new_slug = slug + "-" + str(count)
34
    while not is_slug_safe(new_slug):
35
        count = count + 1
36
        new_slug = slug + "-" + str(count)
37
    return new_slug
38
39
def unique_slug_required(form, slug):
40
    """Enforce a unique slug accross all pages and websistes."""
41
42
    if hasattr(form, 'instance') and form.instance.id:
43
        if Content.objects.exclude(page=form.instance).filter(
44
            body=slug, type="slug").count():
45
            raise forms.ValidationError(error_dict['another_page_error'])
46
    elif Content.objects.filter(body=slug, type="slug").count():
47
        raise forms.ValidationError(error_dict['another_page_error'])
48
    return slug
49
50
def intersect_sites_method(form):
51
    """Return a method to intersect sites."""
52
    if settings.PAGE_USE_SITE_ID:
53
        if settings.PAGE_HIDE_SITES:
54
            site_ids = [global_settings.SITE_ID]
55
        else:
56
            site_ids = [int(x) for x in form.data.getlist('sites')]
57
        def intersects_sites(sibling):
58
            return sibling.sites.filter(id__in=site_ids).count() > 0
59
    else:
60
        def intersects_sites(sibling):
61
            return True
62
    return intersects_sites
63
64
def make_form(model_, placeholders):
65
66
    # a new form is needed every single time as some
67
    # initial data are bound
68
    class PageForm(forms.ModelForm):
69
        """Form for page creation"""
70
71
        def __init__(self, *args, **kwargs):
72
            super(PageForm, self).__init__(*args, **kwargs)
73
            for p in placeholders:
74
                if not self.fields[p.ctype]:
75
                    self.fields[p.ctype] = forms.TextField()
76
77
        target = forms.IntegerField(required=False, widget=forms.HiddenInput)
78
        position = forms.CharField(required=False, widget=forms.HiddenInput)
79
80
        class Meta:
81
            model = model_
82
            exclude = ('author', 'last_modification_date', 'parent')
83
84
        title = forms.CharField(
85
            label=_('Title'),
86
            widget=forms.TextInput(),
87
        )
88
        slug = forms.CharField(
89
            label=_('Slug'),
90
            widget=forms.TextInput(),
91
            help_text=_('The slug will be used to create the page URL, it must be unique among the other pages of the same level.')
92
        )
93
94
        language = forms.ChoiceField(
95
            label=_('Language'),
96
            choices=settings.PAGE_LANGUAGES,
97
            widget=LanguageChoiceWidget()
98
        )
99
        template = forms.ChoiceField(
100
            required=False,
101
            label=_('Template'),
102
            choices=settings.get_page_templates(),
103
        )
104
        delegate_to = forms.ChoiceField(
105
            required=False,
106
            label=_('Delegate to application'),
107
            choices=get_choices(),
108
        )
109
        freeze_date = forms.DateTimeField(
110
            required=False,
111
            label=_('Freeze'),
112
            help_text=_("Don't publish any content after this date. Format is 'Y-m-d H:M:S'")
113
            # those make tests fail miserably
114
            #widget=widgets.AdminSplitDateTime()
115
            #widget=widgets.AdminTimeWidget()
116
        )
117
118
        def clean_slug(self):
119
            """Handle move action on the pages"""
120
121
            slug = slugify(self.cleaned_data['slug'])
122
            target = self.data.get('target', None)
123
            position = self.data.get('position', None)
124
125
            # this enforce a unique slug for every page
126
            if settings.PAGE_AUTOMATIC_SLUG_RENAMING:
127
                def is_slug_safe(slug):
128
                    content = Content.objects.get_content_slug_by_slug(slug)
129
                    if content is None:
130
                        return True
131
                    if self.instance.id:
132
                        if content.page.id == self.instance.id:
133
                            return True
134
                    else:
135
                        return False
136
137
                return automatic_slug_renaming(slug, is_slug_safe)
138
139
            if settings.PAGE_UNIQUE_SLUG_REQUIRED:
140
                # We can return here as not futher checks
141
                # are necessary
142
                return unique_slug_required(self, slug)
143
144
            intersects_sites = intersect_sites_method(self)
145
146
            if not settings.PAGE_UNIQUE_SLUG_REQUIRED:
147
                if target and position:
148
                    target = Page.objects.get(pk=target)
149
                    if position in ['right', 'left']:
150
                        slugs = [sibling.slug() for sibling in
151
                                target.get_siblings()
152
                                if intersects_sites(sibling)]
153
                        slugs.append(target.slug())
154
                        if slug in slugs:
155
                            raise forms.ValidationError(error_dict['sibling_position_error'])
156
                    if position == 'first-child':
157
                        if slug in [sibling.slug() for sibling in
158
                                    target.get_children()
159
                                    if intersects_sites(sibling)]:
160
                            raise forms.ValidationError(error_dict['child_error'])
161
                else:
162
                    if self.instance.id:
163
                        if (slug in [sibling.slug() for sibling in
164
                            self.instance.get_siblings().exclude(
165
                                id=self.instance.id
166
                            ) if intersects_sites(sibling)]):
167
                            raise forms.ValidationError(error_dict['sibling_error'])
168
                    else:
169
                        if slug in [sibling.slug() for sibling in
170
                                    Page.objects.root()
171
                                    if intersects_sites(sibling)]:
172
                            raise forms.ValidationError(error_dict['sibling_root_error'])
173
            return slug
174
175
    return PageForm
176