Skip to content

Commit 7d6e84b

Browse files
erikvwRoss Mechanic
authored andcommitted
create historical instance with respect to using() for multidb enviro… (#507)
* create historical instance with respect to using() for multidb environment. * add name to authors * add name to authors * code formatting * update CHANGES * revert post delete method to current version, add using * rename second DB to other to be consistent with Django docs * add section on multiple databases to documentation. * minor edit to Multiple Databases section in documents * minor edit to Multiple Databases section in documents
1 parent 65785a0 commit 7d6e84b

File tree

7 files changed

+141
-7
lines changed

7 files changed

+141
-7
lines changed

AUTHORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Authors
2828
- David Grochowski (`ThePumpingLemma <https://github.com/ThePumpingLemma>`_)
2929
- David Hite
3030
- Eduardo Cuducos
31+
- Erik van Widenfelt (`erikvw <https://github.com/erikvw>`_)
3132
- Filipe Pina (@fopina)
3233
- Florian Eßer
3334
- Frank Sachsenheim

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
Changes
22
=======
33

4+
Unreleased
5+
----------
6+
- Add support for `using` chained manager method and save/delete keyword argument (gh-507)
7+
48
2.6.0 (2018-12-12)
59
------------------
610
- Add `app` parameter to the constructor of `HistoricalRecords` (gh-486)

docs/advanced.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,36 @@ be accomplished by setting ``cascade_delete_history=True``.
486486
history = HistoricalRecords(cascade_delete_history=True)
487487
488488
489+
Multiple databases
490+
------------------
491+
`django-simple-history` follows the Django conventions for interacting with multiple databases.
492+
493+
.. code-block:: python
494+
495+
>>> # This will create a new historical record on the 'other' database.
496+
>>> poll = Poll.objects.using('other').create(question='Question 1')
497+
498+
>>> # This will also create a new historical record on the 'other' database.
499+
>>> poll.save(using='other')
500+
501+
502+
When interacting with ``QuerySets``, use ``using()``:
503+
504+
.. code-block:: python
505+
506+
>>> # This will return a QuerySet from the 'other' database.
507+
Poll.history.using('other').all()
508+
509+
When interacting with manager methods, use ``db_manager()``:
510+
511+
.. code-block:: python
512+
513+
>>> # This will call a manager method on the 'other' database.
514+
>>> poll.history.db_manager('other').as_of(datetime(2010, 10, 25, 18, 4, 0))
515+
516+
See the Django documentation for more information on how to interact with multiple databases.
517+
518+
489519
History Diffing
490520
-------------------
491521
@@ -523,6 +553,8 @@ history_change_reason
523553
Freetext description of the reason for the change
524554
history_user
525555
The user that instigated the change
556+
using
557+
The database alias being used
526558
527559
To connect the signals to your callbacks, you can use the @receiver decorator:
528560

runtests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
DATABASES={
3939
'default': {
4040
'ENGINE': 'django.db.backends.sqlite3',
41+
},
42+
'other': {
43+
'ENGINE': 'django.db.backends.sqlite3',
4144
}
4245
},
4346
TEMPLATES=[{

simple_history/models.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -349,20 +349,20 @@ def get_meta_options(self, model):
349349
meta_fields["app_label"] = self.app
350350
return meta_fields
351351

352-
def post_save(self, instance, created, **kwargs):
352+
def post_save(self, instance, created, using=None, **kwargs):
353353
if not created and hasattr(instance, "skip_history_when_saving"):
354354
return
355355
if not kwargs.get("raw", False):
356-
self.create_historical_record(instance, created and "+" or "~")
356+
self.create_historical_record(instance, created and "+" or "~", using=using)
357357

358-
def post_delete(self, instance, **kwargs):
358+
def post_delete(self, instance, using=None, **kwargs):
359359
if self.cascade_delete_history:
360360
manager = getattr(instance, self.manager_name)
361-
manager.all().delete()
361+
manager.using(using).all().delete()
362362
else:
363-
self.create_historical_record(instance, "-")
363+
self.create_historical_record(instance, "-", using=using)
364364

365-
def create_historical_record(self, instance, history_type):
365+
def create_historical_record(self, instance, history_type, using=None):
366366
history_date = getattr(instance, "_history_date", now())
367367
history_user = self.get_history_user(instance)
368368
history_change_reason = getattr(instance, "changeReason", None)
@@ -387,9 +387,10 @@ def create_historical_record(self, instance, history_type):
387387
history_user=history_user,
388388
history_change_reason=history_change_reason,
389389
history_instance=history_instance,
390+
using=using,
390391
)
391392

392-
history_instance.save()
393+
history_instance.save(using=using)
393394

394395
post_create_historical_record.send(
395396
sender=manager.model,
@@ -398,6 +399,7 @@ def create_historical_record(self, instance, history_type):
398399
history_date=history_date,
399400
history_user=history_user,
400401
history_change_reason=history_change_reason,
402+
using=using,
401403
)
402404

403405
def get_history_user(self, instance):

simple_history/signals.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"history_date",
99
"history_user",
1010
"history_change_reason",
11+
"using",
1112
]
1213
)
1314
post_create_historical_record = django.dispatch.Signal(
@@ -17,5 +18,6 @@
1718
"history_date",
1819
"history_user",
1920
"history_change_reason",
21+
"using",
2022
]
2123
)

simple_history/tests/tests/test_models.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from datetime import datetime, timedelta
77
from django.apps import apps
88
from django.contrib.auth import get_user_model
9+
from django.core.exceptions import ObjectDoesNotExist
910
from django.core.files.base import ContentFile
1011
from django.db import models
1112
from django.db.models.fields.proxy import OrderWrt
@@ -15,6 +16,7 @@
1516
from simple_history.models import HistoricalRecords, ModelChange
1617
from simple_history.signals import pre_create_historical_record
1718
from simple_history.tests.tests.utils import middleware_override_settings
19+
from simple_history.utils import get_history_model_for_model
1820
from simple_history.utils import update_change_reason
1921
from ..external.models import ExternalModel2, ExternalModel4
2022
from ..models import (
@@ -1219,3 +1221,91 @@ class Meta:
12191221
"(AbstractModelWithInheritFalse) without "
12201222
"inherit=True",
12211223
)
1224+
1225+
1226+
class MultiDBWithUsingTest(TestCase):
1227+
1228+
"""Asserts historical manager respects `using()` and the `using`
1229+
keyword argument in `save()`.
1230+
"""
1231+
1232+
multi_db = True
1233+
db_name = "other"
1234+
1235+
def test_multidb_with_using_not_on_default(self):
1236+
book = Book.objects.using(self.db_name).create(isbn="1-84356-028-1")
1237+
self.assertRaises(ObjectDoesNotExist, book.history.get, isbn="1-84356-028-1")
1238+
1239+
def test_multidb_with_using_is_on_dbtwo(self):
1240+
book = Book.objects.using(self.db_name).create(isbn="1-84356-028-1")
1241+
try:
1242+
book.history.using(self.db_name).get(isbn="1-84356-028-1")
1243+
except ObjectDoesNotExist:
1244+
self.fail("ObjectDoesNotExist unexpectedly raised.")
1245+
1246+
def test_multidb_with_using_and_fk_not_on_default(self):
1247+
book = Book.objects.using(self.db_name).create(isbn="1-84356-028-1")
1248+
library = Library.objects.using(self.db_name).create(book=book)
1249+
self.assertRaises(ObjectDoesNotExist, library.history.get, book=book)
1250+
1251+
def test_multidb_with_using_and_fk_on_dbtwo(self):
1252+
book = Book.objects.using(self.db_name).create(isbn="1-84356-028-1")
1253+
library = Library.objects.using(self.db_name).create(book=book)
1254+
try:
1255+
library.history.using(self.db_name).get(book=book)
1256+
except ObjectDoesNotExist:
1257+
self.fail("ObjectDoesNotExist unexpectedly raised.")
1258+
1259+
def test_multidb_with_using_keyword_in_save_not_on_default(self):
1260+
book = Book(isbn="1-84356-028-1")
1261+
book.save(using=self.db_name)
1262+
self.assertRaises(ObjectDoesNotExist, book.history.get, isbn="1-84356-028-1")
1263+
1264+
def test_multidb_with_using_keyword_in_save_on_dbtwo(self):
1265+
book = Book(isbn="1-84356-028-1")
1266+
book.save(using=self.db_name)
1267+
try:
1268+
book.history.using(self.db_name).get(isbn="1-84356-028-1")
1269+
except ObjectDoesNotExist:
1270+
self.fail("ObjectDoesNotExist unexpectedly raised.")
1271+
1272+
def test_multidb_with_using_keyword_in_save_with_fk(self):
1273+
book = Book(isbn="1-84356-028-1")
1274+
book.save(using=self.db_name)
1275+
library = Library(book=book)
1276+
library.save(using=self.db_name)
1277+
# assert not created on default
1278+
self.assertRaises(ObjectDoesNotExist, library.history.get, book=book)
1279+
# assert created on dbtwo
1280+
try:
1281+
library.history.using(self.db_name).get(book=book)
1282+
except ObjectDoesNotExist:
1283+
self.fail("ObjectDoesNotExist unexpectedly raised.")
1284+
1285+
def test_multidb_with_using_keyword_in_save_and_update(self):
1286+
book = Book.objects.using(self.db_name).create(isbn="1-84356-028-1")
1287+
book.save(using=self.db_name)
1288+
self.assertEqual(
1289+
["+", "~"],
1290+
[
1291+
obj.history_type
1292+
for obj in book.history.using(self.db_name)
1293+
.all()
1294+
.order_by("history_date")
1295+
],
1296+
)
1297+
1298+
def test_multidb_with_using_keyword_in_save_and_delete(self):
1299+
HistoricalBook = get_history_model_for_model(Book)
1300+
book = Book.objects.using(self.db_name).create(isbn="1-84356-028-1")
1301+
book.save(using=self.db_name)
1302+
book.delete(using=self.db_name)
1303+
self.assertEqual(
1304+
["+", "~", "-"],
1305+
[
1306+
obj.history_type
1307+
for obj in HistoricalBook.objects.using(self.db_name)
1308+
.all()
1309+
.order_by("history_date")
1310+
],
1311+
)

0 commit comments

Comments
 (0)