File: user_tracking.rst

package info (click to toggle)
python-django-simple-history 3.7.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,124 kB
  • sloc: python: 8,454; makefile: 186
file content (172 lines) | stat: -rw-r--r-- 6,045 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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).

.. _`_history_user section`:

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:

.. code-block:: python

    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 :doc:`/admin`).

Another option for identifying the change user is by providing a function via ``get_user``.
If provided it will be called every time that the ``history_user`` needs to be
identified with the following key word arguments:

* ``instance``:  The current instance being modified
* ``request``:  If using the middleware the current request object will be provided if they are authenticated.

This is very helpful when using ``register``:

.. code-block:: python

    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`:


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:

1. You are working on a Django app with multiple databases, and your history table
   is in a separate database from the user table.

2. 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)`` or
    ``UUIDField(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:

.. code-block:: python

    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:

.. code-block:: python

    def _history_user_setter(historical_instance, user):
        if user is not None:
            historical_instance.history_user_id = user.pk


.. _`Change User Model`:

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.

.. code-block:: python

    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