Opened 3 days ago

Closed 2 days ago

Last modified 2 days ago

#36197 closed Bug (fixed)

Model.relatedmanager.count() always 0 for custom ManyToMany relationships with through model with to_fields

Reported by: mfontana-elem Owned by: Simon Charette
Component: Database layer (models, ORM) Version: 5.1
Severity: Release blocker Keywords:
Cc: ontowhee, Simon Charette, Mariusz Felisiak Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

While upgrading my codebase to Django 5.1 I started noticing some tests were failing. At the end of the day, the reason was that, given an object's manager for a ManyToMany relation I was getting object.relatedmanager.count() != len(object.relatedmanager.all()). The LHS was always zero.

To illustrate my setup, I have the following models:

from django.db import models


class Course(models.Model):
    uuid = models.UUIDField(unique=True, editable=False, verbose_name="UUID")
    title = models.CharField(max_length=200)

class Student(models.Model):
    uuid = models.UUIDField(unique=True, editable=False, verbose_name="UUID")
    name = models.CharField(max_length=100)
    courses = models.ManyToManyField(Course, through="CourseStudent", related_name='students')

class CourseStudent(models.Model):
    course = models.ForeignKey(Course, to_field="uuid", on_delete=models.RESTRICT, null=False)
    student = models.ForeignKey(Student, to_field="uuid", on_delete=models.RESTRICT, null=False)

And the problem is that it course.students.count() generates the following SQL (both for SQLite and Postgres):
SELECT COUNT(*) AS "__count" FROM "courses_coursestudent" WHERE "courses_coursestudent"."course_id" = '00000000000000000000000000000001',
which is of course wrong.

Under the exact same setup, Django 5.0 generates:
SELECT COUNT(*) AS "__count" FROM "courses_student" INNER JOIN "courses_coursestudent" ON ("courses_student"."uuid" = "courses_coursestudent"."student_id") WHERE "courses_coursestudent"."course_id" = '0817704cdf2d478dba5d629b8997f73b'.

I attach a .zip containing a Dockerfile and a src directory to easily reproduce this behavior.

Best regards,

Attachments (1)

count-bug.zip (14.8 KB ) - added by mfontana-elem 3 days ago.

Download all attachments as: .zip

Change History (9)

by mfontana-elem, 3 days ago

Attachment: count-bug.zip added

comment:1 by mfontana-elem, 3 days ago

Summary: `Model.relatedmanager.count()` fails for custom ManyToMany relationships which don't use `id``Model.relatedmanager.count()` fails for custom ManyToMany relationships with `through` definition

comment:2 by Sarah Boyce, 3 days ago

Cc: ontowhee Simon Charette Mariusz Felisiak added
Severity: NormalRelease blocker
Summary: `Model.relatedmanager.count()` fails for custom ManyToMany relationships with `through` definitionModel.relatedmanager.count() always 0 for custom ManyToMany relationships with through model with to_fields
Triage Stage: UnreviewedAccepted

Thank you! Replicated and bisected to 66e47ac69a7e71cf32eee312d05668d8f1ba24bb (#29725)

Possible regression test/asserts

  • tests/m2m_through/tests.py

    a b class M2mThroughToFieldsTests(TestCase):  
    521521
    522522    def test_retrieval(self):
    523523        # Forward retrieval
     524        self.assertEqual(self.curry.ingredients.exists(), True)
     525        self.assertEqual(self.curry.ingredients.count(), 3)
    524526        self.assertSequenceEqual(
    525527            self.curry.ingredients.all(), [self.pea, self.potato, self.tomato]
    526528        )

comment:3 by Simon Charette, 3 days ago

Owner: set to Simon Charette
Status: newassigned

I can work on that one.

We can't be systematically using self.instance.pk here

comment:4 by Simon Charette, 3 days ago

Has patch: set

comment:5 by Sarah Boyce, 2 days ago

Triage Stage: AcceptedReady for checkin

comment:6 by Sarah Boyce <42296566+sarahboyce@…>, 2 days ago

Resolution: fixed
Status: assignedclosed

In c3a23aa:

Fixed #36197 -- Fixed improper many-to-many count() and exists() for non-pk to_field.

Regression in 66e47ac69a7e71cf32eee312d05668d8f1ba24bb.

Thanks mfontana-elem for the report and Sarah for the tests.

comment:7 by Sarah Boyce <42296566+sarahboyce@…>, 2 days ago

In 6b8a6e1:

[5.2.x] Fixed #36197 -- Fixed improper many-to-many count() and exists() for non-pk to_field.

Regression in 66e47ac69a7e71cf32eee312d05668d8f1ba24bb.

Thanks mfontana-elem for the report and Sarah for the tests.

Backport of c3a23aa02faa1cf1d32e43d66858e793cd9ecac4 from main.

comment:8 by Sarah Boyce <42296566+sarahboyce@…>, 2 days ago

In 8488074f:

[5.1.x] Fixed #36197 -- Fixed improper many-to-many count() and exists() for non-pk to_field.

Regression in 66e47ac69a7e71cf32eee312d05668d8f1ba24bb.

Thanks mfontana-elem for the report and Sarah for the tests.

Backport of c3a23aa02faa1cf1d32e43d66858e793cd9ecac4 from main.

Note: See TracTickets for help on using tickets.
Back to Top