Opened 7 years ago

Closed 7 years ago

Last modified 6 years ago

#24156 closed Bug (fixed)

inherited manytomany.all() empty when 2 or more inherited model from abstract one

Reported by: Timothée Mazzucotelli Owned by: Andriy Sokolovskiy
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: inherit asbtract manytomanys
Cc: me@… 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

If you set a ManyToMany field in an abstract model, and then you use this model as inheritance for 2 or more sub-models (non-abstracts), you will not be able to use submodel_instance.manytomany_field.all() anymore (actually it will work but return an empty list).

Here is an example of code producing this "bug":

from django.db import models


class Something(models.Model):
    name = models.CharField('something', max_length=255)
    
    def __unicode__(self):
        return self.name


class AbstractModel(models.Model):
    manythings = models.ManyToManyField(
        Something, verbose_name='manythings', related_name='+',
        blank=True, null=True)

    class Meta:
        abstract = True


class BugModel1(AbstractModel):
    custom1 = models.CharField('custom_field', max_length=255)


class BugModel2(AbstractModel):
    custom2 = models.CharField('custom_field', max_length=255)

When you add a Something instance to the manythings field of a BugModelN instance, it is correctly added (I was able to confirm this by trying to delete the object through admin interface: it lists the foreign key related object that will be deleted on cascade), but when you want to get it through the all() method of the manytomany field, it returns an empty list.

I am not sure if it is a bug since I was not able to get any raised error or else through debug. Maybe a voluntary behavior or a forgotten use case?

(Django 1.7.3)

Change History (8)

comment:1 Changed 7 years ago by Tim Graham

Summary: inherited manytomany.all() empty when 2 or more inherited model from asbtract oneinherited manytomany.all() empty when 2 or more inherited model from abstract one
Triage Stage: UnreviewedAccepted

Seems like a bug.

comment:2 Changed 7 years ago by Andriy Sokolovskiy

Version: 1.7master

I faced with this issue too.

My example:

class ModelA(models.Model):
    field1 = models.TextField(blank=True)


class AbstrMdl(models.Model):
    class Meta:
        abstract = True

    field1 = models.ManyToManyField('ModelA', related_name='+')


class ModelB(AbstrMdl):
    field2 = models.TextField(blank=True)


class ModelC(AbstrMdl):
    field2 = models.TextField(blank=True)


---


In [1]: from myapp.models import ModelA, ModelB

In [2]: a = ModelA.objects.create(field1='bla bla')

In [3]: b = ModelB.objects.create()

In [4]: b.field1.all()
Out[4]: []

In [5]: b.field1.add(a)

In [6]: b.field1.all()
Out[6]: []

Data is saving to database, but queryset is empty because it is filtered by something like +__in=.

Investigation showed me that problem is near django.db.models.fields.related.RelatedField#related_query_name.
For example this diff fixed my issue, but some tests are failing, so I will investigate deeper.

diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 4569037..345a83c 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -371,7 +371,10 @@ class RelatedField(Field):
         Define the name that can be used to identify this related object in a
         table-spanning query.
         """
-        return self.remote_field.related_query_name or self.remote_field.related_name or self.opts.model_name
+        if self.remote_field.is_hidden():
+            return self.remote_field.related_query_name or self.opts.model_name
+        else:
+            return self.remote_field.related_query_name or self.remote_field.related_name or self.opts.model_name

comment:3 Changed 7 years ago by Andriy Sokolovskiy

Cc: me@… added

comment:4 Changed 7 years ago by Andriy Sokolovskiy

In fact, list is not empty.
In my example it get empty list because ModelB results are empty.
That means that if I will inherit 2 or more models from abstract one,
ModelA and ModelB will have the result of ModelC queryset.

comment:5 Changed 7 years ago by Andriy Sokolovskiy

Has patch: set
Owner: changed from nobody to Andriy Sokolovskiy
Status: newassigned

comment:6 Changed 7 years ago by Tim Graham

Triage Stage: AcceptedReady for checkin

comment:7 Changed 7 years ago by Tim Graham <timograham@…>

Resolution: fixed
Status: assignedclosed

In f7b29781:

Fixed #24156 -- Fixed inherited related name of ManyToManyField.

Fixed situation when parent abstract model declares related_name='+',
and child models had an invalid queryset.

comment:8 Changed 6 years ago by Tim Graham <timograham@…>

In eb85e667:

[1.8.x] Fixed #24156 -- Fixed inherited related name of ManyToManyField.

Fixed situation when parent abstract model declares related_name='+'
and child models had an invalid queryset.

Backport of f7b297815819153b53dc1125d3f42869fb1b7ebc from master

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