Skip to content

Commit fd05524

Browse files
authored
Change "object_id" to be a "CharField" (#138)
* [-] CrudEvent object_id as a `CharField` * [-] Use `pk` instead of `id` in pre_save object lookup * [-] Fixes issue w/ post_delete / Adds delete test * [-] Adds updates from #141 PR * [-] Removes the need for an explicit query - Uses the internal `_state` object * [-] Adds `_state` compat test * [-] Removes sqlite3 test db
1 parent 6ee15f2 commit fd05524

9 files changed

+226
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.0.6 on 2020-05-13 00:08
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('easyaudit', '0013_auto_20190723_0126'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='crudevent',
15+
name='object_id',
16+
field=models.CharField(max_length=255),
17+
),
18+
]

easyaudit/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class CRUDEvent(models.Model):
1919
)
2020

2121
event_type = models.SmallIntegerField(choices=TYPES)
22-
object_id = models.IntegerField() # we should try to allow other ID types
22+
object_id = models.CharField(max_length=255)
2323
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, db_constraint=False)
2424
object_repr = models.TextField(null=True, blank=True)
2525
object_json_repr = models.TextField(null=True, blank=True)

easyaudit/signals/model_signals.py

+7-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.contrib.auth.models import AnonymousUser
77
from django.contrib.contenttypes.models import ContentType
88
from django.core import serializers
9+
from django.core.exceptions import ObjectDoesNotExist
910
from django.db import transaction
1011
from django.db.models import signals
1112
from django.utils import timezone
@@ -62,10 +63,8 @@ def pre_save(sender, instance, raw, using, update_fields, **kwargs):
6263
# We need a better way for this to work. ManyToMany will fail on pre_save on create
6364
return None
6465

65-
if instance.pk is None:
66-
created = True
67-
else:
68-
created = False
66+
# Determine if the instance is a create
67+
created = instance.pk is None or instance._state.adding
6968

7069
# created or updated?
7170
if not created:
@@ -300,6 +299,9 @@ def post_delete(sender, instance, using, **kwargs):
300299
user = None
301300
c_t = ContentType.objects.get_for_model(instance)
302301

302+
# object id to be used later
303+
obj_id = instance.pk
304+
303305
def crud_flow():
304306
try:
305307
with transaction.atomic(using=DATABASE_ALIAS):
@@ -309,7 +311,7 @@ def crud_flow():
309311
'object_repr': str(instance),
310312
'object_json_repr': object_json_repr,
311313
'content_type_id': c_t.id,
312-
'object_id': instance.pk,
314+
'object_id': obj_id,
313315
'user_id': getattr(user, 'id', None),
314316
'datetime': timezone.now(),
315317
'user_pk_as_string': str(user.pk) if user else user

easyaudit/tests/db.sqlite3

-220 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Generated by Django 3.0.6 on 2020-05-14 17:28
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import uuid
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('test_app', '0002_auto_20180220_1533'),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name='TestBigIntModel',
17+
fields=[
18+
('id', models.BigAutoField(primary_key=True, serialize=False)),
19+
('name', models.CharField(default='test data', max_length=50)),
20+
],
21+
),
22+
migrations.CreateModel(
23+
name='TestUUIDModel',
24+
fields=[
25+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
26+
('name', models.CharField(default='test data', max_length=50)),
27+
],
28+
),
29+
migrations.CreateModel(
30+
name='TestUUIDM2M',
31+
fields=[
32+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
33+
('name', models.CharField(max_length=50)),
34+
('test_m2m', models.ManyToManyField(to='test_app.TestUUIDModel')),
35+
],
36+
),
37+
migrations.CreateModel(
38+
name='TestUUIDForeignKey',
39+
fields=[
40+
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
41+
('name', models.CharField(max_length=50)),
42+
('test_fk', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.TestUUIDModel')),
43+
],
44+
),
45+
migrations.CreateModel(
46+
name='TestBigIntM2M',
47+
fields=[
48+
('id', models.BigAutoField(primary_key=True, serialize=False)),
49+
('name', models.CharField(max_length=50)),
50+
('test_m2m', models.ManyToManyField(to='test_app.TestBigIntModel')),
51+
],
52+
),
53+
migrations.CreateModel(
54+
name='TestBigIntForeignKey',
55+
fields=[
56+
('id', models.BigAutoField(primary_key=True, serialize=False)),
57+
('name', models.CharField(max_length=50)),
58+
('test_fk', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.TestBigIntModel')),
59+
],
60+
),
61+
]

easyaudit/tests/test_app/models.py

+42
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import uuid
2+
13
from django.db import models
24

35

@@ -13,3 +15,43 @@ class TestForeignKey(models.Model):
1315
class TestM2M(models.Model):
1416
name = models.CharField(max_length=50)
1517
test_m2m = models.ManyToManyField(TestModel)
18+
19+
20+
class TestUUIDModel(models.Model):
21+
id = models.UUIDField(
22+
primary_key=True, unique=True, editable=False, default=uuid.uuid4
23+
)
24+
name = models.CharField(max_length=50, default='test data')
25+
26+
27+
class TestUUIDForeignKey(models.Model):
28+
id = models.UUIDField(
29+
primary_key=True, unique=True, editable=False, default=uuid.uuid4
30+
)
31+
name = models.CharField(max_length=50)
32+
test_fk = models.ForeignKey(TestUUIDModel, on_delete=models.CASCADE)
33+
34+
35+
class TestUUIDM2M(models.Model):
36+
id = models.UUIDField(
37+
primary_key=True, unique=True, editable=False, default=uuid.uuid4
38+
)
39+
name = models.CharField(max_length=50)
40+
test_m2m = models.ManyToManyField(TestUUIDModel)
41+
42+
43+
class TestBigIntModel(models.Model):
44+
id = models.BigAutoField(primary_key=True)
45+
name = models.CharField(max_length=50, default='test data')
46+
47+
48+
class TestBigIntForeignKey(models.Model):
49+
id = models.BigAutoField(primary_key=True)
50+
name = models.CharField(max_length=50)
51+
test_fk = models.ForeignKey(TestBigIntModel, on_delete=models.CASCADE)
52+
53+
54+
class TestBigIntM2M(models.Model):
55+
id = models.BigAutoField(primary_key=True)
56+
name = models.CharField(max_length=50)
57+
test_m2m = models.ManyToManyField(TestBigIntModel)

easyaudit/tests/test_app/tests.py

+50-13
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
from django.contrib.auth.models import User
1212
from django.contrib.contenttypes.models import ContentType
1313
import bs4
14-
from test_app.models import TestModel, TestForeignKey, TestM2M
14+
from test_app.models import (
15+
TestModel, TestForeignKey, TestM2M,
16+
TestBigIntModel, TestBigIntForeignKey, TestBigIntM2M,
17+
TestUUIDModel, TestUUIDForeignKey, TestUUIDM2M
18+
)
1519
from easyaudit.models import CRUDEvent
1620
from easyaudit.middleware.easyaudit import set_current_user, clear_request
1721

@@ -22,38 +26,49 @@
2226
TEST_ADMIN_PASSWORD = 'password'
2327

2428

29+
@override_settings(TEST=True)
30+
class TestDjangoCompat(TestCase):
31+
32+
def test_model_state(self):
33+
"""Ensures models have the internal `_state` object."""
34+
inst = TestModel()
35+
self.assertTrue(hasattr(inst, '_state'))
36+
37+
2538
@override_settings(TEST=True)
2639
class TestAuditModels(TestCase):
40+
Model = TestModel
41+
FKModel = TestForeignKey
42+
M2MModel = TestM2M
2743

2844
def test_create_model(self):
29-
obj = TestModel.objects.create()
30-
self.assertEqual(obj.id, 1)
45+
obj = self.Model.objects.create()
3146
crud_event_qs = CRUDEvent.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj))
3247
self.assertEqual(1, crud_event_qs.count())
3348
crud_event = crud_event_qs[0]
3449
data = json.loads(crud_event.object_json_repr)[0]
3550
self.assertEqual(data['fields']['name'], obj.name)
3651

3752
def test_fk_model(self):
38-
obj = TestModel.objects.create()
39-
obj_fk = TestForeignKey(name='test', test_fk=obj)
53+
obj = self.Model.objects.create()
54+
obj_fk = self.FKModel(name='test', test_fk=obj)
4055
obj_fk.save()
4156
crud_event = CRUDEvent.objects.filter(object_id=obj_fk.id, content_type=ContentType.objects.get_for_model(obj_fk))[0]
4257
data = json.loads(crud_event.object_json_repr)[0]
43-
self.assertEqual(data['fields']['test_fk'], obj.id)
58+
self.assertEqual(str(data['fields']['test_fk']), str(obj.id))
4459

4560
def test_m2m_model(self):
46-
obj = TestModel.objects.create()
47-
obj_m2m = TestM2M(name='test')
61+
obj = self.Model.objects.create()
62+
obj_m2m = self.M2MModel(name='test')
4863
obj_m2m.save()
4964
obj_m2m.test_m2m.add(obj)
5065
crud_event = CRUDEvent.objects.filter(object_id=obj_m2m.id, content_type=ContentType.objects.get_for_model(obj_m2m))[0]
5166
data = json.loads(crud_event.object_json_repr)[0]
52-
self.assertEqual(data['fields']['test_m2m'], [obj.id])
67+
self.assertEqual([str(d) for d in data['fields']['test_m2m']], [str(obj.id)])
5368

5469
@override_settings(DJANGO_EASY_AUDIT_CRUD_EVENT_NO_CHANGED_FIELDS_SKIP=True)
5570
def test_update_skip_no_changed_fields(self):
56-
obj = TestModel.objects.create()
71+
obj = self.Model.objects.create()
5772
crud_event_qs = CRUDEvent.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj))
5873
self.assertEqual(1, crud_event_qs.count())
5974
obj.name = 'changed name'
@@ -63,7 +78,7 @@ def test_update_skip_no_changed_fields(self):
6378
self.assertIn('name', last_change.changed_fields)
6479

6580
def test_update(self):
66-
obj = TestModel.objects.create()
81+
obj = self.Model.objects.create()
6782
crud_event_qs = CRUDEvent.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj))
6883
self.assertEqual(1, crud_event_qs.count())
6984
obj.name = 'changed name'
@@ -74,17 +89,39 @@ def test_update(self):
7489

7590
@override_settings(DJANGO_EASY_AUDIT_CRUD_EVENT_NO_CHANGED_FIELDS_SKIP=True)
7691
def test_fake_update_skip_no_changed_fields(self):
77-
obj = TestModel.objects.create()
92+
obj = self.Model.objects.create()
7893
crud_event_qs = CRUDEvent.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj))
7994
obj.save()
8095
self.assertEqual(1, crud_event_qs.count())
8196

8297
def test_fake_update(self):
83-
obj = TestModel.objects.create()
98+
obj = self.Model.objects.create()
8499
crud_event_qs = CRUDEvent.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj))
85100
obj.save()
86101
self.assertEqual(2, crud_event_qs.count())
87102

103+
def test_delete(self):
104+
obj = self.Model.objects.create()
105+
crud_event_qs = CRUDEvent.objects.filter(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj))
106+
self.assertEqual(1, crud_event_qs.count())
107+
108+
obj_id = obj.pk
109+
obj.delete()
110+
crud_event_qs = CRUDEvent.objects.filter(object_id=obj_id, content_type=ContentType.objects.get_for_model(obj))
111+
self.assertEqual(2, crud_event_qs.count())
112+
113+
114+
class TestAuditUUIDModels(TestAuditModels):
115+
Model = TestUUIDModel
116+
FKModel = TestUUIDForeignKey
117+
M2MModel = TestUUIDM2M
118+
119+
120+
class TestAuditBigIntModels(TestAuditModels):
121+
Model = TestBigIntModel
122+
FKModel = TestBigIntForeignKey
123+
M2MModel = TestBigIntM2M
124+
88125

89126
@override_settings(TEST=True)
90127
class TestMiddleware(TestCase):

easyaudit/tests/test_app/urls.py

+6
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,10 @@
66
urlpatterns = [
77
url("create-obj", views.create_obj_view, name="create-obj"),
88
url("update-obj", views.update_obj_view, name="update-obj"),
9+
10+
url("create-uuid-obj", views.create_uuid_obj_view, name="create-uuid-obj"),
11+
url("update-uuid-obj", views.update_uuid_obj_view, name="update-uuid-obj"),
12+
13+
url("create-big-obj", views.create_big_obj_view, name="create-big-obj"),
14+
url("update-big-obj", views.update_big_obj_view, name="update-big-obj"),
915
]

easyaudit/tests/test_app/views.py

+41-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,48 @@
1+
from datetime import datetime
2+
13
from django.http import HttpResponse
2-
from test_app.models import TestModel
4+
from test_app.models import TestModel, TestUUIDModel, TestBigIntModel
5+
6+
7+
def create_obj(Model):
8+
return Model.objects.create()
9+
10+
11+
def update_obj(Model, pk, name):
12+
tm = Model.objects.get(pk=pk)
13+
tm.name = name
14+
tm.save()
15+
return tm
316

417

518
def create_obj_view(request):
6-
return HttpResponse(TestModel.objects.create().id)
19+
return HttpResponse(create_obj(TestModel).id)
720

821

922
def update_obj_view(request):
10-
tm = TestModel.objects.get(pk=request.GET['id'])
11-
tm.name = request.GET['id']
12-
tm.save()
13-
return HttpResponse(tm.id)
23+
name = datetime.now().isoformat()
24+
return HttpResponse(update_obj(
25+
TestModel, request.GET['id'], name
26+
).id)
27+
28+
29+
def create_uuid_obj_view(request):
30+
return HttpResponse(create_obj(TestUUIDModel).id)
31+
32+
33+
def update_uuid_obj_view(request):
34+
name = datetime.now().isoformat()
35+
return HttpResponse(update_obj(
36+
TestUUIDModel, request.GET['id'], name
37+
).id)
38+
39+
40+
def create_big_obj_view(request):
41+
return HttpResponse(create_obj(TestBigIntModel).id)
42+
43+
44+
def update_big_obj_view(request):
45+
name = datetime.now().isoformat()
46+
return HttpResponse(update_obj(
47+
TestBigIntModel, request.GET['id'], name
48+
).id)

0 commit comments

Comments
 (0)