1-
2-
31import os
42import json
53import glob
64
75from django .core .management import call_command
86from django .core .management .base import BaseCommand
7+ from django .db import IntegrityError , connection
8+ from django .core import serializers
99
1010class Command (BaseCommand ):
1111 """Implementation for the `manage.py `dumpbyorg` subcommand."""
@@ -17,6 +17,9 @@ def add_arguments(self, parser):
1717 "--dir" ,
1818 type = str ,
1919 help = "Directory to write files to." )
20+ parser .add_argument ("--debug" ,
21+ action = "store_true" ,
22+ help = "Load files one by one to identify problematic data." )
2023
2124 def handle (self , * args , ** options ) -> None :
2225 self .stdout .write ('Checking if directory exists.' )
@@ -28,5 +31,151 @@ def handle(self, *args, **options) -> None:
2831 exit (0 )
2932
3033 fixture_filepaths = glob .glob (options ['dir' ] + '/**/*.json' , recursive = True )
34+
35+ self .stdout .write (f'Found { len (fixture_filepaths )} fixture files to load.' )
36+
37+ if options ['debug' ]:
38+ self ._load_files_individually (fixture_filepaths )
39+ else :
40+ try :
41+ # Disable foreign key checks during loading
42+ self ._disable_foreign_key_checks ()
43+ self .stdout .write ('Disabled foreign key constraints for loading.' )
44+
45+ call_command ('loaddata' , fixture_filepaths )
46+
47+ # Re-enable foreign key checks and validate
48+ self ._enable_foreign_key_checks ()
49+ self .stdout .write ('Re-enabled foreign key constraints.' )
50+
51+ # Check constraints after all data is loaded
52+ self ._check_constraints ()
53+ self .stdout .write ('All foreign key constraints validated successfully.' )
54+
55+ except IntegrityError as e :
56+ # Re-enable foreign key checks even if there was an error
57+ self ._enable_foreign_key_checks ()
58+
59+ self .stdout .write (self .style .ERROR (
60+ f'Data import completed but foreign key constraint validation failed.'
61+ ))
62+ self .stdout .write (self .style .ERROR (f'Error details: { e } ' ))
63+
64+ # Extract and format the violation details for a clear worklist
65+ if "Foreign key constraint violations found:" in str (e ):
66+ violations = str (e ).split ("Foreign key constraint violations found:\n " )[1 ]
67+ self .stdout .write (self .style .ERROR ('\n FOREIGN KEY CONSTRAINT VIOLATIONS - WORKLIST:' ))
68+ for violation in violations .split ('\n ' ):
69+ if violation .strip ():
70+ self .stdout .write (self .style .ERROR (f' • { violation } ' ))
71+
72+ self .stdout .write (self .style .ERROR (
73+ '\n Data import FAILED due to foreign key constraint violations.'
74+ ))
75+ self .stdout .write (self .style .WARNING (
76+ 'Fix the above violations and re-run the import.'
77+ ))
78+ raise
79+
80+ def _disable_foreign_key_checks (self ):
81+ """Disable foreign key constraint checking."""
82+ with connection .cursor () as cursor :
83+ if connection .vendor == 'sqlite' :
84+ cursor .execute ('PRAGMA foreign_keys = OFF;' )
85+ elif connection .vendor == 'mysql' :
86+ cursor .execute ('SET foreign_key_checks = 0;' )
87+ elif connection .vendor == 'postgresql' :
88+ cursor .execute ('SET session_replication_role = replica;' )
89+
90+ def _enable_foreign_key_checks (self ):
91+ """Re-enable foreign key constraint checking."""
92+ with connection .cursor () as cursor :
93+ if connection .vendor == 'sqlite' :
94+ cursor .execute ('PRAGMA foreign_keys = ON;' )
95+ elif connection .vendor == 'mysql' :
96+ cursor .execute ('SET foreign_key_checks = 1;' )
97+ elif connection .vendor == 'postgresql' :
98+ cursor .execute ('SET session_replication_role = DEFAULT;' )
99+
100+ def _check_constraints (self ):
101+ """Check all foreign key constraints after loading."""
102+ try :
103+ with connection .cursor () as cursor :
104+ if connection .vendor == 'sqlite' :
105+ cursor .execute ('PRAGMA foreign_key_check;' )
106+ violations = cursor .fetchall ()
107+ if violations :
108+ violation_details = []
109+ for violation in violations :
110+ violation_details .append (f"Table: { violation [0 ]} , Row: { violation [1 ]} , Parent: { violation [2 ]} , FK: { violation [3 ]} " )
111+ raise IntegrityError (f"Foreign key constraint violations found:\n " + "\n " .join (violation_details ))
112+ pass
113+ except Exception as e :
114+ raise IntegrityError (f"Foreign key constraint validation failed: { e } " )
115+
116+ def _load_files_individually (self , fixture_filepaths ):
117+ """Load fixture files one by one to identify which one causes the foreign key error."""
118+ loaded_count = 0
119+
120+ for filepath in sorted (fixture_filepaths ):
121+ try :
122+ self .stdout .write (f'Loading: { filepath } ' )
123+ call_command ('loaddata' , filepath )
124+ loaded_count += 1
125+ except IntegrityError as e :
126+ self .stdout .write (self .style .ERROR (
127+ f'FOREIGN KEY CONSTRAINT FAILED in file: { filepath } '
128+ ))
129+ self .stdout .write (self .style .ERROR (f'Error: { e } ' ))
130+
131+ # Try to identify the specific problematic object
132+ self ._analyze_problematic_file (filepath )
133+
134+ self .stdout .write (f'Successfully loaded { loaded_count } files before failure.' )
135+ raise
136+ except Exception as e :
137+ self .stdout .write (self .style .ERROR (
138+ f'Other error in file { filepath } : { e } '
139+ ))
140+ raise
31141
32- call_command ('loaddata' , fixture_filepaths )
142+ def _analyze_problematic_file (self , filepath ):
143+ """Analyze the problematic file to identify specific objects causing issues."""
144+ try :
145+ with open (filepath , 'r' ) as f :
146+ data = json .load (f )
147+
148+ self .stdout .write (f'Analyzing { len (data )} objects in { filepath } :' )
149+
150+ for i , obj in enumerate (data ):
151+ try :
152+ # Try to deserialize just this one object
153+ for deserialized_obj in serializers .deserialize ('json' , [obj ]):
154+ # Try to save it
155+ deserialized_obj .save ()
156+ except IntegrityError as e :
157+ self .stdout .write (self .style .ERROR (
158+ f' Object { i + 1 } : { obj .get ("model" , "unknown" )} '
159+ f'pk="{ obj .get ("pk" , "unknown" )} " FAILED'
160+ ))
161+ self .stdout .write (self .style .ERROR (f' Error: { e } ' ))
162+
163+ # Show the object's foreign key fields
164+ fields = obj .get ('fields' , {})
165+ fk_fields = {}
166+ for field_name , field_value in fields .items ():
167+ if field_value and (isinstance (field_value , str ) or isinstance (field_value , list )):
168+ # Likely a foreign key reference
169+ fk_fields [field_name ] = field_value
170+
171+ if fk_fields :
172+ self .stdout .write (f' Foreign key fields: { fk_fields } ' )
173+
174+ return # Stop at first problematic object
175+ except Exception as e :
176+ self .stdout .write (self .style .WARNING (
177+ f' Object { i + 1 } : Other error - { e } '
178+ ))
179+
180+ except Exception as e :
181+ self .stdout .write (self .style .ERROR (f'Could not analyze file: { e } ' ))
0 commit comments