Skip to content

Commit e5503cb

Browse files
committed
Tolerate unknown fields coming from JOIN'd data
1 parent d0b4df0 commit e5503cb

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

psqlextra/introspect/models.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ def _construct_model(
2828
apply_converters: bool = True
2929
) -> TModel:
3030
fields_by_name_and_column = {}
31-
for field in inspect_model_local_concrete_fields(model):
32-
fields_by_name_and_column[field.attname] = field
31+
for concrete_field in inspect_model_local_concrete_fields(model):
32+
fields_by_name_and_column[concrete_field.attname] = concrete_field
3333

34-
if field.db_column:
35-
fields_by_name_and_column[field.db_column] = field
34+
if concrete_field.db_column:
35+
fields_by_name_and_column[concrete_field.db_column] = concrete_field
3636

3737
indexable_columns = list(columns)
3838

@@ -41,9 +41,12 @@ def _construct_model(
4141
for index, value in enumerate(values):
4242
column = indexable_columns[index]
4343
try:
44-
field = cast(Field, model._meta.get_field(column))
44+
field: Optional[Field] = cast(Field, model._meta.get_field(column))
4545
except FieldDoesNotExist:
46-
field = fields_by_name_and_column[column]
46+
field = fields_by_name_and_column.get(column)
47+
48+
if not field:
49+
continue
4750

4851
field_column_expression = field.get_col(model._meta.db_table)
4952

tests/test_introspect.py

+45
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,48 @@ def test_models_from_cursor_generator_efficiency(
415415

416416
assert not next(instances_generator, None)
417417
assert cursor.rownumber == 2
418+
419+
420+
@pytest.mark.skipif(
421+
django.VERSION < (3, 1),
422+
reason=django_31_skip_reason,
423+
)
424+
def test_models_from_cursor_tolerates_additional_columns(
425+
mocked_model_foreign_keys, mocked_model_varying_fields
426+
):
427+
with connection.cursor() as cursor:
428+
cursor.execute(
429+
f"ALTER TABLE {mocked_model_foreign_keys._meta.db_table} ADD COLUMN new_col text DEFAULT NULL"
430+
)
431+
cursor.execute(
432+
f"ALTER TABLE {mocked_model_varying_fields._meta.db_table} ADD COLUMN new_col text DEFAULT NULL"
433+
)
434+
435+
instance = mocked_model_foreign_keys.objects.create(
436+
varying_fields=mocked_model_varying_fields.objects.create(
437+
title="test", updated_at=timezone.now()
438+
),
439+
single_field=None,
440+
)
441+
442+
with connection.cursor() as cursor:
443+
cursor.execute(
444+
f"""
445+
SELECT fk_t.*, vf_t.* FROM {mocked_model_foreign_keys._meta.db_table} fk_t
446+
INNER JOIN {mocked_model_varying_fields._meta.db_table} vf_t ON vf_t.id = fk_t.varying_fields_id
447+
"""
448+
)
449+
450+
queried_instances = list(
451+
models_from_cursor(
452+
mocked_model_foreign_keys,
453+
cursor,
454+
related_fields=["varying_fields"],
455+
)
456+
)
457+
458+
assert len(queried_instances) == 1
459+
assert queried_instances[0].id == instance.id
460+
assert (
461+
queried_instances[0].varying_fields.id == instance.varying_fields.id
462+
)

0 commit comments

Comments
 (0)