Graphene-django: M2M connection is broken

Created on 9 May 2017  路  8Comments  路  Source: graphql-python/graphene-django

Hi,

I have following models:

class Place(models.Model):
    categories = models.ManyToManyField(PlaceCategory, related_name='places')
class PlaceCategory(models.Model):
    name = models.CharField(max_length=255, unique=True)



md5-31ca6232148ba1c5e2c3900b52753dda



class PlaceNode(DjangoObjectType):
    class Meta:
        model = Place
        interfaces = (graphene.relay.Node, )



md5-8221ac3fcb3f6d4fc8ecaa8cf579d623



class PlaceCategoryNode(DjangoObjectType):
    class Meta:
        model = PlaceCategory
        interfaces = (graphene.relay.Node, )



md5-cf3404bb4bb134b2a688b0ef8520cbad



{
  placeCategories {
    edges {
      node {
        id
        places {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
}



md5-b47e61a8cae941ea66f9cb2048593fca



{
  places {
    edges {
      node {
        id
        categories {
          edges {
            node {
              id
            }
          }
        }
      }
    }
  }
}

I get errors:

Cannot combine queries on two different base models.

It seems that M2M connection is working improperly and it is unable to fetch related objects from instance where M2M is defined.

Has anyone run into the same problem ?

Most helpful comment

You can just define custom resolver and return queryset. It should work.

In my case it would be:

class PlaceNode(DjangoObjectType):
    class Meta:
        model = Place
        interfaces = (graphene.relay.Node, )

    categories = DjangoFilterConnectionField(PlaceCategoryNode)

    def resolve_categories(self, *args):
        return self.categories.all()

But this is only a workaround. In my case when I have lots of M2M it is annoying that I have to define custom resolvers for all of them.

All 8 comments

Do you have any ideas for a work around? I have just run into this issue.

You can just define custom resolver and return queryset. It should work.

In my case it would be:

class PlaceNode(DjangoObjectType):
    class Meta:
        model = Place
        interfaces = (graphene.relay.Node, )

    categories = DjangoFilterConnectionField(PlaceCategoryNode)

    def resolve_categories(self, *args):
        return self.categories.all()

But this is only a workaround. In my case when I have lots of M2M it is annoying that I have to define custom resolvers for all of them.

It looks that exception is raised by:

DjangoConnectionField.merge_querysets

Method gets two queryset, default_queryset and queryset. Although queryset contains proper instances from m2m connection, default_queryset contains all objects from base model. In my case queryset has PlaceCategory instances while default_queryset has Place instances. That's why it raises error.

Looks to me that DjangoFilterConnectionField.connection_resolver receives wrong default_manager parameter.

Can anyone confirm this ?

I believe I have solved this bug in #181. Feel free to try that out and let me know if it works for you.

@spockNinja is this already in 1.3 release, how can I try it?

@keremgocen No, this was not merged in time for the 1.3 release. There has not been a new release to pypi since this was merged. From the commit log, it looks like a release is pushed about every month or so, so I don't expect it will be too long.

In the meantime, you can pull down master instead of 1.3.

pip uninstall graphene-django
pip install git+https://github.com/graphql-python/graphene-django.git

or in requirements.txt
pip install git+ssh://[email protected]/graphql-python/graphene-django.git#egg=graphene-django

Updated, see bottom for working installation with M2M relation

Passing by. #181 is still not available at the time of this posting.

The requirements.txt version spockNinja provided didn't work for me. The following did instead (deprecated. see updated answer below):

// requirements.txt
...
graphene==1.4.1
git+git://github.com/graphql-python/graphene-django.git#egg=graphene-django
graphql-core==1.1
...

Also remember to do pip uninstall graphene-django and pip install -r requirements.txt before and after making the change respectively. M2M FTW!


Update: A recent merge of the PR #229 on 28 Aug 2017 breaks the ability to pip install from the master branch with the following error:

Collecting graphene-django from git+git://github.com/graphql-python/graphene-django.git@master#egg=graphene-django (from -r requirements.txt (line 43))
  Cloning git://github.com/graphql-python/graphene-django.git (to master) to /private/var/folders/89/rncftgg148s_bf25n4kr806c0000gn/T/pip-build-gtuljhuf/graphene-django
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/setuptools/dist.py", line 490, in fetch_build_egg
        cmd = self._egg_fetcher
    AttributeError: 'Distribution' object has no attribute '_egg_fetcher'

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/private/var/folders/89/rncftgg148s_bf25n4kr806c0000gn/T/pip-build-gtuljhuf/graphene-django/setup.py", line 77, in <module>
        platforms='any',
      File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/distutils/core.py", line 108, in setup
        _setup_distribution = dist = klass(attrs)
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/setuptools/dist.py", line 325, in __init__
        self.fetch_build_eggs(attrs['setup_requires'])
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/setuptools/dist.py", line 446, in fetch_build_eggs
        replace_conflicting=True,
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/pkg_resources/__init__.py", line 855, in resolve
        dist = best[req.key] = env.best_match(req, ws, installer)
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1127, in best_match
        return self.obtain(req, installer)
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/pkg_resources/__init__.py", line 1139, in obtain
        return installer(requirement)
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/setuptools/dist.py", line 495, in fetch_build_egg
        dist.parse_config_files()
      File "/Users/x/Sites/x.com-v2/src/server/env/lib/python3.6/site-packages/setuptools/dist.py", line 425, in parse_config_files
        _Distribution.parse_config_files(self, filenames=filenames)
      File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/distutils/dist.py", line 395, in parse_config_files
        parser.read(filename)
      File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/configparser.py", line 697, in read
        self._read(fp, filename)
      File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/configparser.py", line 1066, in _read
        lineno)
    configparser.DuplicateSectionError: While reading from 'setup.cfg' [line 17]: section 'bdist_wheel' already exists

To resolve this for now, pip install the commit before it:

// requirements.txt
...
graphene==1.4.1
git+git://github.com/graphql-python/graphene-django.git@bf2b5a940e8a2c4324fa5550cfd2d61570650e22#egg=graphene-django
graphql-core==1.1
...

Why?

Feature to query m2m relation has been built and merged into master via #181, but has not been published to the pypi package. We could use the feature by pulling from master as @spockNinja suggested in the comment above as a workaround until the package is ready. However, recent changes in the graphene-django repository breaks the ability to query m2m relations by pulling from master, so we're reverting to a specific commit right before the breaking change in order to query m2m relations, until the official pypi package with the m2m feature is released.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

x9sheikh picture x9sheikh  路  4Comments

amiyatulu picture amiyatulu  路  3Comments

mraak picture mraak  路  3Comments

StefanoSega picture StefanoSega  路  4Comments

hyusetiawan picture hyusetiawan  路  4Comments