Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtering by id for MongoengineConnectionField returns edge with null #146

Open
Gentatsu opened this issue May 27, 2020 · 11 comments
Open

Comments

@Gentatsu
Copy link

When I try to filter by id using MongoengineConnectionField, using this query:

{tasks(id:"5ecd4880f80bde8199d3d005") {
  edges {  
    node { 
      id 
      state
    }
  } 
}}```

returns this:

```{
  "data": {
    "tasks": {
      "edges": [
        {
          "node": null
        }
      ]
    }
  }
}```

@DonQueso89
Copy link
Contributor

can you show your type class and resolver?

@Gentatsu
Copy link
Author

Gentatsu commented May 27, 2020

class Task(Document):

    meta = {"collection": "tasks", "strict": False}
    dateStarted = DateTimeField(default=datetime.now)
    state = StringField()
class CustomNode(Node):
    class Meta:
        name = 'Node'

    @staticmethod
    def to_global_id(type, id):
        return id
class Task_T(MongoengineObjectType):
    class Meta:
        model = Task
        interfaces = (CustomNode,)

 tasks = MongoengineConnectionField(Task_T)

@DonQueso89
Copy link
Contributor

DonQueso89 commented May 27, 2020

you should either

  1. Define get_node_from_global_id as mentioned here https://docs.graphene-python.org/en/latest/relay/nodes/#custom-nodes
    or
  2. Implement a resolver for your field on the parent object that takes into account the id filter

Not sure if this belongs in the issues sections of this repo

@Gentatsu
Copy link
Author

A custom resolver defeats the purpose of the MongoengineConnectionField. I like being able to expose the model and filter on any item without explicitly defining them in resolver.

The get_node_from_global_id was the kicker. It's not very well defined in that example as it forces me to define the type. With a bit of cheeky debugging, I found a way to resolve generic types:

    @staticmethod
    def get_node_from_global_id(info, global_id, only_type=None):
        model = getattr(info.parent_type.graphene_type, info.field_name).model
        return model.objects.get(id=global_id)

Hope this helps someone in the future. I think it should be an issue as there are many instances where people want to resolve non-global ids and filter on them.

@DonQueso89
Copy link
Contributor

yeah having a generic get_node_from_global_id is simple as long as you are doing a plain get, once you get into embedded documents and nesting thats where it becomes harder

@Gentatsu
Copy link
Author

I've got both in mine actually! You're very correct! I tried filtering ids on an embedded document w/ a different field name.

I've modified it to this to work generically:

 @staticmethod
    def get_node_from_global_id(info, global_id, only_type=None):
        model = info.return_type.graphene_type.Edge.node.type._meta.model
        return model.objects.get(id=global_id)

Tested on embedded field + list of reference fields and seems to work.

@DonQueso89
Copy link
Contributor

how does this work on EmbeddedDocuments when they dont have an objects property?

@Gentatsu
Copy link
Author

Gentatsu commented Jun 6, 2020

You're right. This does not work on filtering EmbeddedDocuents. I needed to look at the last several records of an EmbeddedDocument, and it kept complaining about not having a pk property. I tried adding in a primary key w/ initialisating an ObjectId, setting primary_key, unique, required to True but to no avail.

I've tried it with both EmbeddedListField and ListField(EmbeddedDocument).

Not sure if I should create a separate issue for this?

@Gentatsu Gentatsu reopened this Jun 6, 2020
@DonQueso89
Copy link
Contributor

DonQueso89 commented Jun 6, 2020

This is because graphene_mongo expects the pk property to be present on your models (for some reason), if your embedded document has a natural/surrogate key, then you can add an alias called pk on your EmbeddedDocument subclass for this key as a workaround:

@property
def pk(self):
    return self.id

replacing id with the name of the attr you want to use

@AyluinReymaer
Copy link

Hello, is there any progress or suggestions in how to filter EmbeddedDocuments?

I have been trying for a while now and I've stumbled upon this open issue for the same thing.

@AyluinReymaer
Copy link

I have changed a few lines in the fields.py file like so:

# This is line 221 in the file
if callable(getattr(self.model, "objects", None)):
    iterables = self.get_queryset(self.model, info, **args)
    list_length = iterables.count()
else:
    iterables = getattr(_root, info.field_name, [])
    _args = args.copy()
    # I don't know what the "pk__in" argument does so I delete this argument in the copy made so that it doesn't cause conflicts.
    del _args["pk__in"]
    # I don't know if there is a more efficient way of doing this search
    for arg_name, arg in _args.items():
        iterables = [_object for _object in iterables if getattr(_object, arg_name, None) == arg]
    list_length = len(iterables)

Right now, it works and have not ran into any issues while doing a few tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants