from typing import Any, Dict, Optional, Type, cast from django.shortcuts import redirect from django.urls import reverse from django.views.generic import DetailView from django.views import View from django.http import HttpRequest, HttpResponse from django.db.models import Model class SlugRedirectMixin(View): """ Mixin that handles redirects for old slugs. Requires the model to inherit from SluggedModel and view to inherit from DetailView. """ model: Optional[Type[Model]] = None slug_url_kwarg: str = 'slug' object: Optional[Model] = None def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: # Only apply slug redirect logic to DetailViews if not isinstance(self, DetailView): return super().dispatch(request, *args, **kwargs) # Get the object using current or historical slug try: self.object = self.get_object() # type: ignore # Check if we used an old slug current_slug = kwargs.get(self.slug_url_kwarg) if current_slug and current_slug != getattr(self.object, 'slug', None): # Get the URL pattern name from the view url_pattern = self.get_redirect_url_pattern() # Build kwargs for reverse() reverse_kwargs = self.get_redirect_url_kwargs() # Redirect to the current slug URL return redirect( reverse(url_pattern, kwargs=reverse_kwargs), permanent=True ) return super().dispatch(request, *args, **kwargs) except (AttributeError, Exception) as e: # type: ignore if self.model and hasattr(self.model, 'DoesNotExist'): if isinstance(e, self.model.DoesNotExist): # type: ignore return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs) def get_redirect_url_pattern(self) -> str: """ Get the URL pattern name for redirects. Should be overridden by subclasses. """ raise NotImplementedError( "Subclasses must implement get_redirect_url_pattern()" ) def get_redirect_url_kwargs(self) -> Dict[str, Any]: """ Get the kwargs for reverse() when redirecting. Should be overridden by subclasses if they need custom kwargs. """ if not self.object: return {} return {self.slug_url_kwarg: getattr(self.object, 'slug', '')}