1
- from collections import defaultdict
2
-
3
1
from django .contrib .auth .models import User
2
+ from django .db import transaction
4
3
from pydantic import BaseModel , Field , ValidationError
5
4
from rest_framework .permissions import IsAdminUser , IsAuthenticated
6
5
from rest_framework .request import Request
14
13
from .models import DataImportLog
15
14
16
15
16
+ class DryRunError (Exception ):
17
+ pass
18
+
19
+
17
20
class ImportDataRequestData (BaseModel ):
18
21
# If True, the data will be validated but not saved to the database
19
22
dry_run : bool = True
@@ -38,51 +41,74 @@ def put(self, request: Request) -> Response:
38
41
request_data = ImportDataRequestData .model_validate (request .data )
39
42
except ValidationError as ex :
40
43
errors = ex .errors (include_url = False )
41
- DataImportLog (user = request .user , is_success = False , error_type = "VALIDATION" , errors = errors ).save ()
44
+ dry_run = request .data .get ("dry_run" , True )
45
+ log_data_import_result (
46
+ request .user ,
47
+ dry_run = dry_run if isinstance (dry_run , bool ) else True ,
48
+ error_type = "VALIDATION" ,
49
+ errors = errors ,
50
+ )
42
51
return Response ({"errors" : errors }, status = 400 )
43
52
44
53
model_importer = JSONModelImporter (legacy_import = request_data .legacy_import )
45
54
result = model_importer .deserialise_all (request .data ["data" ])
46
55
47
56
if not result .success :
48
- DataImportLog (
57
+ log_data_import_result (
58
+ request .user ,
59
+ dry_run = request_data .dry_run ,
49
60
description = request_data .description ,
50
- user = request .user ,
51
- is_success = False ,
52
61
error_type = "DESERIALISATION" ,
53
62
errors = result .errors ,
54
- ). save ()
63
+ )
55
64
return Response ({"errors" : result .errors }, status = 400 )
56
65
57
- if not request_data .dry_run :
58
- try :
59
- model_importer .save_deserialisation_result_to_db (result )
60
- except Exception as ex :
61
- DataImportLog (
62
- description = request_data .description ,
63
- user = request .user ,
64
- is_success = False ,
65
- error_type = "IMPORT" ,
66
- errors = [repr (ex )],
67
- ).save ()
68
- return Response ({"errors" : [{"type" : "import_error" , "message" : repr (ex )}]}, status = 400 )
69
-
70
- save_successful_import_to_db (request_data .description , request .user , result )
71
- return Response ({})
72
-
73
-
74
- def save_successful_import_to_db (description : str , user : User | None , result : DeserialisationResult ) -> None :
75
- updated_record_count = 0
76
- updated_records = defaultdict (list )
77
- for model_import , instances in result .instances .items ():
78
- instance_pks = [ins .pk for ins in instances ]
79
- updated_record_count += len (instance_pks )
80
- updated_records [model_import .model_class .__name__ ].extend (instance_pks )
66
+ try :
67
+ import_stats = save_deserialisation_result_to_db (result , request_data .dry_run )
68
+ except Exception as ex :
69
+ errors = [{"type" : "import_error" , "message" : repr (ex )}]
70
+ log_data_import_result (
71
+ request .user ,
72
+ dry_run = request_data .dry_run ,
73
+ description = request_data .description ,
74
+ error_type = "IMPORT" ,
75
+ errors = errors ,
76
+ )
77
+ return Response ({"errors" : errors }, status = 400 )
78
+
79
+ log_data_import_result (
80
+ request .user , dry_run = request_data .dry_run , description = request_data .description , import_stats = import_stats
81
+ )
82
+ return Response (import_stats )
81
83
84
+
85
+ def save_deserialisation_result_to_db (result : DeserialisationResult , dry_run : bool ) -> dict :
86
+ try :
87
+ with transaction .atomic ():
88
+ import_stats = JSONModelImporter .save_deserialisation_result_to_db (result )
89
+
90
+ if dry_run :
91
+ raise DryRunError ("Transaction should be rolled back as this is a dry run" ) # noqa: TRY301
92
+ except DryRunError :
93
+ pass # Rollback the transaction, but keep import_stats
94
+ return import_stats
95
+
96
+
97
+ def log_data_import_result (
98
+ user : User ,
99
+ dry_run : bool ,
100
+ description : str = "" ,
101
+ import_stats : dict | None = None ,
102
+ error_type : str | None = None ,
103
+ errors : list | None = None ,
104
+ ):
82
105
DataImportLog (
83
- description = description ,
106
+ is_success = False if error_type else True ,
107
+ dry_run = dry_run ,
84
108
user = user ,
85
- is_success = True ,
86
- total_records = updated_record_count ,
87
- updated_records = updated_records ,
109
+ description = description ,
110
+ total_records = import_stats .get ("total_count" ) if import_stats else None ,
111
+ import_result = import_stats ,
112
+ error_type = error_type ,
113
+ errors = errors ,
88
114
).save ()
0 commit comments