#34195: Duplicate Records created when specifying None as a target in a custom
ManyToManyField with sqlite3
-------------------------------------+-------------------------------------
               Reporter:             |          Owner:  nobody
  Credentive                         |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  4.1
  layer (models, ORM)                |       Keywords:  ManyToManyField,
               Severity:  Normal     |  null
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 Reading through the documents, the only reference to null in
 ManyToManyFields is the following:

 **null has no effect since there is no way to require a relationship at
 the database level.**

 When allowing null values as a target in an M2M field, you are allowed to
 assign "None" as the target of a model. However, if you assign "None"
 multiple times, you will get multiple DB records.

 Understanding that M2M fields are implemented as join tables, I can see
 why this may be happening, but I think this behavior should be documented
 at least. Note from the example that adding non-null targets multiple
 times produces the expected result (it works, but no extra rows are
 created)

 **Model code (<ProjectRoot>/policypublisher/models.py):**


 {{{
 class Section(models.Model):
     uuid = models.UUIDField(
         primary_key=True,
         unique=True,
         editable=False,
         default=uuid.uuid4,
         help_text="A unique identifier for the Section",
     )
     <...>
     version = models.ManyToManyField(Version,
 related_name="sections_in_version")
     <...>
     under = models.ManyToManyField("self", through="SectionHierarchy",
 symmetrical=False, related_name="over")

 class SectionHierarchy(models.Model):
     under_id = models.ForeignKey(Section, on_delete=models.CASCADE,
 related_name="+")
     over_id = models.ForeignKey(Section, null=True,
 on_delete=models.CASCADE, related_name="+")
     version = models.ForeignKey(Version, on_delete=models.CASCADE)

     class Meta:
         constraints = [
             models.UniqueConstraint(fields=["under_id", "over_id",
 "version"], name="unique_sec_under_per_version")
         ]

 }}}


 **$ python manage.py shell**

 {{{
 Python 3.9.15 (main, Nov 15 2022, 09:54:34)
 [GCC 10.2.1 20210110] on linux
 Type "help", "copyright", "credits" or "license" for more information.
 (InteractiveConsole)
 >>> from policypublisher.models import *
 >>> section = Section.objects.first()
 >>> version = section.version.first()
 >>> SectionHierarchy.objects.count()
 0
 >>> section.under.add(None, through_defaults={"version": version})
 >>> section.under.add(None, through_defaults={"version": version})
 >>> SectionHierarchy.objects.count()
 2
 >>> section2 = Section.objects.last()
 >>> section.under.add(section2, through_defaults={"version":version})
 >>> SectionHierarchy.objects.count()
 3
 >>> section.under.add(section2, through_defaults={"version":version})
 >>> SectionHierarchy.objects.count()
 3
 >>>

 }}}

 **$ python manage.py dbshell**

 {{{
 SQLite version 3.34.1 2021-01-20 14:10:07
 Enter ".help" for usage hints.
 sqlite> select * from policypublisher_sectionhierarchy;
 55||055744fc79e24f89a9384d79894eee7a|be8225c85fab4a0ba8ea879a3d992abe
 56||055744fc79e24f89a9384d79894eee7a|be8225c85fab4a0ba8ea879a3d992abe

 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34195>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/01070184cd31fa29-ff4e10fb-b55c-44d1-ad64-3dd0792a4507-000000%40eu-central-1.amazonses.com.

Reply via email to