Skip to content

Commit

Permalink
rev: category slug url
Browse files Browse the repository at this point in the history
  • Loading branch information
happychuks committed Dec 13, 2024
1 parent 669144e commit fda2f14
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 5 deletions.
3 changes: 2 additions & 1 deletion server/apps/research/admin/category_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
class CategoryAdmin(admin.ModelAdmin):
"""Admin interface for the Category model."""

list_display = ('name', 'created_at')
list_display = ('name', 'slug', 'created_at')
list_per_page = 25
search_fields = ('name',)
list_filter = ('created_at',)
ordering = ('name',)
readonly_fields = ('slug',)
30 changes: 30 additions & 0 deletions server/apps/research/migrations/0015_category_slug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 5.0.8 on 2024-12-13 07:34

from django.db import migrations, models

def remove_duplicate_slugs(apps, schema_editor):
Category = apps.get_model('research', 'Category')
seen_slugs = {}

for category in Category.objects.order_by('id'):
base_slug = category.slug
counter = 1
while category.slug in seen_slugs:
category.slug = f"{base_slug}-{counter}"
counter += 1
seen_slugs[category.slug] = True
category.save()

class Migration(migrations.Migration):

dependencies = [
('research', '0014_alter_article_authors'),
]

operations = [
migrations.AddField(
model_name='category',
name='slug',
field=models.SlugField(blank=True, max_length=255),
),
]
33 changes: 33 additions & 0 deletions server/apps/research/models/category.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
from django.db import models
from apps.common.models import BaseModel
from django.utils.text import slugify
from django.db import transaction

class Category(BaseModel):
"""Model for categories."""
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, blank=True)

class Meta:
verbose_name_plural = 'Categories'

def save(self, *args, **kwargs):
with transaction.atomic():
try:
if not self.slug:
self.slug = self.generate_slug()
if len(self.slug) > 255:
raise ValueError("Generated slug exceeds maximum length")
except ValueError as e:
raise ValueError(f"Failed to generate valid slug") from e
super().save(*args, **kwargs)

def __str__(self):
return self.name

def generate_slug(self):

if not self.name:
raise ValueError("Name is required to generate slug")

base_slug = slugify(self.name)
slug = base_slug
num = 1
with transaction.atomic():
while (
Category.objects.select_for_update()
.filter(slug=slug)
.exclude(id=self.id) # Exclude current instance when updating
.exists()
):
slug = f"{base_slug}-{num}"
num += 1
return slug
3 changes: 2 additions & 1 deletion server/apps/research/serializers/category_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

class CategorySerializer(serializers.ModelSerializer):
"""Serializer for the Category model."""
slug = serializers.SlugField(read_only=True, max_length=255, help_text='URL-friendly version of the category name.')
class Meta:
model = Category
fields = ['id', 'name']
fields = ['id', 'name', 'slug']
8 changes: 5 additions & 3 deletions server/apps/research/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ def retrieve_by_identifier(self, request, identifier=None):
status=status.HTTP_404_NOT_FOUND)

# Custom action to retrieve articles by category
@action(detail=False, methods=['get'], url_path=r'category/(?P<category>[-\w]+)')
def retrieve_by_category(self, request, category=None):
@action(detail=False, methods=['get'], url_path=r'category/(?P<category_slug>[-\w]+)')
def retrieve_by_category(self, request, category_slug=None):
"""Retrieve article list by category."""
try:
instances = Article.objects.filter(categories__name=category)
instances = Article.objects.filter(categories__slug=category_slug)
if not instances.exists():
return Response({'error': 'No articles found for this category'}, status=status.HTTP_404_NOT_FOUND)
serializer = self.get_serializer(instances, many=True)
return Response({'success': True, 'data': serializer.data})
except Exception as e:
Expand Down

0 comments on commit fda2f14

Please sign in to comment.