I want to log actions made by users. In most OO languages, I would implement this via a LoggedAction class, having several child classes like LoginActionand LogoutAction. I could then iterate over a list of LoggedActions and get the specific child behaviour through virtual inheritance. This does not work using Django models however.
Example models.py:
class LoggedAction(models.Model):
user = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return "%s: %s %s" % (unicode(self.timestamp), unicode(self.user), unicode(self.action()))
def action(self):
return ""
class LoginAction(LoggedAction):
def action(self):
return "logged in"
class LogoutAction(LoggedAction):
def action(self):
return "logged out"
Then I’d like to do [unicode(l) for l in LoggedAction.objects.all()] and get a list of messages like u'2012-02-18 18:47:09.105840: knatten logged in'.
As expected, this does not work, since what I get from all() is a list of LoggedAction objects having either a loginaction member or a logoutaction member. (The output is a list of messages like u'2012-02-18 18:47:09.105840: knatten, with no mention of the action.)
Is there a sane way to get the behaviour I’m after, or am I trying to apply the wrong paradigm here? (I guess I am, and that I should just have the specific action as a member in LoggedAction)
Yes, this is probably the wrong paradigm. It’s easy to be misled by the object-relational mapper (ORM) – database tables don’t really map all that well to objects, and this difference is known as the object-relational impedance mismatch.
What you actually need is to make
actiona field. This field can take achoicesparameter which represents the possible values of that field – ie logged in or logged out:Note that I’ve used arbitrary single-character strings to represent the actions, and the
get_action_display()magic method to get the full description.