Repository: wagtail/wagtail
File: wagtail/query.py (Python)

Code before:

                if qs._specific_prefetch_related_lookups:
                    items = items.prefetch_related(
                        *qs._specific_prefetch_related_lookups
                    )

                if qs._defer_streamfields and hasattr(items, "defer_streamfields"):
                    items = items.defer_streamfields()

                items_for_type = {item.pk: item for item in items}
                items_by_type[content_type] = items_for_type
                missing_pks.extend(pk for pk in pks if pk not in items_for_type)

            # Fetch generic items to supplement missing items
            if missing_pks:
                generic_items = (
                    qs.model.objects.filter(pk__in=missing_pks)
                    .select_related("content_type")
                    .in_bulk()
                )
                warnings.warn(
                    "Specific versions of the following items could not be found. "
                    "This is most likely because a database migration has removed "
                    "the relevant table or record since the item was created:\n{}".format(
                        [
                            {"id": p.id, "type": p.content_type}
                            for p in generic_items.values()
                        ]
                    ),
                    category=RuntimeWarning,
                )
            else:
                generic_items = {}

            # Yield all items in the order they occurred in the original query.
            for pk, content_type in pks_and_types:
                try:
                    item = items_by_type[content_type][pk]
                except KeyError:
                    item = generic_items[pk]
                if annotation_aliases:
                    # Reapply annotations before returning
                    for annotation, value in annotations_by_pk.get(item.pk, {}).items():
                        setattr(item, annotation, value)
                yield item

    def _get_chunks(self, queryset) -> Iterable[tuple[dict[str, Any]]]:
        if not self.chunked_fetch:
            # The entire result will be stored in memory, so there is no
            # benefit to splitting the result
            yield tuple(queryset)

Review comment (suggestion):
Perhaps we should use `getattr()` with a fall back to `str()` so we can still display some useful info about the object?

```suggestion
                            {"id": p.id, "title": getattr(p, "title", str(p)), "type": p.content_type}
```

Code after:

                if qs._specific_prefetch_related_lookups:
                    items = items.prefetch_related(
                        *qs._specific_prefetch_related_lookups
                    )

                if qs._defer_streamfields and hasattr(items, "defer_streamfields"):
                    items = items.defer_streamfields()

                items_for_type = {item.pk: item for item in items}
                items_by_type[content_type] = items_for_type
                missing_pks.extend(pk for pk in pks if pk not in items_for_type)

            # Fetch generic items to supplement missing items
            if missing_pks:
                generic_items = (
                    qs.model.objects.filter(pk__in=missing_pks)
                    .select_related("content_type")
                    .in_bulk()
                )
                warnings.warn(
                    "Specific versions of the following items could not be found. "
                    "This is most likely because a database migration has removed "
                    "the relevant table or record since the item was created:\n{}".format(
                        [
                            {
                                "id": p.id,
                                "title": getattr(p, "title", str(p)),
                                "type": p.content_type,
                            }
                            for p in generic_items.values()
                        ]
                    ),
                    category=RuntimeWarning,
                )
            else:
                generic_items = {}

            # Yield all items in the order they occurred in the original query.
            for pk, content_type in pks_and_types:
                try:
                    item = items_by_type[content_type][pk]
                except KeyError:
                    item = generic_items[pk]
                if annotation_aliases:
                    # Reapply annotations before returning
                    for annotation, value in annotations_by_pk.get(item.pk, {}).items():
                        setattr(item, annotation, value)
                yield item

    def _get_chunks(self, queryset) -> Iterable[tuple[dict[str, Any]]]: