currently active, otherwise we can't see from the return value whether a given time predates the history set. The downside is that the caller may now need to check whether it got a history object or the same object back. - Legacy-Id: 4560
61 lines
2.2 KiB
Python
61 lines
2.2 KiB
Python
def find_history_active_at(obj, time):
|
|
"""Assumes obj has a corresponding history model (e.g. obj could
|
|
be Person with a corresponding PersonHistory model), then either
|
|
returns the object itself if it was active at time, or the history
|
|
object active at time, or None if time predates the object and its
|
|
history (assuming history is complete).
|
|
|
|
For this to work, the history model must use
|
|
related_name="history_set" for the foreign key connecting to the
|
|
live model, both models must have a "time" DateTimeField and a
|
|
history object must be saved with a copy of the old values and
|
|
old time when the time field changes.
|
|
"""
|
|
if obj.time <= time:
|
|
return obj
|
|
|
|
histories = obj.history_set.order_by('-time')
|
|
|
|
for h in histories:
|
|
if h.time <= time:
|
|
return h
|
|
|
|
return None
|
|
|
|
def get_history_object_for(obj):
|
|
"""Construct history object for obj, i.e. instantiate history
|
|
object, copy relevant attributes and set a link to obj, but done
|
|
save. Any customizations can be done by the caller afterwards.
|
|
Many-to-many fields are not copied.
|
|
|
|
The history model must use related_name="history_set" for the
|
|
foreign key connecting to the live model for this function to be
|
|
able to discover it."""
|
|
|
|
history_model = obj.history_set.model
|
|
h = history_model()
|
|
|
|
# copy attributes shared between history and obj
|
|
history_field_names = set(f.name for f in history_model._meta.fields)
|
|
|
|
for field in obj._meta.fields:
|
|
if field is not obj._meta.pk and field.name in history_field_names:
|
|
setattr(h, field.name, getattr(obj, field.name))
|
|
|
|
# try setting foreign key to obj
|
|
key_name = obj._meta.object_name.lower()
|
|
if key_name in history_field_names:
|
|
setattr(h, key_name, obj)
|
|
|
|
# we can't copy many-to-many fields as h isn't saved yet, leave
|
|
# that to caller
|
|
|
|
return h
|
|
|
|
def copy_many_to_many_for_history(history_obj, obj):
|
|
"""Copy basic many-to-many fields from obj to history_obj."""
|
|
# copy many to many
|
|
for field in obj._meta.many_to_many:
|
|
if field.rel.through and field.rel.through._meta.auto_created:
|
|
setattr(history_obj, field.name, getattr(obj, field.name).all())
|