Skip to content

Commit 39266d2

Browse files
authored
Add slug field for Event (#60)
1 parent 7525747 commit 39266d2

File tree

9 files changed

+107
-4
lines changed

9 files changed

+107
-4
lines changed

events/admin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ class EventAdmin(admin.ModelAdmin):
5252

5353
fieldsets = (
5454
(None, {
55-
'fields': ('creator', 'title', 'description', 'event_time',
55+
'fields': ('creator', 'title', 'slug', 'description', 'event_time',
5656
'event_type', 'status', 'is_featured',
57-
'tags', 'playlists', 'videoasset')
57+
'tags', 'playlists', 'videoasset',)
5858
}),
5959
)
6060

events/apps.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ class EventsConfig(AppConfig):
55
""" Configuration for the events app """
66
default_auto_field = 'django.db.models.BigAutoField'
77
name = 'events'
8+
9+
def ready(self):
10+
import events.signals # pylint: disable=import-outside-toplevel, unused-import

events/forms.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,16 @@ class Meta:
5353

5454
def __init__(self, *args, **kwargs):
5555
super().__init__(*args, **kwargs)
56-
5756
current_event = self.instance
57+
58+
# Disable slug field if the instance is new
59+
if current_event and current_event.pk:
60+
self.fields['slug'].disabled = False
61+
else:
62+
self.fields['slug'].disabled = True
63+
64+
# Populate videoasset queryset based on the current event
65+
# If the event is new, show all video assets without an event
5866
qs = VideoAsset.objects.filter(event__isnull=True)
5967

6068
if current_event.pk:
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.16 on 2025-06-10 01:47
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('events', '0008_alter_event_tags'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='event',
15+
name='slug',
16+
field=models.SlugField(blank=True, max_length=255, null=True),
17+
),
18+
]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Generated by Django 4.2.16 on 2025-06-10 02:02
2+
3+
from django.db import migrations
4+
from django.utils.text import slugify
5+
6+
def populate_event_slugs(apps, schema_editor):
7+
Event = apps.get_model('events', 'Event')
8+
9+
titles_seen = {}
10+
11+
for event in Event.objects.all().order_by('id'):
12+
if not event.slug:
13+
base_slug = slugify(event.title)
14+
15+
if base_slug in titles_seen:
16+
event.slug = f"{base_slug}-{event.id}"
17+
else:
18+
if Event.objects.filter(title=event.title).exclude(pk=event.pk).exists():
19+
event.slug = f"{base_slug}-{event.id}"
20+
else:
21+
event.slug = base_slug
22+
23+
titles_seen[base_slug] = event.id
24+
25+
event.save(update_fields=['slug'])
26+
27+
class Migration(migrations.Migration):
28+
29+
dependencies = [
30+
('events', '0009_event_slug'),
31+
]
32+
33+
operations = [
34+
migrations.RunPython(populate_event_slugs, reverse_code=migrations.RunPython.noop),
35+
]

events/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class EventStatus(models.TextChoices):
5959

6060
creator = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='events')
6161
title = models.CharField(max_length=255)
62+
slug = models.SlugField(max_length=255, blank=True, null=True)
6263
description = models.TextField()
6364
event_time = models.DateTimeField()
6465
event_type = models.CharField(max_length=20, choices=EventType.choices, default=EventType.SESSION)

events/signals.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from django.db.models.signals import post_save
2+
from django.dispatch import receiver
3+
from django.utils.text import slugify
4+
5+
from events.models import Event
6+
7+
8+
@receiver(post_save, sender=Event)
9+
def set_slug_on_create(_sender, instance, created, **kwargs):
10+
"""
11+
Set a slug for the event instance if it is created and does not have a slug.
12+
The slug is generated from the title and includes the event ID to ensure uniqueness.
13+
"""
14+
if created and not instance.slug:
15+
base_slug = slugify(instance.title)
16+
exists_with_title = Event.objects.filter(title=instance.title).exclude(pk=instance.pk).exists()
17+
18+
if exists_with_title:
19+
slug = f"{base_slug}-{instance.id}"
20+
else:
21+
slug = base_slug
22+
23+
instance.slug = slug
24+
instance.save(update_fields=['slug'])

events/tests/conftest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import pytest
2+
3+
from django.db.models.signals import post_save
4+
5+
from events.models import Event
6+
from events.signals import set_slug_on_create
7+
8+
9+
@pytest.fixture(autouse=True)
10+
def disable_event_signals():
11+
""" Disable the post_save signal for Event during tests """
12+
post_save.disconnect(set_slug_on_create, sender=Event)
13+
yield
14+
post_save.connect(set_slug_on_create, sender=Event)

events/v1/serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class EventSerializer(serializers.ModelSerializer):
4141
class Meta:
4242
model = Event
4343
fields = (
44-
'id', 'title', 'description', 'publisher', 'event_time',
44+
'id', 'title', 'slug', 'description', 'publisher', 'event_time',
4545
'event_type', 'status', 'is_featured', 'tags',
4646
'thumbnail', 'video_duration', 'presenters', 'playlists',
4747
'video_file'

0 commit comments

Comments
 (0)