Search code examples
djangodjango-modelsdjango-viewsdjango-urlsslug

handling duplicate objects with custom slugs via mixins


we have a django app with two objects, cities and events. a common url pattern for us would be:

app.com/city/name-of-city/event/name-of-event where name-of-city and name-of-event are slugs. ex:

class City(models.Model):
      name = models.CharField(max_length=100, unique=True)
      slug = AutoSlugField(populate_from='name', always_update=True, unique=True, sep='-', max_length=255)
      ...

events get their slug the same way on the Event model:

slug = AutoSlugField(populate_from='name', always_update=True, unique_with='city', sep='-', max_length=255)

the issue I'm having difficulty sorting out is how to handle an instance where the same city has multiple events with the same name.

mixins.py

class CityMixin(object):
    city_slug_url_kwarg = 'slug'
    city = None

    def fetch_city(self, request, *args, **kwargs):
        if not self.city:
            self.city = get_object_or_404(
                City, slug=kwargs.get(self.city_slug_url_kwarg))
        return self.city

    def get_context_data(self, **kwargs):
        kwargs.setdefault('city', self.city)
        return super(CityMixin, self).get_context_data(**kwargs)

    def dispatch(self, request, *args, **kwargs):
        """
        Sets 'city' within the view
        """
        self.city = self.fetch_city(request, *args, **kwargs)
        return super(CityMixin, self).dispatch(request, *args, **kwargs)


class EventMixin(CityMixin):
    city_slug_url_kwarg = 'city_slug'
    event_slug_url_kwarg = 'slug'
    event = None

    def fetch_event(self, request, *args, **kwargs):

        if not self.event:
            event = get_list_or_404(
                Event, slug=kwargs.get(self.event_slug_url_kwarg))

        return self.event

    def dispatch(self, request, *args, **kwargs):
        """
        Sets 'city' and 'event' within the view
        """
        user = request.user
        self.city = self.fetch_city(request, *args, **kwargs)
        self.event = self.fetch_event(request, *args, **kwargs)
        if self.event.city != self.city:
            raise Http404

        return super(EventMixin, self).dispatch(request, *args, **kwargs)

if I request event with:

event = get_object_or_404(Event, slug=kwargs.get(self.event_slug_url_kwarg))

it will throw an err (MultipleObjectsReturned) if there are multiple identically-named events.

if I request event with:

event = get_list_or_404(Event, slug=kwargs.get(self.event_slug_url_kwarg))

and there are identical events the app doesn't know which obj or index in the list is the correct obj.

is there a method that will ensure the right obj is requested that can also handle the duplicate Event names case?

thanks


Solution

  • The slugs will always be unique per city, because you've set that unique_with attribute. But you're not using the city in the query, so you'll get events for all cities - which can use the same slug. You need to use the city in the lookup:

    event = get_list_or_404(
        Event,
        slug=kwargs.get(self.event_slug_url_kwarg),
        city__slug=kwargs.get(self.city_slug_url_kwarg)
    )