Skip to content

Commit cfc223b

Browse files
author
Ross Mechanic
authored
Get default user for bulk ops from middleware (#677)
* Get default user for bulk ops from middleware * add user on init * Added user to bulk ops * Added changes * lint * skip tests
1 parent 66cb8e4 commit cfc223b

File tree

6 files changed

+184
-4
lines changed

6 files changed

+184
-4
lines changed

CHANGES.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
Changes
22
=======
3-
2.10.1 (2020-06-16)
4-
- added ``user_db_constraint`` param to history to avoid circular reference on delete (gh-676)
5-
- added ``clean_old_history`` management command (gh-675)
3+
4+
Unreleased
5+
----------
6+
- Added ``clean_old_history`` management command (gh-675)
7+
- Added ``user_db_constraint`` param to history to avoid circular reference on delete (gh-676)
8+
- Leverages ``get_user`` from ``HistoricalRecords`` in order to set a fallback user on
9+
bulk update and bulk create (gh-677)
610

711
2.10.0 (2020-04-27)
812
-------------------

simple_history/manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,14 @@ def bulk_history_create(
120120

121121
historical_instances = []
122122
for instance in objs:
123+
history_user = getattr(
124+
instance,
125+
"_history_user",
126+
default_user or self.model.get_default_history_user(instance),
127+
)
123128
row = self.model(
124129
history_date=getattr(instance, "_history_date", timezone.now()),
125-
history_user=getattr(instance, "_history_user", default_user),
130+
history_user=history_user,
126131
history_change_reason=get_change_reason_from_object(instance)
127132
or default_change_reason,
128133
history_type=history_type,

simple_history/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,13 @@ def get_prev_record(self):
422422
.last()
423423
)
424424

425+
def get_default_history_user(instance):
426+
"""
427+
Returns the user specified by `get_user` method for manually creating
428+
historical objects
429+
"""
430+
return self.get_history_user(instance)
431+
425432
extra_fields = {
426433
"history_id": self._get_history_id_field(),
427434
"history_date": models.DateTimeField(),
@@ -441,6 +448,7 @@ def get_prev_record(self):
441448
"__str__": lambda self: "{} as of {}".format(
442449
self.history_object, self.history_date
443450
),
451+
"get_default_history_user": staticmethod(get_default_history_user),
444452
}
445453

446454
extra_fields.update(self._get_history_related_field(model))

simple_history/tests/tests/test_middleware.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from datetime import date
2+
from unittest import skipIf
23

4+
import django
35
from django.test import TestCase, override_settings
46
from django.urls import reverse
57

@@ -145,3 +147,91 @@ def test_bucket_member_is_set_on_create_view_when_logged_in(self):
145147
history = bucket_datas.first().history.all()
146148

147149
self.assertListEqual([h.history_user_id for h in history], [member1.id])
150+
151+
152+
@override_settings(**middleware_override_settings)
153+
class MiddlewareBulkOpsTest(TestCase):
154+
def setUp(self):
155+
self.user = CustomUser.objects.create_superuser(
156+
"user_login", "[email protected]", "pass"
157+
)
158+
159+
def test_user_is_set_on_bulk_create_view_when_logged_in(self):
160+
self.client.force_login(self.user)
161+
self.client.post(reverse("poll-bulk-create"), data={})
162+
polls = Poll.objects.all()
163+
self.assertEqual(len(polls), 2)
164+
165+
poll_history = Poll.history.all()
166+
167+
self.assertCountEqual(
168+
[ph.history_user_id for ph in poll_history], [self.user.id, self.user.id]
169+
)
170+
171+
def test_user_is_not_set_on_bulk_create_view_not_logged_in(self):
172+
self.client.post(reverse("poll-bulk-create"), data={})
173+
174+
polls = Poll.objects.all()
175+
self.assertEqual(polls.count(), 2)
176+
177+
poll_history = Poll.history.all()
178+
179+
self.assertListEqual([ph.history_user_id for ph in poll_history], [None, None])
180+
181+
def test_request_user_is_overwritten_by_default_user_on_bulk_create_view(self,):
182+
self.client.force_login(self.user)
183+
self.client.post(reverse("poll-bulk-create-with-default-user"), data={})
184+
185+
polls = Poll.objects.all()
186+
self.assertEqual(len(polls), 2)
187+
188+
poll_history = Poll.history.all()
189+
190+
self.assertFalse(any(ph.history_user_id == self.user.id for ph in poll_history))
191+
self.assertFalse(any(ph.history_user_id is None for ph in poll_history))
192+
193+
@skipIf(django.VERSION < (2, 2,), reason="bulk_update does not exist before 2.2")
194+
def test_user_is_set_on_bulk_update_view_when_logged_in(self):
195+
self.client.force_login(self.user)
196+
poll_1 = Poll.objects.create(question="Test question 1", pub_date=date.today())
197+
poll_2 = Poll.objects.create(
198+
question="Test question 2", pub_date=date(2020, 1, 1)
199+
)
200+
201+
self.client.post(reverse("poll-bulk-update"), data={})
202+
203+
polls = Poll.objects.all()
204+
self.assertEqual(2, len(polls))
205+
206+
self.assertEqual("1", poll_1.history.latest("history_date").question)
207+
self.assertEqual("0", poll_2.history.latest("history_date").question)
208+
self.assertEqual(
209+
self.user.id, poll_1.history.latest("history_date").history_user_id
210+
)
211+
self.assertEqual(
212+
self.user.id, poll_2.history.latest("history_date").history_user_id
213+
)
214+
215+
@skipIf(django.VERSION < (2, 2,), reason="bulk_update does not exist before 2.2")
216+
def test_user_is_not_set_on_bulk_update_view_when_not_logged_in(self):
217+
poll_1 = Poll.objects.create(question="Test question 1", pub_date=date.today())
218+
poll_2 = Poll.objects.create(
219+
question="Test question 2", pub_date=date(2020, 1, 1)
220+
)
221+
222+
self.client.post(reverse("poll-bulk-update"), data={})
223+
224+
self.assertIsNone(poll_1.history.latest("history_date").history_user_id)
225+
self.assertIsNone(poll_2.history.latest("history_date").history_user_id)
226+
227+
@skipIf(django.VERSION < (2, 2,), reason="bulk_update does not exist before 2.2")
228+
def test_request_user_is_overwritten_by_default_user_on_bulk_update(self):
229+
self.client.force_login(self.user)
230+
poll = Poll.objects.create(pub_date=date(2020, 1, 1), question="123")
231+
232+
self.client.post(reverse("poll-bulk-update-with-default-user"), data={})
233+
234+
self.assertIsNotNone(poll.history.latest("history_date").history_user_id)
235+
self.assertNotEqual(
236+
self.user.id, poll.history.latest("history_date").history_user_id
237+
)

simple_history/tests/urls.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
PollList,
1313
PollUpdate,
1414
PollWithHistoricalIPAddressCreate,
15+
PollBulkCreateView,
16+
PollBulkCreateWithDefaultUserView,
17+
PollBulkUpdateView,
18+
PollBulkUpdateWithDefaultUserView,
1519
)
1620
from . import other_admin
1721

@@ -40,4 +44,16 @@
4044
url(r"^poll/(?P<pk>[0-9]+)/delete/$", PollDelete.as_view(), name="poll-delete"),
4145
url(r"^polls/(?P<pk>[0-9]+)/$", PollDetail.as_view(), name="poll-detail"),
4246
url(r"^polls/$", PollList.as_view(), name="poll-list"),
47+
url(r"^polls-bulk-create/$", PollBulkCreateView.as_view(), name="poll-bulk-create"),
48+
url(
49+
r"^polls-bulk-create-default-user/$",
50+
PollBulkCreateWithDefaultUserView.as_view(),
51+
name="poll-bulk-create-with-default-user",
52+
),
53+
url(r"^polls-bulk-update/$", PollBulkUpdateView.as_view(), name="poll-bulk-update"),
54+
url(
55+
r"^polls-bulk-update-default-user/$",
56+
PollBulkUpdateWithDefaultUserView.as_view(),
57+
name="poll-bulk-update-with-default-user",
58+
),
4359
]

simple_history/tests/view.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
from datetime import date
2+
3+
from django.http import HttpResponse
14
from django.urls import reverse_lazy
5+
from django.views import View
26
from django.views.generic import (
37
CreateView,
48
DeleteView,
@@ -7,18 +11,71 @@
711
UpdateView,
812
)
913

14+
from simple_history.tests.custom_user.models import CustomUser
1015
from simple_history.tests.models import (
1116
BucketDataRegisterRequestUser,
1217
Poll,
1318
PollWithHistoricalIPAddress,
1419
)
20+
from simple_history.utils import bulk_create_with_history, bulk_update_with_history
1521

1622

1723
class PollCreate(CreateView):
1824
model = Poll
1925
fields = ["question", "pub_date"]
2026

2127

28+
class PollBulkCreateView(View):
29+
def post(self, request, *args, **kwargs):
30+
poll_info_list = [
31+
{"question": "1", "pub_date": date(2020, 1, 1)},
32+
{"question": "2", "pub_date": date(2020, 1, 2)},
33+
]
34+
polls_to_create = [Poll(**poll_info) for poll_info in poll_info_list]
35+
bulk_create_with_history(polls_to_create, Poll)
36+
return HttpResponse(status=200)
37+
38+
39+
class PollBulkCreateWithDefaultUserView(View):
40+
def post(self, request, *args, **kwargs):
41+
default_user = CustomUser.objects.create_superuser(
42+
"test_user", "[email protected]", "pass"
43+
)
44+
# Bulk create objects with history
45+
poll_info_list = [
46+
{"question": "1", "pub_date": date(2020, 1, 1)},
47+
{"question": "2", "pub_date": date(2020, 1, 2)},
48+
]
49+
polls_to_create = [Poll(**poll_info) for poll_info in poll_info_list]
50+
bulk_create_with_history(polls_to_create, Poll, default_user=default_user)
51+
return HttpResponse(status=200)
52+
53+
54+
class PollBulkUpdateView(View):
55+
def post(self, request, *args, **kwargs):
56+
polls = Poll.objects.order_by("pub_date")
57+
for i, poll in enumerate(polls):
58+
poll.question = str(i)
59+
60+
bulk_update_with_history(polls, fields=["question"], model=Poll)
61+
return HttpResponse(status=201)
62+
63+
64+
class PollBulkUpdateWithDefaultUserView(View):
65+
def post(self, request, *args, **kwargs):
66+
default_user = CustomUser.objects.create_superuser(
67+
"test_user", "[email protected]", "pass"
68+
)
69+
polls = Poll.objects.all()
70+
for i, poll in enumerate(polls):
71+
poll.question = str(i)
72+
73+
bulk_update_with_history(
74+
polls, fields=["question"], model=Poll, default_user=default_user
75+
)
76+
return HttpResponse(status=201)
77+
78+
2279
class PollWithHistoricalIPAddressCreate(CreateView):
2380
model = PollWithHistoricalIPAddress
2481
fields = ["question", "pub_date"]

0 commit comments

Comments
 (0)