User Tracking¶
Recording Which User Changed a Model¶
There are four documented ways to attach users to a tracked change:
1. Use the HistoryRequestMiddleware
. The middleware sets the
User instance that made the request as the history_user
on the history
table.
2. Use simple_history.admin.SimpleHistoryAdmin
. Under the hood,
SimpleHistoryAdmin
actually sets the _history_user
on the object to
attach the user to the tracked change by overriding the save_model method.
3. Assign a user to the _history_user
attribute of the object as described
in the _history_user section.
4. Track the user using an explicit history_user_id
, which is described in
Manually Track User Model. This method is particularly useful when using multiple
databases (where your user model lives in a separate database to your historical model),
or when using a user that doesn’t live within the Django app (i.e. a user model retrieved
from an API).
Using _history_user
to Record Which User Changed a Model¶
To denote which user changed a model, assign a _history_user
attribute on
your model.
For example if you have a changed_by
field on your model that records which
user last changed the model, you could create a _history_user
property
referencing the changed_by
field:
from django.db import models
from simple_history.models import HistoricalRecords
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
changed_by = models.ForeignKey('auth.User')
history = HistoricalRecords()
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
self.changed_by = value
Admin integration requires that you use a _history_user.setter
attribute with
your custom _history_user
property (see Admin Integration).
Another option for identifying the change user is by providing a function via get_user
.
If provided it will be called everytime that the history_user
needs to be
identified with the following key word arguments:
instance
: The current instance being modifiedrequest
: If using the middleware the current request object will be provided if they are authenticated.
This is very helpful when using register
:
from django.db import models
from simple_history.models import HistoricalRecords
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
changed_by = models.ForeignKey('auth.User')
def get_poll_user(instance, **kwargs):
return instance.changed_by
register(Poll, get_user=get_poll_user)
Manually Track User Model¶
Although django-simple-history
tracks the history_user
(the user who changed the
model) using a django foreign key, there are instances where we might want to track this
user but cannot use a Django foreign key.
Note: If you want to track a custom user model that is still accessible through a Django foreign key, refer to Change User Model.
The two most common cases where this feature will be helpful are:
You are working on a Django app with multiple databases, and your history table is in a separate database from the user table.
The user model that you want to use for
history_user
does not live within the Django app, but is only accessible elsewhere (i.e. through an API call).
There are three parameters to HistoricalRecords
or register
that facilitate
the ability to manually track a history_user
.
- history_user_id_field
An instance of field (i.e.
IntegerField(null=True)
orUUIDField(default=uuid.uuid4, null=True)
that will uniquely identify your user object. This is generally the field type of the primary key on your user object.- history_user_getter
optional. A callable that takes the historical instance of the model and returns the
history_user
object. The default getter is shown below:
def _history_user_getter(historical_instance):
if historical_instance.history_user_id is None:
return None
User = get_user_model()
try:
return User.objects.get(pk=historical_instance.history_user_id)
except User.DoesNotExist:
return None
- history_user_setter
optional. A callable that takes the historical instance and the user instance, and sets
history_user_id
on the historical instance. The default setter is shown below:
def _history_user_setter(historical_instance, user):
if user is not None:
historical_instance.history_user_id = user.pk
Change User Model¶
If you need to use a different user model then settings.AUTH_USER_MODEL
,
pass in the required model to user_model
. Doing this requires _history_user
or get_user
is provided as detailed above.
from django.db import models
from simple_history.models import HistoricalRecords
class PollUser(models.Model):
user_id = models.ForeignKey('auth.User')
# Only PollUsers should be modifying a Poll
class Poll(models.Model):
question = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
changed_by = models.ForeignKey(PollUser)
history = HistoricalRecords(user_model=PollUser)
@property
def _history_user(self):
return self.changed_by
@_history_user.setter
def _history_user(self, value):
self.changed_by = value