The application I’m writing includes a pipeline for creating events. An Event is proposed by one class of users, then approved and edited by administrators. The problem for me is that there is a subclass of Event, ScoredEvent, which the administrators should be able to specify on-the-fly. This adds a ScoredEvent.competition foreign key and overrides the Event.participants to put them through a table that includes an associated score for each participant.
Ideally, a normal user creates an event containing only the name and a short description (easy enough to do by limiting the fields on the non-admin CreateEventForm) then the admins can go back in and fill out the other fields when they are approving the event.
The problem I’m hitting is I don’t know how it would be possible for an administrator to change an Event to a ScoredEvent in the approval form view when they are editing it or how to make that happen. My vision of the page is an edit view with a checkbox labeled “Tie To Competition” that, when checked, would allow the admin to select a competition and then save the event as a ScoredEvent. If that box weren’t checked, the event would continue it’s life as an Event.
Where should this be handled? My gut feeling is that I should do something special in the forms.py or something in the Templates, but I don’t know where I should begin.
class Event(models.Model):
"""Representation of any community event"""
name = models.CharField(max_length=50, unique_for_date="start_datetime")
slug = models.SlugField(max_length=57) # length +7 for datestamp
description = models.TextField()
location = models.CharField(max_length=100, blank=True)
start_datetime = models.DateTimeField('Start', blank=True)
end_datetime = models.DateTimeField('End', blank=True)
participants = models.ManyToManyField(Participant)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)+'-'+datetime.now().strftime("%d%m%y")
super(Event, self).save(*args, **kwargs)
def __unicode__(self):
if self.start_datetime:
return "%s (%s)" % (self.name, self.start_datetime.date())
else:
return self.name
class ScoredEvent(Event):
"""Representation of an event that is tied to a competition"""
competition = models.ForeignKey(Competition)
participants = models.ManyToManyField(Participant, through="ScoredParticipant")
def is_scored(self):
"""Returns true if any of the participants has a score, else returns false"""
for participant in self.participants.objects.all():
if participant.score != 0:
return False
return True
class ScoredPartcipant(models.Model):
"""Participant and associated score for an event"""
participant = models.ForeignKey(Participant)
event = models.ForeignKey(ScoredEvent)
score = models.IntegerField(default=0)
Override
get_querysetand vary on whether one of the fields is posted:So, just to explain a little better: you would use your “Tie to Competition” option you mentioned to add a field to the form via Javascript. Then, if the field is sent in the POST, it would switch to using
ScoredEventinstead.If you want to automatically switch based on what the object currently is, you’d need to override
get_objectfor that. Something along the lines of:Which basically tries to lookup the object again as if it was a
ScoredEventand returns that version if it finds one, instead ofEvent.You also might need to similarly override
get_form_classto ensure that it validates as aScoredEventshould if it’s aScoredEventor is becoming aScoredEvent: