Repository: django/django
File: django/core/serializers/python.py (Python)

Code before:
            if related:
                value = related.natural_key()
            else:
                value = None
        else:
            value = self._value_from_field(obj, field)
        self._current[field.name] = value

    def handle_m2m_field(self, obj, field):
        if field.remote_field.through._meta.auto_created:
            if self.use_natural_foreign_keys and hasattr(
                field.remote_field.model, "natural_key"
            ):

                def m2m_value(value):
                    return value.natural_key()

                def queryset_iterator(obj, field):
                    attr = getattr(obj, field.name)
                    qs = attr
                    model = field.remote_field.model
                    # If the related model has no explicit ordering, fall back
                    # to primary key ordering to ensure deterministic DB-level
                    # ordering of natural-key M2M relations.
                    if not model._meta.ordering:
                        qs = qs.order_by(model._meta.pk.name)
                    chunk_size = (
                        2000 if getattr(attr, "prefetch_cache_name", None) else None
                    )
                    return qs.iterator(chunk_size=chunk_size)

            else:

                def m2m_value(value):
                    return self._value_from_field(value, value._meta.pk)

                def queryset_iterator(obj, field):
                    query_set = getattr(obj, field.name).select_related(None).only("pk")
                    chunk_size = 2000 if query_set._prefetch_related_lookups else None
                    return query_set.iterator(chunk_size=chunk_size)

            m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
                field.name,
                queryset_iterator(obj, field),
            )
            self._current[field.name] = [m2m_value(related) for related in m2m_iter]

    def getvalue(self):
        return self.objects



Review comment (suggestion):
We should likely use `qs.ordered` here as the ordering might be applied by a related manager.

The fact a query is ordered also doesn't mean the order is _total_ e.g. it could be ordered by set of fields that are not unique together.

We do have some logic in the admin list view to determine if an ordering is total and inject `pk` otherwise if it can be handy.

It makes me wonder if a `totally_ordered` property might be handy on `QuerySet`.

Code after:
        else:
            value = self._value_from_field(obj, field)
        self._current[field.name] = value

    def handle_m2m_field(self, obj, field):
        if field.remote_field.through._meta.auto_created:
            if self.use_natural_foreign_keys and self._model_supports_natural_key(
                field.remote_field.model
            ):

                def m2m_value(value):
                    if natural := value.natural_key():
                        return natural
                    else:
                        return self._value_from_field(value, value._meta.pk)

                def queryset_iterator(obj, field):
                    attr = getattr(obj, field.name)
                    chunk_size = (
                        2000 if getattr(attr, "prefetch_cache_name", None) else None
                    )
                    query_set = attr.all()
                    if not query_set.totally_ordered:
                        current_ordering = (
                            query_set.query.order_by
                            or query_set.model._meta.ordering
                            or []
                        )
                        query_set = query_set.order_by(*current_ordering, "pk")
                    return query_set.iterator(chunk_size)

            else:

                def m2m_value(value):
                    return self._value_from_field(value, value._meta.pk)

                def queryset_iterator(obj, field):
                    query_set = getattr(obj, field.name).select_related(None).only("pk")
                    if not query_set.totally_ordered:
                        current_ordering = (
                            query_set.query.order_by
                            or query_set.model._meta.ordering
                            or []
                        )
                        query_set = query_set.order_by(*current_ordering, "pk")
                    chunk_size = 2000 if query_set._prefetch_related_lookups else None
                    return query_set.iterator(chunk_size=chunk_size)

            m2m_iter = getattr(obj, "_prefetched_objects_cache", {}).get(
                field.name,
                queryset_iterator(obj, field),