Skip to content

Commit 59e58b8

Browse files
committed
OSB-12: added command to load fixture with unknown foreign key
1 parent 88cc99d commit 59e58b8

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from django.core.management.base import BaseCommand
2+
from django.core.exceptions import ObjectDoesNotExist
3+
import json
4+
from django.apps import apps
5+
from django.core.management import call_command
6+
import os
7+
8+
9+
class Command(BaseCommand):
10+
help = 'Load a fixture and replace foreign keys using a natural key (like uuid, name, location, etc.) with corresponding model IDs'
11+
12+
def add_arguments(self, parser):
13+
# Argument for the fixture file
14+
parser.add_argument(
15+
'fixture_file',
16+
type=str,
17+
help='Path to the fixture file (JSON format)'
18+
)
19+
# Argument for the field name that will be used as the natural key (e.g., "uuid", "name", "location", etc.)
20+
parser.add_argument(
21+
'--field',
22+
type=str,
23+
help="The unique field to use for resolving foreign keys (e.g., 'uuid', 'name', 'location', etc.)",
24+
required=True
25+
)
26+
27+
def handle(self, *args, **kwargs):
28+
fixture_file = kwargs['fixture_file']
29+
field_name = kwargs['field']
30+
31+
# Load the fixture data
32+
try:
33+
with open(fixture_file, 'r') as f:
34+
data = json.load(f)
35+
except FileNotFoundError:
36+
self.stdout.write(self.style.ERROR(f"Fixture file '{fixture_file}' not found"))
37+
return
38+
except json.JSONDecodeError:
39+
self.stdout.write(self.style.ERROR(f"Fixture file '{fixture_file}' is not valid JSON"))
40+
return
41+
42+
# Process the fixture data
43+
for obj in data:
44+
model = obj['model']
45+
if model:
46+
# Get the model class dynamically using the app label and model name
47+
app_label, model_name = model.split('.')
48+
try:
49+
model_class = apps.get_model(app_label, model_name)
50+
except LookupError:
51+
self.stdout.write(self.style.ERROR(f"Model '{model}' not found"))
52+
continue
53+
54+
# Loop through fields in the fixture and process foreign keys
55+
for field, field_value in obj['fields'].items():
56+
# If the field value is a list (e.g., ["uuid_value"]), handle it as a list
57+
if isinstance(field_value, list):
58+
if len(field_value) == 1: # If there is only one element (like with the 'role' field)
59+
related_field = model_class._meta.get_field(field)
60+
61+
if related_field.is_relation: # Check if it's a foreign key
62+
# Look up the related model (e.g., Role) using the field_name (e.g., uuid, name)
63+
related_model = related_field.related_model
64+
try:
65+
# We fetch the related object by the unique field (e.g., uuid, name, location, etc.)
66+
related_object = related_model.objects.get(**{field_name: field_value[0]})
67+
# Replace the field value with the primary key (ID) - not a list anymore
68+
obj['fields'][field] = related_object.id
69+
except ObjectDoesNotExist:
70+
self.stdout.write(self.style.ERROR(
71+
f"{related_model} with {field_name} '{field_value[0]}' not found"))
72+
continue
73+
# If it's not a list, process as usual (no change needed)
74+
elif isinstance(field_value, str): # Checking if the value is a string (uuid, name, etc.)
75+
related_field = model_class._meta.get_field(field)
76+
if related_field.is_relation:
77+
related_model = related_field.related_model
78+
try:
79+
# Fetch the related object
80+
related_object = related_model.objects.get(**{field_name: field_value})
81+
# Replace the value with the ID
82+
obj['fields'][field] = related_object.id
83+
except ObjectDoesNotExist:
84+
self.stdout.write(
85+
self.style.ERROR(f"{related_model} with {field_name} '{field_value}' not found"))
86+
continue
87+
88+
# Save the modified fixture to a new file
89+
output_file = fixture_file.replace('.json', '_modified.json')
90+
with open(output_file, 'w') as f:
91+
json.dump(data, f, indent=4)
92+
93+
self.stdout.write(self.style.SUCCESS(f'Successfully transformed the fixture and saved it as {output_file}'))
94+
95+
# Now that unique field values are replaced with IDs, load this fixture into the database
96+
try:
97+
call_command('loaddata', output_file) # This will load the modified fixture
98+
self.stdout.write(self.style.SUCCESS(f'Successfully loaded the modified fixture into the database'))
99+
except Exception as e:
100+
self.stdout.write(self.style.ERROR(f'Error loading fixture: {e}'))

0 commit comments

Comments
 (0)