Reputation: 58858
I have a script which imports millions of records from a legacy database into a Django database using raw SQL for efficiency. The legacy database does not have ID primary keys, so these are created from a sequence when inserting rows.
I'm testing that this import process is working properly by creating rows in the legacy database, running the import and verifying that I get the proper Django objects back out. The last part of a test typically looks a bit like this:
actual = MyModel.objects.get(code=legacy_object_code)
expected = MyModel(
id=actual.id,
code=legacy_object_code,
other_property=other_value,
…
)
I have to pull the ID in from the actual object because otherwise there's no way the two objects could be tested for equality. But the problem is that self.assertEqual(expected, actual)
always succeeds! This is because Django object equality is defined as having the same type and ID/PK.
My workaround to actually check the properties of the model instances is this:
def assert_model_instance_field_equality(self, expected, actual):
expected_dict = expected.__dict__
expected_dict.pop('_state')
actual_dict = actual.__dict__
actual_dict.pop('_state')
self.assertDictEqual(expected_dict, actual_dict)
I couldn't find this sort of assertion method, but maybe I'm missing something. Is there such a method, am I missing something, or is this really the best way to check field equality?
Upvotes: 1
Views: 1128
Reputation: 33169
I agree with @schillingt's answer in that you should use _meta.get_fields() over __dict__
. _meta.get_fields() provides the fields that are meaningful from a Django model's perspective instead of just all fields that happen to be implemented via the Python __dict__
property.
Instead of creating some pseudo-assertion, however, I'd recommend defining an "extractor" that returns the structural bits you care about in your comparison. Pseudo-assertions get a bit fiddly when it comes to readability, and I try to avoid them where I can.
Here's an example of how that might look:
def extract_structural_values(model_instance, ignore_pk='id'):
field_values = {}
for field in model_instance._meta.get_fields():
if field.name == ignore_pk:
continue
field_values[field.name] = getattr(model_instance, field.name)
return field_values
In a test I might use it thusly:
# ...in some test class/function
self.assertEqual(
extract_structural_values(actual_instance),
extract_structural_values(expected_instance))
# or even
self.assertEqual(
extract_structural_values(actual_instance),
extract_structural_values(SomeModel(
some_first_field='some value',
some_second_field='some other value',
)),
)
Upvotes: 0
Reputation: 13731
I'd recommend using the _meta.fields
property rather than __dict__
def assert_model_instance_fields_equal(self, model, expected, actual):
for field_name in model._meta.get_fields():
expected_value = getattr(expected, field_name)
actual_value = getattr(actual, field_name)
self.assertEqual(expected_value, actual_value)
Upvotes: 1