0001-use-unique-category-slugs-in-urls-13791.patch
corbo/forms.py | ||
---|---|---|
1 | 1 |
from django import forms |
2 | 2 |
from django.utils.translation import ugettext_lazy as _ |
3 | 3 |
from django.utils.text import slugify |
4 |
from django.core.exceptions import ObjectDoesNotExist |
|
4 | 5 | |
5 | 6 |
from .models import Announce, Category, Broadcast, channel_choices |
6 | 7 | |
... | ... | |
31 | 32 |
model = Category |
32 | 33 | |
33 | 34 |
def save(self, commit=True): |
35 |
slug = slugify(self.instance.name) |
|
36 |
base_slug = slug |
|
34 | 37 |
if not self.instance.slug: |
35 |
self.instance.slug = slugify(self.instance.name) |
|
38 |
i = 1 |
|
39 |
while True: |
|
40 |
try: |
|
41 |
c = Category.objects.get(slug=slug) |
|
42 |
except ObjectDoesNotExist: |
|
43 |
break |
|
44 |
i += 1 |
|
45 |
slug = '%s-%s' % (base_slug, i) |
|
46 |
self.instance.slug = slug |
|
36 | 47 |
return super(CategoryForm, self).save(commit=commit) |
corbo/manage_urls.py | ||
---|---|---|
6 | 6 | |
7 | 7 |
urlpatterns = patterns('', |
8 | 8 |
url(r'^$', manage, name='manage'), |
9 |
url(r'^category/(?P<pk>\d+)/announce/$', add_announce,
|
|
9 |
url(r'^category/(?P<slug>[\w-]+)/announce/$', add_announce,
|
|
10 | 10 |
name='add_announce'), |
11 | 11 |
url(r'^announce/edit/(?P<pk>\d+)$', edit_announce, |
12 | 12 |
name='edit_announce'), |
13 | 13 |
url(r'^announce/delete/(?P<pk>\d+)$', delete_announce, |
14 | 14 |
name='delete_announce'), |
15 |
url(r'^category/(?P<pk>\d+)/$', view_category,
|
|
15 |
url(r'^category/(?P<slug>[\w-]+)/$', view_category,
|
|
16 | 16 |
name='view_category'), |
17 | 17 |
url(r'^category/add$', add_category, |
18 | 18 |
name='add_category'), |
19 |
url(r'^category/edit/(?P<pk>\d+)$', edit_category,
|
|
19 |
url(r'^category/edit/(?P<slug>[\w-]+)$', edit_category,
|
|
20 | 20 |
name='edit_category'), |
21 |
url(r'^category/delete/(?P<pk>\d+)$', delete_category,
|
|
21 |
url(r'^category/delete/(?P<slug>[\w-]+)$', delete_category,
|
|
22 | 22 |
name='delete_category'), |
23 | 23 |
url(r'^menu.json$', menu_json), |
24 | 24 |
) |
corbo/migrations/0009_auto_20170120_1533.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 |
from django.utils.text import slugify |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('corbo', '0008_category_slug'), |
|
12 |
] |
|
13 | ||
14 |
def make_slug_unique(apps, schema_editor): |
|
15 |
Category = apps.get_model('corbo', 'Category') |
|
16 |
for category in Category.objects.all(): |
|
17 |
category.slug = slugify(category.name) |
|
18 |
category.save() |
|
19 | ||
20 |
operations = [ |
|
21 |
migrations.RunPython(make_slug_unique), |
|
22 |
migrations.AlterField( |
|
23 |
model_name='category', |
|
24 |
name='slug', |
|
25 |
field=models.SlugField(unique=True, verbose_name='Slug'), |
|
26 |
), |
|
27 |
] |
corbo/models.py | ||
---|---|---|
43 | 43 | |
44 | 44 |
class Category(models.Model): |
45 | 45 |
name = models.CharField(_('Name'), max_length=64, blank=False, null=False) |
46 |
slug = models.SlugField(_('Slug')) |
|
46 |
slug = models.SlugField(_('Slug'), unique=True)
|
|
47 | 47 |
rss_feed_url = models.URLField(_('Feed URL'), blank=True, null=True, |
48 | 48 |
help_text=_('if defined, announces will be automatically created from rss items')) |
49 | 49 |
ctime = models.DateTimeField(auto_now_add=True) |
corbo/templates/corbo/announce_form.html | ||
---|---|---|
4 | 4 |
{% block breadcrumb %} |
5 | 5 |
{{ block.super }} |
6 | 6 |
{% if category %} |
7 |
<a href='{% url "view_category" pk=category.pk %}'>{{ category }}</a>
|
|
7 |
<a href='{% url "view_category" slug=category.slug %}'>{{ category }}</a>
|
|
8 | 8 |
{% endif %} |
9 | 9 |
{% endblock %} |
10 | 10 | |
... | ... | |
26 | 26 |
{{ form.as_p }} |
27 | 27 |
<div class="buttons"> |
28 | 28 |
<button>{% trans "Save" %}</button> |
29 |
<a class="cancel" href="{% url 'view_category' pk=category.pk %}">{% trans 'Cancel' %}</a>
|
|
29 |
<a class="cancel" href="{% url 'view_category' slug=category.slug %}">{% trans 'Cancel' %}</a>
|
|
30 | 30 |
</div> |
31 | 31 |
<script type="text/javascript"> |
32 | 32 |
$(function() { |
corbo/templates/corbo/category_detail.html | ||
---|---|---|
4 | 4 |
{% block breadcrumb %} |
5 | 5 |
{{ block.super }} |
6 | 6 |
{% if category %} |
7 |
<a href='{% url "view_category" pk=category.pk %}'>{{ category }}</a>
|
|
7 |
<a href='{% url "view_category" slug=category.slug %}'>{{ category }}</a>
|
|
8 | 8 |
{% endif %} |
9 | 9 |
{% endblock %} |
10 | 10 | |
11 | 11 |
{% block appbar %} |
12 | 12 |
<h2>{{ object.name }}</h2> |
13 |
<a href="{% url 'delete_category' object.id %}" rel="popup">{% trans 'Delete' %}</a>
|
|
14 |
<a href="{% url 'edit_category' object.id %}" rel="popup">{% trans 'Edit' %}</a>
|
|
15 |
<a href="{% url 'add_announce' pk=object.pk %}">{% trans 'New announce' %}</a>
|
|
13 |
<a href="{% url 'delete_category' slug=object.slug %}" rel="popup">{% trans 'Delete' %}</a>
|
|
14 |
<a href="{% url 'edit_category' slug=object.slug %}" rel="popup">{% trans 'Edit' %}</a>
|
|
15 |
<a href="{% url 'add_announce' slug=object.slug %}">{% trans 'New announce' %}</a>
|
|
16 | 16 |
{% endblock %} |
17 | 17 | |
18 | 18 |
{% block content %} |
corbo/templates/corbo/manage.html | ||
---|---|---|
11 | 11 |
<ul class='objects-list single-links'> |
12 | 12 |
{% for obj in object_list %} |
13 | 13 |
<li class='category'> |
14 |
<a href="{% url 'view_category' pk=obj.id %}">{{ obj.name }} /
|
|
14 |
<a href="{% url 'view_category' slug=obj.slug %}">{{ obj.name }} /
|
|
15 | 15 |
{% blocktrans count announces_number=obj.get_announces_count %} |
16 | 16 |
{{ announces_number }} announce |
17 | 17 |
{% plural %} |
corbo/views.py | ||
---|---|---|
58 | 58 |
template_name = 'corbo/announce_form.html' |
59 | 59 | |
60 | 60 |
def get_success_url(self): |
61 |
return reverse('view_category', kwargs={'pk': self.object.category.pk})
|
|
61 |
return reverse('view_category', kwargs={'slug': self.object.category.slug})
|
|
62 | 62 | |
63 | 63 |
def get_initial(self): |
64 | 64 |
initial = super(AnnounceCreateView, self).get_initial() |
65 |
initial['category'] = models.Category.objects.get(pk=self.kwargs['pk'])
|
|
65 |
initial['category'] = models.Category.objects.get(slug=self.kwargs['slug'])
|
|
66 | 66 |
return initial |
67 | 67 | |
68 | 68 |
def get_context_data(self, **kwargs): |
... | ... | |
95 | 95 |
model = models.Announce |
96 | 96 | |
97 | 97 |
def get_success_url(self): |
98 |
return reverse('view_category', kwargs={'pk': self.object.category.pk})
|
|
98 |
return reverse('view_category', kwargs={'slug': self.object.category.slug})
|
|
99 | 99 | |
100 | 100 | |
101 | 101 |
delete_announce = AnnounceDeleteView.as_view() |
... | ... | |
121 | 121 |
model = models.Category |
122 | 122 | |
123 | 123 |
def get_success_url(self): |
124 |
return reverse('view_category', kwargs={'pk': self.object.pk})
|
|
124 |
return reverse('view_category', kwargs={'slug': self.object.slug})
|
|
125 | 125 | |
126 | 126 | |
127 | 127 |
edit_category = CategoryEditView.as_view() |
tests/test_emailing.py | ||
---|---|---|
9 | 9 |
from django.core import mail, signing |
10 | 10 |
from django.utils import timezone |
11 | 11 |
from django.core.files.storage import DefaultStorage |
12 |
from django.utils.text import slugify |
|
12 | 13 | |
13 | 14 |
from corbo.models import Category, Announce, Subscription, Broadcast, transform_image_src |
14 | 15 | |
15 | 16 |
pytestmark = pytest.mark.django_db |
16 | 17 | |
17 |
CATEGORIES = ('Alerts', 'News')
|
|
18 |
CATEGORIES = (u'Alerts', u'News')
|
|
18 | 19 | |
19 | 20 | |
20 | 21 |
@pytest.fixture |
21 | 22 |
def categories(): |
22 | 23 |
categories = [] |
23 | 24 |
for category in CATEGORIES: |
24 |
c, created = Category.objects.get_or_create(name=category) |
|
25 |
c, created = Category.objects.get_or_create(name=category, slug=slugify(category))
|
|
25 | 26 |
categories.append(c) |
26 | 27 |
return categories |
27 | 28 | |
28 |
- |