Reputation: 120
I have a number of classes (e.g. Contact) for which I also want to track the change history. Below is my starting point. I imagine this is a common use case, so would appreciate guidance on best practice here. Would this be a good place to use multiple inheritance, and what would that look like?
from datetime import date
class ContactHistory(object):
def __init__(self, contact, change_action, change_user_id, change_source):
self.name = contact.name
self.phone = contact.phone
self.email = contact.email
self.change_action = change_action
self.change_user_id = change_user_id
self.change_source = change_source
self.change_date = date.today()
def __repr__(self):
return '%s, %s, %s, %s, %s, %s, %s' % (self.name, self.email, self.phone, self.change_action, self.change_user_id, self.change_source, self.change_date)
class Contact(object):
def __init__(self, name, phone, email, change_user_id, change_source):
self.name = name
self.phone = phone
self.email = email
self.history = []
self.history.append(ContactHistory(self, 'created', change_user_id, change_source))
def update_phone(self, phone, change_user_id, change_source):
self.phone = phone
self.history.append(ContactHistory(self, 'phone updated', change_user_id, change_source))
def get_history(self):
return self.history
contact = Contact('Bill', '214-555-1212', 'me', 'admin page')
contact.update('972-555-1212', 'me', 'contact management page')
print contact.get_history()
Upvotes: 2
Views: 201
Reputation: 18653
Here's one way you could use inheritance to approach this. ChangeableObject
could be inherited by any object that needed to be changeable, with that object just describing the parameters that are used to create a shallow clone for history:
from collections import namedtuple
Change = namedtuple("Change", ("old", "new", "action"))
class History(object):
def __init__(self):
self.history = []
def save_change(self, old, new, action):
change = Change(old, new, action)
self.history.append(change)
def get_history(self):
return self.history
class ChangeableObject(object):
def __init__(self, make_history=True):
if make_history:
self.history = History()
self.history.save_change(None, self, "created")
self.cloneable_attributes = ()
@classmethod
def get_clone(cls, obj):
attrs = {attr: getattr(obj, attr) for attr in obj.cloneable_attributes}
return cls(make_history=False, **attrs)
def view_history(self):
return self.history.get_history()
class Contact(ChangeableObject):
def __init__(self, name, phone, email, make_history=True):
super(Contact, self).__init__(make_history=make_history)
self.name = name
self.phone = phone
self.email = email
self.cloneable_attributes = ("name", "phone", "email")
def update_phone(self, phone):
clone = self.get_clone(self)
self.phone = phone
self.history.save_change(clone, self, "phone updated")
def __repr__(self):
return "{} {} {}".format(self.name, self.phone, self.email)
With example code:
c = Contact("me", "123-123-123", "[email protected]")
c.update_phone("456-456-456")
print c
for i, hist in enumerate(c.view_history()):
print "{}. {}".format(i, hist)
Output:
me 456-456-456 [email protected]
0. Change(old=None, new=me 456-456-456 [email protected], action='created')
1. Change(old=me 123-123-123 [email protected], new=me 456-456-456 [email protected], action='phone updated')
Upvotes: 1