Skip to content
Merged
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
41 changes: 24 additions & 17 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,45 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
django_version: [ '3.2', '4.0', '4.1', '4.2' ]
python_version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ]
django_version: [ '4.2', '5.2', '6.0']
python_version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' ]
exclude:
- django_version: '3.2'
python_version: '3.11'
- django_version: '4.2'
python_version: '3.13'

- django_version: '4.0'
python_version: '3.11'
- django_version: '4.2'
python_version: '3.14'

- django_version: '4.1'
python_version: '3.11'
- django_version: '5.2'
python_version: '3.8'

- django_version: '4.0'
python_version: '3.7'
- django_version: '5.2'
python_version: '3.9'

- django_version: '4.1'
python_version: '3.7'
- django_version: '6.0'
python_version: '3.8'

- django_version: '6.0'
python_version: '3.9'

- django_version: '6.0'
python_version: '3.10'

- django_version: '6.0'
python_version: '3.11'

- django_version: '4.2'
python_version: '3.7'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python_version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python_version }}
- name: Cache pip
uses: actions/cache@v2
uses: actions/cache@v4
with:
# This path is specific to Ubuntu
path: ~/.cache/pip
Expand Down
43 changes: 43 additions & 0 deletions tracking_fields/middleware/cuser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from __future__ import unicode_literals
import threading
from django.contrib.auth import get_user_model


class CuserMiddleware:
_thread_local = threading.local()

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
try:
self.__class__.set_user(request.user)
response = self.get_response(request)
return response
finally:
self.__class__.del_user()

@classmethod
def get_user(cls, default=None):
"""
Retrieve user info
"""
return getattr(cls._thread_local, 'user', default)

@classmethod
def set_user(cls, user):
"""
Store user info
"""
if isinstance(user, str):
user_model = get_user_model()
user = user_model.objects.get(username=user)
cls._thread_local.user = user

@classmethod
def del_user(cls):
"""
Delete user info
"""
if hasattr(cls._thread_local, 'user'):
del cls._thread_local.user
1 change: 1 addition & 0 deletions tracking_fields/tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"tracking_fields.middleware.cuser.CuserMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
)
Expand Down
22 changes: 1 addition & 21 deletions tracking_fields/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import datetime
import json

from cuser.middleware import CuserMiddleware
from tracking_fields.middleware.cuser import CuserMiddleware
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.files import File
Expand Down Expand Up @@ -55,26 +55,6 @@ def test_create(self):
assert human_event.user == self.user
assert human_event.user_repr == self.user_repr

def test_update_without_cuser(self):
"""
Test the CREATE event without the cuser module
"""
from tracking_fields import tracking

tracking.CUSER = False
self.human.age = 43
self.human.save()
events = TrackingEvent.objects.order_by("date").all()
assert events.count() == 3
human_event = events.last()
assert human_event.date is not None
assert human_event.action == UPDATE
assert human_event.object == self.human
assert human_event.object_repr == self.human_repr
assert human_event.user is None
assert human_event.user_repr == "None"
tracking.CUSER = True

def test_update(self):
"""
Test the UPDATE event
Expand Down
20 changes: 7 additions & 13 deletions tracking_fields/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import datetime
import json
import logging
import uuid

from tracking_fields.middleware.cuser import CuserMiddleware
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import ManyToManyField, Model
Expand All @@ -15,13 +17,6 @@
except ImportError:
StateWrapper = type("StateWrapper", (object,), dict())

try:
from cuser.middleware import CuserMiddleware

CUSER = True
except ImportError:
CUSER = False

from tracking_fields.models import (
CREATE,
DELETE,
Expand Down Expand Up @@ -116,13 +111,10 @@ def _create_event(instance, action):
"""
Create a new event, getting the use if django-cuser is available.
"""
user = None
user = CuserMiddleware.get_user()
user_repr = repr(user)
if CUSER:
user = CuserMiddleware.get_user()
user_repr = repr(user)
if user is not None and user.is_anonymous:
user = None
if user is not None and user.is_anonymous:
user = None
return TrackingEvent.objects.create(
action=action,
object_content_type=ContentType.objects.get_for_model(instance),
Expand All @@ -144,6 +136,8 @@ def _serialize_field(field):
except ValueError:
# No file
return json.dumps(None, ensure_ascii=False)
if isinstance(field, uuid.UUID):
return json.dumps(str(field), ensure_ascii=False)
if isinstance(field, Model):
return json.dumps(str(field), ensure_ascii=False)
if isinstance(field, StateWrapper):
Expand Down