Opened 2 years ago

Closed 2 years ago

Last modified 2 years ago

#21145 closed Uncategorized (fixed)

pk_trace fails with ValueError in case when one of the three interrelated models has different primary key type than the others

Reported by: thepapermen Owned by: nobody
Component: Database layer (models, ORM) Version: 1.5
Severity: Release blocker Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In a project in which all of the models have UUIDs as the primary key, including the custom user model, this bug renders django.contrib.auth permission checking unusable (because the Group and Permission models have integer primary keys, but the user has UUID one).

Permission.objects.filter(**{user_groups_query: user_obj}) in django.contrib.auth.backends.py fails.

This bug also renders django guardian 3-rd party app unusable.

The root of the issue is that pk_trace incorrectly retrieves the primary key type from intermediary model instead of the last model in the chain.

See line 212:
https://github.com/django/django/blob/d04e8f8c7869b79a6f848a94bd51ce71223c2df2/django/db/models/fields/related.py

Run check() below to reproduce:

from django.db import models
import string
import random


class ModelA(models.Model):
    pass


class ModelB(models.Model):
    modela = models.ManyToManyField(ModelA, blank=True, null=True, )


class ModelC(models.Model):
    id = models.CharField(primary_key=True, max_length=1)  # Or any non-integer PK field
    modelb = models.ManyToManyField(ModelB, blank=True, null=True, )


def check():
    """
    This function will fail with ValueError: invalid literal for int() with base 10: 'y'
    which, in turn, points us to the fact that pk_trace for pk of ModelC tries to operate
    with field type of ModelB instead.

    See line 212:
    https://github.com/django/django/blob/d04e8f8c7869b79a6f848a94bd51ce71223c2df2/django/db/models/fields/related.py
    """
    a = ModelA.objects.create()

    b = ModelB.objects.create()
    b.modela.add(a)

    c = ModelC.objects.create(id=random.choice(string.ascii_letters))
    c.modelb.add(b)

    return ModelA.objects.filter(modelb__modelc=c)  # Will fail

    # But ModelA.objects.filter(modelb__modelc__pk=c.pk) works like a charm

Change History (3)

comment:1 Changed 2 years ago by thepapermen

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

I'd also like to point that you need a relationship that spans at least three models. Two models are not enough to trigger the bug.

comment:2 Changed 2 years ago by bmispelon

  • Resolution set to fixed
  • Status changed from new to closed

Hi,

The issue is not present on the master branch and from a quick git bisect, it looks like it was fixed in commit 69597e5bcc89aadafd1b76abf7efab30ee0b8b1a.

Consequently, I'll mark this ticket as resolved.

Thanks for the detailed report.

comment:3 Changed 2 years ago by thepapermen

Wow, that's marvelous indeed! Than you for your efforts! That's a great reason to upgrade! )))

At least, I hope that the ticket will help some unfortunate soul that would stumble upon the issue while still using Django 1.5.

Good luck!

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