Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/aggregating_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,4 @@ Unlike individual event models, only the `pgh_context` field can be proxied on t
## Using the `MiddlewareEvents` model

If you use [the middleware](context.md#middleware) to attach context on requests, you can make use of `pghistory.models.MiddlewareEvents`, which attaches a `user` and `url` field that correspond to the attributes captured by the middleware.
When `settings.AUTH_USER_MODEL` is defined, the `user` field will be a foreign key to your `User` model. If it's `None`, for example when the `django.contrib.auth` app is not installed, it will be a plain `TextField`.
39 changes: 30 additions & 9 deletions pghistory/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,21 +683,42 @@ def check(cls, **kwargs):
return [error for error in errors if error.id != "models.E017"]


def _middleware_events_user_field():
"""
Return the MiddlewareEvents user proxy field.

Uses a ForeignKey when AUTH_USER_MODEL's app is installed, otherwise a
TextField so pghistory can import without the user model app present.
"""
auth_user_model = getattr(settings, "AUTH_USER_MODEL", None)
if auth_user_model:
try:
app_label, _ = auth_user_model.split(".", 1)
except ValueError:
app_label = None
if app_label and apps.is_installed(app_label):
return core.ProxyField(
"pgh_context__user",
models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.DO_NOTHING,
null=True,
help_text="The user associated with the event.",
),
)
return core.ProxyField(
"pgh_context__user",
models.TextField(null=True, help_text="The user associated with the event."),
)


class MiddlewareEvents(Events):
"""
A proxy model for aggregating events. Includes additional fields that
are captured by the pghistory middleware
"""

user = core.ProxyField(
"pgh_context__user",
models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.DO_NOTHING,
null=True,
help_text="The user associated with the event.",
),
)
user = _middleware_events_user_field()
url = core.ProxyField(
"pgh_context__url",
models.TextField(null=True, help_text="The url associated with the event."),
Expand Down
29 changes: 29 additions & 0 deletions pghistory/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.db import models

from pghistory import models as pgh_models


def test_middleware_events_user_field_foreign_key(mocker):
mocker.patch("pghistory.models.settings.AUTH_USER_MODEL", "auth.User")
mocker.patch("pghistory.models.apps.is_installed", return_value=True)
field = pgh_models._middleware_events_user_field()
assert isinstance(field, models.ForeignKey)


def test_middleware_events_user_field_textfield(mocker):
mocker.patch("pghistory.models.settings.AUTH_USER_MODEL", "myapp.User")
mocker.patch("pghistory.models.apps.is_installed", return_value=False)
field = pgh_models._middleware_events_user_field()
assert isinstance(field, models.TextField)


def test_middleware_events_user_field_textfield_no_auth_user_model(mocker):
mocker.patch("pghistory.models.settings.AUTH_USER_MODEL", None)
field = pgh_models._middleware_events_user_field()
assert isinstance(field, models.TextField)


def test_middleware_events_user_field_textfield_invalid_auth_user_model(mocker):
mocker.patch("pghistory.models.settings.AUTH_USER_MODEL", "invalid")
field = pgh_models._middleware_events_user_field()
assert isinstance(field, models.TextField)