1
1
"""Public API for dbdiff."""
2
2
3
3
from django .apps import apps
4
+ from django .core .management .color import no_style
5
+ from django .db import connections
4
6
7
+ from .exceptions import DiffFound
5
8
from .fixture_diff import FixtureDiff
6
9
7
10
__all__ = ('diff' ,)
8
11
9
12
DEBUG = apps .get_app_config ('dbdiff' ).debug
10
13
11
14
15
+ class exact (object ): # noqa
16
+ """
17
+ Context manager interface for dbdiff.
18
+
19
+ To clean up the database from any model that's provided by the fixture on
20
+ context enter, and raise a :py:class:`~dbdiff.exceptions.DiffFound` on
21
+ context enter if any diff was found::
22
+
23
+ with dbdiff.exact('your_app/tests/your_test_expected.json'):
24
+ # do stuff
25
+
26
+ .. py:attribute:: diff
27
+
28
+ :py:class:`~dbdiff.fixture_diff.FixtureDiff` instance for fixture.
29
+
30
+ .. py:attribute:: database
31
+
32
+ Database to use, 'default' by default.
33
+
34
+ .. py:attribute:: reset_models
35
+
36
+ If False, do not delete models and reset PK sequences on context enter.
37
+ """
38
+
39
+ def __init__ (self , fixture , reset_models = True , database = None ):
40
+ """Instanciate with a fixture."""
41
+ self .diff = FixtureDiff (fixture )
42
+ self .database = database if database else 'default'
43
+ self .reset_models = reset_models
44
+
45
+ def __enter__ (self ):
46
+ if not self .reset_models :
47
+ return self
48
+
49
+ tables = set ()
50
+
51
+ for model_name in self .diff .fixture_models :
52
+ model = apps .get_model (model_name )
53
+ tables .add (model ._meta .db_table )
54
+ tables .update (f .m2m_db_table () for f in
55
+ model ._meta .local_many_to_many )
56
+
57
+ connection = connections [self .database ]
58
+
59
+ statements = connection .ops .sql_flush (
60
+ no_style (),
61
+ list (tables ),
62
+ connection .introspection .sequence_list (),
63
+ True
64
+ )
65
+ if connection .settings_dict ['ENGINE' ] == 'django.db.backends.sqlite3' :
66
+ # Initially, we were just doing a model.objects.all().delete() in
67
+ # this method, and it worked only in SQLite:
68
+ # https://travis-ci.org/yourlabs/django-dbdiff/builds/100059628
69
+ # That's why this method now uses sqlflush method, but then PKs are
70
+ # not reset anymore in SQLite:
71
+ # http://stackoverflow.com/questions/24098733/why-doesnt-django-reset-sequences-in-sqlite3 # noqa
72
+ statements += [
73
+ "UPDATE SQLITE_SEQUENCE SET SEQ=0 WHERE NAME='%s';" % t
74
+ for t in tables
75
+ ]
76
+ cursor = connection .cursor ()
77
+
78
+ for statement in statements :
79
+ cursor .execute (statement )
80
+
81
+ return self
82
+
83
+ def __exit__ (self , exception_type , exception_value , traceback ):
84
+ out = self .diff .get_diff ()
85
+
86
+ if not out :
87
+ self .diff .clean ()
88
+ else :
89
+ print (self .diff .cmd )
90
+ raise DiffFound (out )
91
+
92
+
12
93
def diff (fixture ):
13
94
"""
14
95
Return the diff between the database and a fixture file as a string.
@@ -28,6 +109,8 @@ def diff(fixture):
28
109
except :
29
110
if not DEBUG :
30
111
diff .clean ()
112
+ else :
113
+ print (diff .cmd )
31
114
raise
32
115
33
116
if not DEBUG :
0 commit comments