Django foreign key query returns records that are not in the database

I have a strange situation where Django seems to be giving me records that do not actually exist in the database when queried via a related_name on a foreign key. Here's a simplified example:

Let's say I have a Person model and a Pet model, where each Pet has an owner, which is a foreign key on Person:

class Pet(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name =models.CharField(max_length=50, null=False, db_index=True)
    owner = models.ForeignKey("Person", null=False, related_name="pets", on_delete=models.CASCADE)
    relationship = models.IntegerField(null=False, choices=PetRelationship.choices(), db_index=True)

Now, I have the below function that retrieves a person's pets:

def pet_tester(person):
    for pet in person.pets.filter(relationship=PetRelationship.FRIENDLY):
        pet_id = pet.id
        LOGGER.info(f"*************** pet.id = {pet_id}")
        LOGGER.info(f"******* Pet exists? = {Pet.objects.filter(id=pet_id).exists()}")
        ...

Note that this is NOT a "minimal reproducible example". I can only reproduce it in my much larger application.

For some reason (in the real app), the "exists" query is coming back as False. The output is like this:

*************** pet.id = 123e4567-e89b-12d3-a456-426614174000
******* Pet exists? = False

If I query the actual database (Postgresql) directly (outside of Django), that pet ID sure enough does NOT exist. However, the person.pets.filter query is returning it just the same.

I do not understand how this is even possible. It JUST retrieved the pet from the database (or so it would seem - it even has a UUID), but it's not really there after all when I immediately try to query it back.

It seems like the Django "filter" results are somehow inconsistent with the underlying database, but I don't know why. This is very reproducible in my app (it happens every time, consistently). It doesn't appear to be a corrupt database, as it happens consistently even on brand new machines, with the app and database freshly installed.

I know you don't know what else my app is doing. I obviously can't post all the source code, but I can assure you that the beginning of the actual function is pretty much verbatim of the pet_tester function above. I only changed the class/field names. There are no extra lines in the actual app between the "for" line and the 3 lines below it.

I'm mostly looking for ideas about how this can possibly happen, and what to look for in the app's other code.

It turns out that there is another service that is deleting the "pets" from the database. There must be some kind of caching in Django, and since the deletion happened outside of Django, Django didn't invalidate its "person.pets" cache.

Back to Top