0

I have a main Event class, and four event types. Each of them is a subclass of (at least) Event. On an event's detail page, I am adding a link back to the admin interface for a given event to save the time of searching in the admin. Unfortunately, regardless of child class, the events are selected from the parent Event class. My solution to this problem was to duck type, which I find to be absolutely awful in this instance. I am hoping there is a more elegant and easy-to-maintain solution to my problem.

Model:

class Event(models.Model):
    ...

class ListedEvent(Event):
    ....

class RSVPEvent(Event):
    ....

class TicketedEvent(Event):
    ....

class TicketedConcert(TicketedEvent):
    ....

To see event details, the URL passes a venue slug and event name slug. This is enough info to isolate a single event from the parent Event model across all the child events. It also lets me keep the event type out of the URL, making them simpler and more friendly.

@render_to('events/event_details.html')
def event_details(request, venue, slug):
    """
    Detail view of an event.
    """
    try:
        event = Event.objects.select_related(
            'listedevent',
            'rsvpevent',
            'ticketedevent',
            'ticketedconcert',
            'venue',
            'sites',
            'dj',
        ).get(
            slug=slug,
            venue__slug=venue,
        )
    except Event.DoesNotExist:
        raise Http404

    return {'event': event}

Before I went back and realized that I was using the parent Event model, this solution was much more elegant and worked fine in the shell assuming I select an event from its actual model (property of parent class Event):

@property
def admin_link(self):
    et = self.__class__.__name__.lower()
    # ALWAYS: et == 'event', reverse() fails, returns ''
    return reverse('admin:events_%s_change' % et, args=(self.id,))

My current solution (property of parent class Event):

@property
def admin_link(self):
    duck = None

    try:
        duck = self.ticketedevent.ticketedconcert.artist_name
        return reverse(
            'admin:events_ticketedconcert_change',
            args=(self.id,)
        )
    except:
        pass

    try:
        duck = self.ticketedevent.max_tickets
        return reverse(
            'admin:events_ticketedevent_change',
            args=(self.id,)
        )
    except:
        pass

    try:
        duck = self.rsvpevent.total_rsvp
        return reverse(
            'admin:events_rsvpevent_change',
            args=(self.id,)
        )
    except:
        pass

    try:
        duck = self.listedevent.name
        return reverse(
            'admin:events_listedevent_change',
            args=(self.id,)
        )
    except:
        pass

There has to be a simpler, more easy-to-maintain method of finding out which type of event I'm looking at. Any ideas?

Jon Clements
  • 138,671
  • 33
  • 247
  • 280
Bryson
  • 1,186
  • 2
  • 13
  • 26

1 Answers1

2

This is a common issue and still seems to be one of Django's uglier ducklings.

You could at least rearrange for 2 cents of DRY gain:

from django.core.exceptions import ObjectDoesNotExist

@property
def event_type(self):
     for et in ('ticketedevent', 'rsvpevent', 'sillyhatsonlyevent', ...):
         try:
             getattr(self, et)
             return et
         except ObjectDoesNotExist:
             pass

@property
def admin_link(self):
    return reverse('admin:events_%s_change' % self.event_type, args=(self.id,))

If you really want to be fancy, you could use Event.__subclasses__ and generate the list of event types.

Community
  • 1
  • 1
Jesse the Game
  • 2,600
  • 16
  • 21