Projet

Général

Profil

0001-agendas-add-slug-form-absence-reason-model-53147.patch

Lauréline Guérin, 15 avril 2021 15:33

Télécharger (10,2 ko)

Voir les différences:

Subject: [PATCH 1/3] agendas: add slug form absence reason model (#53147)

 chrono/agendas/migrations/0079_reason_slug.py | 20 ++++++++++
 chrono/agendas/migrations/0080_reason_slug.py | 40 +++++++++++++++++++
 chrono/agendas/migrations/0081_reason_slug.py | 16 ++++++++
 chrono/agendas/models.py                      | 11 +++++
 chrono/manager/forms.py                       | 14 +++++++
 chrono/manager/views.py                       |  3 +-
 tests/manager/test_absence_reason.py          | 22 ++++++++--
 tests/test_agendas.py                         | 20 ++++++++++
 8 files changed, 142 insertions(+), 4 deletions(-)
 create mode 100644 chrono/agendas/migrations/0079_reason_slug.py
 create mode 100644 chrono/agendas/migrations/0080_reason_slug.py
 create mode 100644 chrono/agendas/migrations/0081_reason_slug.py
chrono/agendas/migrations/0079_reason_slug.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('agendas', '0078_absence_reasons'),
8
    ]
9

  
10
    operations = [
11
        migrations.AddField(
12
            model_name='absencereason',
13
            name='slug',
14
            field=models.SlugField(max_length=160, null=True, verbose_name='Identifier'),
15
        ),
16
        migrations.AlterUniqueTogether(
17
            name='absencereason',
18
            unique_together=['group', 'slug'],
19
        ),
20
    ]
chrono/agendas/migrations/0080_reason_slug.py
1
from django.db import migrations
2
from django.utils.text import slugify
3

  
4

  
5
def generate_slug(instance, **query_filters):
6
    base_slug = slugify(instance.label)
7
    slug = base_slug
8
    i = 1
9
    while True:
10
        queryset = instance._meta.model.objects.filter(slug=slug, **query_filters).exclude(pk=instance.pk)
11
        if not queryset.exists():
12
            break
13
        slug = '%s-%s' % (base_slug, i)
14
        i += 1
15
    return slug
16

  
17

  
18
def set_slug_on_absence_reasons(apps, schema_editor):
19
    AbsenceReason = apps.get_model('agendas', 'AbsenceReason')
20
    for reason in AbsenceReason.objects.all().order_by('-pk'):
21
        if (
22
            reason.slug
23
            and not AbsenceReason.objects.filter(slug=reason.slug, group=reason.group)
24
            .exclude(pk=reason.pk)
25
            .exists()
26
        ):
27
            continue
28
        reason.slug = generate_slug(reason, group=reason.group)
29
        reason.save(update_fields=['slug'])
30

  
31

  
32
class Migration(migrations.Migration):
33

  
34
    dependencies = [
35
        ('agendas', '0079_reason_slug'),
36
    ]
37

  
38
    operations = [
39
        migrations.RunPython(set_slug_on_absence_reasons, migrations.RunPython.noop),
40
    ]
chrono/agendas/migrations/0081_reason_slug.py
1
from django.db import migrations, models
2

  
3

  
4
class Migration(migrations.Migration):
5

  
6
    dependencies = [
7
        ('agendas', '0080_reason_slug'),
8
    ]
9

  
10
    operations = [
11
        migrations.AlterField(
12
            model_name='absencereason',
13
            name='slug',
14
            field=models.SlugField(max_length=160, verbose_name='Identifier'),
15
        ),
16
    ]
chrono/agendas/models.py
2455 2455

  
2456 2456
class AbsenceReason(models.Model):
2457 2457
    group = models.ForeignKey(AbsenceReasonGroup, on_delete=models.CASCADE, related_name='absence_reasons')
2458
    slug = models.SlugField(_('Identifier'), max_length=160)
2458 2459
    label = models.CharField(_('Label'), max_length=150)
2459 2460

  
2460 2461
    class Meta:
2461 2462
        ordering = ['label']
2463
        unique_together = ['group', 'slug']
2462 2464

  
2463 2465
    def __str__(self):
2464 2466
        return self.label
2467

  
2468
    def save(self, *args, **kwargs):
2469
        if not self.slug:
2470
            self.slug = generate_slug(self, group=self.group)
2471
        super().save(*args, **kwargs)
2472

  
2473
    @property
2474
    def base_slug(self):
2475
        return slugify(self.label)
chrono/manager/forms.py
53 53
from .widgets import SplitDateTimeField
54 54

  
55 55

  
56
class AbsenceReasonForm(forms.ModelForm):
57
    class Meta:
58
        model = AbsenceReason
59
        fields = ['label', 'slug']
60

  
61
    def clean_slug(self):
62
        slug = self.cleaned_data['slug']
63

  
64
        if self.instance.group.absence_reasons.filter(slug=slug).exclude(pk=self.instance.pk).exists():
65
            raise ValidationError(_('Another absence reason exists with the same identifier.'))
66

  
67
        return slug
68

  
69

  
56 70
class AgendaAddForm(forms.ModelForm):
57 71
    edit_role = forms.ModelChoiceField(
58 72
        label=_('Edit Role'), required=False, queryset=Group.objects.all().order_by('name')
chrono/manager/views.py
79 79
)
80 80

  
81 81
from .forms import (
82
    AbsenceReasonForm,
82 83
    AgendaAddForm,
83 84
    AgendaEditForm,
84 85
    AgendaBookingDelaysForm,
......
703 704
class AbsenceReasonEditView(UpdateView):
704 705
    template_name = 'chrono/manager_absence_reason_form.html'
705 706
    model = AbsenceReason
706
    fields = ['label']
707
    form_class = AbsenceReasonForm
707 708

  
708 709
    def dispatch(self, request, *args, **kwargs):
709 710
        self.group_pk = kwargs.pop('group_pk')
tests/manager/test_absence_reason.py
68 68

  
69 69
def test_edit_group(app, admin_user):
70 70
    group = AbsenceReasonGroup.objects.create(label='Foo bar')
71
    group2 = AbsenceReasonGroup.objects.create(label='baz')
71 72

  
72 73
    app = login(app)
73 74
    resp = app.get('/manage/absence-reasons/')
74 75
    resp = resp.click(href='/manage/absence-reason/group/%s/edit/' % group.pk)
75 76
    resp.form['label'] = 'Foo bar baz'
76
    resp.form['slug'] = 'baz'
77
    resp.form['slug'] = group2.slug
78
    resp = resp.form.submit()
79
    assert resp.context['form'].errors['slug'] == [
80
        'Absence reason group with this Identifier already exists.'
81
    ]
82

  
83
    resp.form['slug'] = 'baz2'
77 84
    resp = resp.form.submit()
78 85
    assert resp.location.endswith('/manage/absence-reasons/')
79 86
    group.refresh_from_db()
80 87
    assert group.label == 'Foo bar baz'
81
    assert group.slug == 'baz'
88
    assert group.slug == 'baz2'
82 89

  
83 90

  
84 91
def test_edit_group_as_manager(app, manager_user, agenda_with_restrictions):
......
121 128
    assert resp.location.endswith('/manage/absence-reasons/')
122 129
    assert reason.label == 'Foo reason'
123 130
    assert reason.group == group
131
    assert reason.slug == 'foo-reason'
124 132

  
125 133

  
126 134
def test_add_reason_as_manager(app, manager_user, agenda_with_restrictions):
......
133 141
def test_edit_reason(app, admin_user):
134 142
    group = AbsenceReasonGroup.objects.create(label='Foo bar')
135 143
    reason = AbsenceReason.objects.create(label='Foo reason', group=group)
144
    reason2 = AbsenceReason.objects.create(label='Baz', group=group)
145
    group2 = AbsenceReasonGroup.objects.create(label='Foo bar')
146
    reason3 = AbsenceReason.objects.create(label='Foo bar reason', group=group2)
136 147

  
137 148
    app = login(app)
138 149
    resp = app.get('/manage/absence-reasons/')
139 150
    resp = resp.click(href='/manage/absence-reason/group/%s/%s/edit/' % (group.pk, reason.pk))
140 151
    resp.form['label'] = 'Foo bar reason'
152
    resp.form['slug'] = reason2.slug
153
    resp = resp.form.submit()
154
    assert resp.context['form'].errors['slug'] == ['Another absence reason exists with the same identifier.']
155

  
156
    resp.form['slug'] = reason3.slug
141 157
    resp = resp.form.submit()
142 158
    assert resp.location.endswith('/manage/absence-reasons/')
143 159
    reason.refresh_from_db()
144 160
    assert reason.label == 'Foo bar reason'
161
    assert reason.slug == 'foo-bar-reason'
145 162

  
146
    group2 = AbsenceReasonGroup.objects.create(label='Foo bar baz')
147 163
    app.get('/manage/absence-reason/group/%s/%s/edit/' % (group2.pk, reason.pk), status=404)
148 164

  
149 165

  
tests/test_agendas.py
14 14
from django.utils.timezone import localtime, make_aware, now
15 15

  
16 16
from chrono.agendas.models import (
17
    AbsenceReasonGroup,
17 18
    Agenda,
18 19
    Booking,
19 20
    Category,
......
177 178
    assert category.slug == 'foo-baz-2'
178 179

  
179 180

  
181
def test_absence_reason_group_slug():
182
    group = AbsenceReasonGroup.objects.create(label=u'Foo bar')
183
    assert group.slug == 'foo-bar'
184

  
185

  
186
def test_absence_reason_group_existing_slug():
187
    group = AbsenceReasonGroup.objects.create(label=u'Foo bar', slug='bar')
188
    assert group.slug == 'bar'
189

  
190

  
191
def test_absence_reason_group_duplicate_slugs():
192
    group = AbsenceReasonGroup.objects.create(label=u'Foo baz')
193
    assert group.slug == 'foo-baz'
194
    group = AbsenceReasonGroup.objects.create(label=u'Foo baz')
195
    assert group.slug == 'foo-baz-1'
196
    group = AbsenceReasonGroup.objects.create(label=u'Foo baz')
197
    assert group.slug == 'foo-baz-2'
198

  
199

  
180 200
@pytest.mark.parametrize('with_prefetch', [True, False])
181 201
def test_agenda_is_available_for_simple_management(settings, with_prefetch):
182 202
    settings.EXCEPTIONS_SOURCES = {
183
-