GithubHelp home page GithubHelp logo

graphene-pydantic's Introduction

Graphene Logo graphene-pydantic Build status PyPI version Coverage Status

A Pydantic integration for Graphene.

Installation

pip install "graphene-pydantic"

Examples

Here is a simple Pydantic model:

import uuid
import pydantic

class PersonModel(pydantic.BaseModel):
    id: uuid.UUID
    first_name: str
    last_name: str

To create a GraphQL schema for it you simply have to write the following:

import graphene
from graphene_pydantic import PydanticObjectType

class Person(PydanticObjectType):
    class Meta:
        model = PersonModel
        # exclude specified fields
        exclude_fields = ("id",)

class Query(graphene.ObjectType):
    people = graphene.List(Person)

    @staticmethod
    def resolve_people(parent, info):
        # fetch actual PersonModels here
        return [PersonModel(id=uuid.uuid4(), first_name="Beth", last_name="Smith")]

schema = graphene.Schema(query=Query)

Then you can simply query the schema:

query = """
    query {
      people {
        firstName,
        lastName
      }
    }
"""
result = schema.execute(query)
print(result.data['people'][0])

Input Object Types

You can also create input object types from Pydantic models for mutations and queries:

from graphene_pydantic import PydanticInputObjectType

class PersonInput(PydanticInputObjectType):
    class Meta:
        model = PersonModel
        # exclude specified fields
        exclude_fields = ("id",)

class CreatePerson(graphene.Mutation):
    class Arguments:
        person = PersonInput()

    Output = Person

    @staticmethod
    def mutate(parent, info, person):
        personModel = PersonModel(id=uuid.uuid4(), first_name=person.first_name, last_name=person.last_name)
        # save PersonModel here
        return person

class Mutation(graphene.ObjectType):
    createPerson = CreatePerson.Field()

schema = graphene.Schema(mutation=Mutation)

Then execute with the input:

mutation = '''
mutation {
    createPerson(person: {
        firstName: "Jerry",
        lastName: "Smith"
    }) {
        firstName
    }
}
'''
result = schema.execute(mutation)
print(result.data['createPerson']['firstName'])

Custom resolve functions

Since PydanticObjectType inherits from graphene.ObjectType you can add custom resolve functions as explained here. For instance:

class Person(PydanticObjectType):
    class Meta:
        model = PersonModel
        # exclude specified fields
        exclude_fields = ("id",)

    full_name = graphene.String()

    def resolve_full_name(self, info, **kwargs):
        return self.first_name + ' ' + self.last_name

Forward declarations and circular references

graphene_pydantic supports forward declarations and circular references, but you will need to call the resolve_placeholders() method to ensure the types are fully updated before you execute a GraphQL query. For instance:

class NodeModel(BaseModel):
    id: int
    name: str
    labels: 'LabelsModel'

class LabelsModel(BaseModel):
    node: NodeModel
    labels: typing.List[str]

class Node(PydanticObjectType):
    class Meta:
        model = NodeModel

class Labels(PydanticObjectType):
    class Meta:
        model = LabelsModel


Node.resolve_placeholders()  # make the `labels` field work
Labels.resolve_placeholders()  # make the `node` field work

Full Examples

Please see the examples directory for more.

License

This project is under the Apache License.

Third Party Code

This project depends on third-party code which is subject to the licenses set forth in Third Party Licenses.

Contributing

Please see the Contributing Guide.

Caveats

Mappings

Note that even though Pydantic is perfectly happy with fields that hold mappings (e.g. dictionaries), because GraphQL's type system doesn't have them those fields can't be exported to Graphene types. For instance, this will fail with an error Don't know how to handle mappings in Graphene:

import typing
from graphene_pydantic import PydanticObjectType

class Pet:
    pass

class Person:
    name: str
    pets_by_name: typing.Dict[str, Pet]

class GraphQLPerson(PydanticObjectType):
    class Meta:
        model = Person

However, note that if you use exclude_fields or only_fields to exclude those values, there won't be a problem:

class GraphQLPerson(PydanticObjectType):
    class Meta:
        model = Person
        exclude_fields = ("pets_by_name",)

Union types

There are some caveats when using Unions. Let's take the following pydantic models as an example for this section:

class EmployeeModel(pydantic.BaseModel):
    name: str


class ManagerModel(EmployeeModel):
    title: str


class DepartmentModel(pydantic.BaseModel):
    employees: T.List[T.Union[ManagerModel, EmployeeModel]]
You have to implement the class method is_type_of in the graphene models

To get the Union between ManagerModel and EmployeeModel to successfully resolve in graphene, you need to implement is_type_of like this:

class Employee(PydanticObjectType):
    class Meta:
        model = EmployeeModel

    @classmethod
    def is_type_of(cls, root, info):
        return isinstance(root, (cls, EmployeeModel))


class Manager(PydanticObjectType):
    class Meta:
        model = ManagerModel

    @classmethod
    def is_type_of(cls, root, info):
        return isinstance(root, (cls, ManagerModel))


class Department(PydanticObjectType):
    class Meta:
        model = DepartmentModel

Otherwise GraphQL will throw an error similar to "[GraphQLError('Abstract type UnionOfManagerModelEmployeeModel must resolve to an Object type at runtime for field Department.employees ..."

For unions between subclasses, you need to put the subclass first in the type annotation

Looking at the employees field above, if you write the type annotation with Employee first, employees: T.List[T.Union[EmployeeModel, ManagerModel]], you will not be able to query manager-related fields (in this case title). In a query containing a spread like this:

...on Employee {
  name
}
...on Manager {
  name
  title
}

... the objects will always resolve to being an Employee. This can be avoided if you put the subclass first in the list of annotations: employees: T.List[T.Union[ManagerModel, EmployeeModel]].

Unions between subclasses don't work in Python 3.6

If a field on a model is a Union between a class and a subclass (as in our example), Python 3.6's typing will not preserve the Union and throws away the annotation for the subclass. See this issue for more details. The solution at present is to use Python 3.7.

Input Object Types don't support unions as fields

This is a GraphQL limitation. See this RFC for the progress on supporting input unions. If you see an error like '{union-type} may only contain Object types', you are most likely encountering this limitation.

graphene-pydantic's People

Contributors

abhinand-c avatar arun-sureshkumar avatar arunsureshkumar avatar boyanghuang avatar conao3 avatar dantheman39 avatar davidkell avatar doctorjohn avatar excessdenied avatar gary-liguoliang avatar jaygorrell avatar jmhammel avatar kierandarcy avatar kodyliszek avatar mak626 avatar mnieber avatar necaris avatar process0 avatar trollboy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphene-pydantic's Issues

Pydantic - Graphene throws error for discriminator input objects.

I am using graphene-pydantic to generate a GraphQL schema for my mutation. I have gone through the documentation and it's working fine for all the types but the problem is when I use discriminators in the modules. Below is the sample code with discriminators and that's throwing an error.

from graphene_pydantic import PydanticInputObjectType, PydanticObjectType
import graphene
from typing import Literal, Union
from pydantic import BaseModel, Field


class Cat(BaseModel):
    pet_type: Literal['cat']
    meows: int


class Dog(BaseModel):
    pet_type: Literal['dog']
    barks: float


class Lizard(BaseModel):
    pet_type: Literal['reptile', 'lizard']
    scales: bool


class Model(BaseModel):
    pet: Union[Cat, Dog, Lizard] = Field(..., discriminator='pet_type')
    n: int


# print(Model(pet={'pet_type': 'dog', 'barks': 3.14, 'eats': 'biscuit'}, n=1))


class Input(PydanticInputObjectType):
    class Meta:
        model = Model
        # exclude specified fields
        exclude_fields = ("id",)


class Output(PydanticObjectType):
    class Meta:
        model = Model
        # exclude specified fields
        exclude_fields = ("id",)


class CreateAnimal(graphene.Mutation):
    class Arguments:
        input = Input()

    output = Output

    @staticmethod
    def mutate(parent, info, input):
        print(input)
        # save model here
        return input


class Mutation(graphene.ObjectType):
    createPerson = CreateAnimal.Field()


schema = graphene.Schema(mutation=Mutation)
print(schema)

The error getting from graphene is like below and it is like a generalized error.

File "\AppData\Local\Programs\Python\Python310\lib\site-packages\graphql\type\definition.py", line 1338, in fields    raise TypeError(f"{self.name} fields cannot be resolved. {error}")
TypeError: Input fields cannot be resolved. The input field type must be a GraphQL input type.

Can someone help on this?

Support pydantic >1.3

Currently pydantic is pinned to <=1.3.

Is this intentional or is it just because the version pinning is old?

Support for pydantic 1.8

Currently, library is built for pydantic:

pydantic = ">=1.0,<1.7"

But newer version 1.8.2 of pydantic is currently available making it impossible to use graphene-pydantic with this version.

support for V3?

looks like v3 is not supported yet, because it's throwing errors during installation

Pydantic -> Graphene type conversion breaks when using freezegun

this schema

class TestSchema(BaseModel):
    date_field: datetime.date

breaks in tests that use freezegun to freeze time:

E   graphene_pydantic.converters.ConversionError: Don't know how to convert the Pydantic field ModelField(name='date_field', type=date, required=True) (<class 'datetime.date'>)

I believe the issue is because freezegun overwrites the type of datetime.date and datetime.datetime, so these lines in the graphene_pydantic converter (find_graphene_type()) don't evaluate to true:

elif type_ == datetime.date:
    return Date

pydantic code: https://github.com/graphql-python/graphene-pydantic/blob/master/graphene_pydantic/converters.py#L186
freezegun code: https://github.com/spulec/freezegun/blob/master/freezegun/api.py#L637
related freezegun issue: spulec/freezegun#170

I'm not sure if this is a weird fix or not, but changing the if condition to:

elif type_.__name__ == "date"

or

elif issubclass(type_, datetime.date):

fixes this use case.

A better suggestion (though I don't know the best way to implement) is to allow a custom type mappings so we don't have to rely on this switch statement.

Don't know how to convert the Pydantic field AnyUrl

I am trying to use pydantic's AnyUrl type:

class SomePydanticModel(pydantic.BaseModel):
    url: pydantic.AnyUrl = pydantic.Field()

class SomeGrapheneObject(graphene_pydantic.PydanticObjectType):
    class Meta:
        model = SomePydanticModel

But I receive the following error:

graphene_pydantic.converters.ConversionError: Don't know how to convert the Pydantic field FieldInfo(annotation=Url, required=True) (<class 'pydantic_core._pydantic_core.Url'>)

Is there a way to support this field?

graphene==3.3
graphene-pydantic==0.6.1

Great work on the package

Hi team!

I think this package could be a great addition to the GraphQL-Python organization.
Let me know if you would be interested in adding this repo to the org! โค๏ธ

Document PydanticObjectType.resolve_foo member functions

I was delighted that the following works (I copied something from django_graphene code), but AFAICT it's not a documented feature

class Resolution(PydanticObjectType):
    class Meta:
        model = ResolutionModel
        exclude_fields = ("status",)

    status = graphene.String()

    def resolve_status(self, info, **kwargs):
        return str(self.status)

I suppose you can add any additional field to a PydanticObjectType by declaring it and adding a resolve function for it? It would be nice to add this to the docs.

Required/NonNull behaviour for Pydantic fields with defaults

We think the following code produces an incorrect schema:

from pydantic import BaseModel
from graphene_pydantic import PydanticObjectType
import graphene

class ExampleModel(BaseModel):
	attr: int = 0

class ExampleType(PydanticObjectType):
	class Meta:
		model = ExampleModel

class Query(graphene.ObjectType):
    example = graphene.Field(ExampleType)

    @staticmethod
    def resolve_example(parent, info):
        # fetch actual PersonModels here
        return [ExampleType()]

schema = graphene.Schema(query=Query)

print(schema)

Result:

schema {
  query: Query
}

type ExampleType {
  attr: Int
}

type Query {
  example: ExampleType
}

AFAICT, the Int field on ExampleType should not be nullable, since there will always be a default value and this value cannot be null. Instead it should be:

type ExampleType {
  attr: Int!
}

I think it's a one line fix here, you need to change:

field_kwargs.setdefault("required", field.required)

to

field_kwargs.setdefault("required", not field.allow_none)

For reference, the Pydantic docs on distinguishing nullability (optional) and required fields.

If you agree with this, I'm happy to submit a PR - we are already doing this on our fork.

problem with examples

Hi,

I was playing around with this library and tried running the departments.py script and got an error:

$ python departments.py
[GraphQLError('Unknown type "Manager".',)]
null

I removed the Manager part of the query:

$ git diff
diff --git a/examples/departments.py b/examples/departments.py
index c77e131..2637cb9 100644
--- a/examples/departments.py
+++ b/examples/departments.py
@@ -115,14 +115,6 @@ if __name__ == "__main__":
               hiredOn
               salary { rating }
             }
-            ...on Manager {
-              name
-              salary {
-                rating
-                amount
-              }
-              teamSize
-            }
           }
         }
       }

and got:

$ python departments.py
None
{
  "listDepartments": [
    {
      "id": "48809bd6-0ff7-4942-94b6-b2c6a77e3621",
      "name": "Administration",
      "employees": [
        {
          "id": "467ae7ca-6398-41d4-b780-ab5f39ed24d3",
          "name": "Jason",
          "hiredOn": null,
          "salary": {
            "rating": "GS-11"
          }
        },
        {
          "id": "4eace3f7-190b-4b49-a9f3-faab3fba8ce7",
          "name": "Carmen",
          "hiredOn": "2019-01-01T15:26:00",
          "salary": {
            "rating": "GS-9"
          }
        },
        {
          "id": "08a791cf-0b36-4f0b-9c87-a1a1f17a5113",
          "name": "Derek",
          "hiredOn": null,
          "salary": null
        }
      ]
    }
  ]
}

Is the union functionality not working?

Support for `Literal` typed fields

I'm using some Literal['some-str'] field types in my pydantic models as a part of a discriminated union. I'd love to be able to use this with graphene-pydantic!

Unfortunately currently using Literal field types seems to produce exceptions like the following:

server_1  |   File "./main.py", line 11, in <module>
server_1  |     import graphql_api
server_1  |   File "./graphql_api.py", line 48, in <module>
server_1  |     class BlockPlateSchema(PydanticObjectType):
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene/utils/subclass_with_meta.py", line 52, in __init_subclass__
server_1  |     super_class.__init_subclass_with_meta__(**options)
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene_pydantic/objecttype.py", line 95, in __init_subclass_with_meta__
server_1  |     exclude_fields=exclude_fields,
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene_pydantic/objecttype.py", line 47, in construct_fields
server_1  |     field, registry, parent_type=obj_type, model=model
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene_pydantic/converters.py", line 130, in convert_pydantic_field
server_1  |     declared_type, field, registry, parent_type=parent_type, model=model
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene_pydantic/converters.py", line 157, in convert_pydantic_type
server_1  |     type_, field, registry, parent_type=parent_type, model=model
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene_pydantic/converters.py", line 211, in find_graphene_type
server_1  |     type_, field, registry, parent_type=parent_type, model=model
server_1  |   File "/opt/conda/lib/python3.7/site-packages/graphene_pydantic/converters.py", line 273, in convert_generic_python_type
server_1  |     ) or issubclass(origin, collections.abc.Sequence):
server_1  |   File "/opt/conda/lib/python3.7/abc.py", line 143, in __subclasscheck__
server_1  |     return _abc_subclasscheck(cls, subclass)
server_1  | TypeError: issubclass() arg 1 must be a class

From what I could tell, there is currently no support for Literal fields. Is that correct?

Several features to improve compatibility with pidantic

@necaris I have some requirements on a project that graphene-pydantic==0.3.0 doesn't support.

  1. In mutations - those fields that were not passed - still come as None. As a result, when parsing a dictionary into a Type[BaseModel], these fields are also passed through and an entry is added to the database with these values. exclude_default, exclude_unset - don't work. It is not correct.
  2. If null comes from the client to the Int field - I get an error. The field must be nullable.
  3. Pass empty strings as null in Output.

I corrected them - #75

How do we indicate that a List is made of non-null elements?

Right now, when my pydantic model looks like the following:

class MyClassModel(BaseModel):
    myList: List[str]

and my graphene model looks like this:

class MyClassType(PydanticObjectType):
    class Meta:
        model = MyClassModel
        fields = ("myList")

the GraphQL schema output for this field looks like this:

myList: [String]!

This means that introspection in TypeScript interprets each element in the List as a Maybe<String> instead of a String. What can I do here to ensure that the type is myList: [String!]!?

Thanks!

Pydantic 1.3+ support

Hi there, thanks for creating the graphene-pydantic project :-) graphene-pydantic is currently locked into Pydantic version <=1.1,>=0.26

Do you have plans to keep up with the pydantic releases, or will you only update from time to time?

Support Constrained types

Currently attempting to convert a constrained type will result in:

graphene_pydantic.converters.ConversionError: Don't know how to convert the Pydantic field ModelField(name='id', type=ConstrainedIntValue, required=True) (<class 'pydantic.types.ConstrainedIntValue'>)

Not working list[str] field

I found graphene-pydantic fails converting list[str] field.

import graphene
import pydantic

import graphene_pydantic


class Model(pydantic.BaseModel):
    field: list[str] = []


class GrapheneModel(graphene_pydantic.PydanticObjectType):
    class Meta:
        model = Model


class Query(graphene.ObjectType):
    model = graphene.Field(GrapheneModel)
    hello = graphene.String()

    @staticmethod
    def resolve_model(parent, info):
        return Model()


schema = graphene.Schema(query=Query)
print(schema)
print(schema.execute('{model {field}}'))

saved as tmp.conao3/04_list.sample.py and run like this

$ poetry run python tmp.conao3/04_list.sample.py

then, you see error.

Traceback (most recent call last):
  File "/home/conao/dev/forks/graphene-pydantic/tmp.conao3/04_list.sample.py", line 11, in <module>
    class GrapheneModel(graphene_pydantic.PydanticObjectType):
  File "/home/conao/dev/forks/graphene-pydantic/.venv/lib/python3.10/site-packages/graphene/types/objecttype.py", line 45, in __new__
    dataclass = make_dataclass(name_, fields, bases=())
  File "/usr/lib/python3.10/dataclasses.py", line 1401, in make_dataclass
    return dataclass(cls, init=init, repr=repr, eq=eq, order=order,
  File "/usr/lib/python3.10/dataclasses.py", line 1185, in dataclass
    return wrap(cls)
  File "/usr/lib/python3.10/dataclasses.py", line 1176, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
  File "/usr/lib/python3.10/dataclasses.py", line 956, in _process_class
    cls_fields.append(_get_field(cls, name, type, kw_only))
  File "/usr/lib/python3.10/dataclasses.py", line 813, in _get_field
    raise ValueError(f'mutable default {type(f.default)} for field '
ValueError: mutable default <class 'list'> for field field is not allowed: use default_factory

I use now main(2fd67a8) and I use Python3.10

Published artifact for 0.0.6 specifies Pydantic <= 1.3

I'm a little confused how this happened, but 0.0.6 on PyPI specifies Pydantic <= 1.3, even though master specifies <= 1.4 and there don't appear to be any intervening commits.

Also, FYI, there is no tag that GitHub can see for 0.0.6.

Model inheritance does not convert correctly

Use Case Scenario

I've two pydantic models. Each in his own module (file). One Model, the TUser depends on the TAddress.

address_model.py

class TAddress(BaseModel):
    id: Optional[int]
    address: Optional[constr(max_length=100, strip_whitespace=True)]
    city: Optional[constr(max_length=80, strip_whitespace=True)]
    postal_code: Optional[constr(max_length=15, strip_whitespace=True)]
    country: Optional[constr(max_length=3)]

user_model.py

class TUser(BaseModel):
    id: UUID = None
    email: Optional[EmailStr]

    address: Optional[TAddress]

    is_active: Optional[bool]
    is_email_verified: Optional[bool]
    created_at: Optional[datetime.datetime]

If I use the TUser model know for my PydanticObjectType

class UserType(PydanticObjectType):
    class Meta:
        model = TUser

I get the following error message:

graphene_pydantic.converters.ConversionError: Don't know how to convert the Pydantic field ModelField(name='address', type=Optional[TAddress], required=False, default=None) (<class 'app.address.serializers.TAddress'>)

It seems like that there is a problem when using pydantic models from different modules that depends on each other.
What is the solution for this use case?

Any idea?

TypeError: __init__() missing 1 required positional argument: 'type'

To reproduce create example models:
python 3.7
graphene==2.1.8
pydantic==1.7.1
graphene-pydantic==0.2.0

`class NodeModel(BaseModel):
    id: int
    name: str
    labels: 'LabelsModel'
    
class LabelsModel(BaseModel):
    node: NodeModel
    labels: typing.List[str]
    
class Node(PydanticObjectType):
    class Meta:
        model = NodeModel
        
class Labels(PydanticObjectType):
    class Meta:
        model = LabelsModel`

then when I try to run my app I receive:

`  File "\site-packages\graphene\utils\subclass_with_meta.py", line 52, in __init_subclass__
    super_class.__init_subclass_with_meta__(**options)
  File "b\site-packages\graphene_pydantic\objecttype.py", line 95, in __init_subclass_with_meta__
    exclude_fields=exclude_fields,
  File "\site-packages\graphene_pydantic\objecttype.py", line 47, in construct_fields
    field, registry, parent_type=obj_type, model=model
  File "\site-packages\graphene_pydantic\converters.py", line 141, in convert_pydantic_field
    return Field(resolver=get_attr_resolver(field.name), **field_kwargs)
TypeError: __init__() missing 1 required positional argument: 'type'
`

I believe the error is because constructor is expecting type but it receives type
When I have changed locally line 129 in converters is working fine.
Here is my PR: #63

Incompatible versions in the resolved dependencies

Tried to install this package to a newly created FastAPI app. Got an "Incompatible versions in the resolved dependencies" error. Pipenv logs below:

Installing graphene-pydantic...
Adding graphene-pydantic to Pipfile's [packages]...
Installation Succeeded
Pipfile.lock (c505a8) out of date, updating to (9f8497)...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
 Locking...Building requirements...
Resolving dependencies...
Locking Failed!
[ResolutionFailure]:   File "c:/users/akuani/appdata/local/programs/python/python38/lib/site-packages/pipenv/resolver.py", line 741, in _main
[ResolutionFailure]:       resolve_packages(pre, clear, verbose, system, write, requirements_dir, packages, dev)
[ResolutionFailure]:   File "c:/users/akuani/appdata/local/programs/python/python38/lib/site-packages/pipenv/resolver.py", line 702, in resolve_packages
[ResolutionFailure]:       results, resolver = resolve(
[ResolutionFailure]:   File "c:/users/akuani/appdata/local/programs/python/python38/lib/site-packages/pipenv/resolver.py", line 684, in resolve
[ResolutionFailure]:       return resolve_deps(
[ResolutionFailure]:   File "c:\users\akuani\appdata\local\programs\python\python38\lib\site-packages\pipenv\utils.py", line 1395, in resolve_deps
[ResolutionFailure]:       results, hashes, markers_lookup, resolver, skipped = actually_resolve_deps(
[ResolutionFailure]:   File "c:\users\akuani\appdata\local\programs\python\python38\lib\site-packages\pipenv\utils.py", line 1108, in actually_resolve_deps
[ResolutionFailure]:       resolver.resolve()
[ResolutionFailure]:   File "c:\users\akuani\appdata\local\programs\python\python38\lib\site-packages\pipenv\utils.py", line 833, in resolve
[ResolutionFailure]:       raise ResolutionFailure(message=str(e))
[pipenv.exceptions.ResolutionFailure]: Warning: Your dependencies could not be resolved. You likely have a mismatch in your sub-dependencies.
  First try clearing your dependency cache with $ pipenv lock --clear, then try the original command again.
 Alternatively, you can use $ pipenv install --skip-lock to bypass this mechanism, then run $ pipenv graph to inspect the situation.
  Hint: try $ pipenv lock --pre if it is a pre-release dependency.
ERROR: Could not find a version that matches graphene>=3.0b5 (from -r C:\Users\akuani\AppData\Local\Temp\pipenvnka3svhprequirements\pipenv-frhatc4t-constraints.txt (line 10))
Tried: 0.1.0, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.5.1, 0.1.5.2, 0.1.5.3, 0.1.6.0, 0.1.6.1, 0.3.0, 0.4.0, 0.4.0.1, 0.4.1, 0.4.1.1, 0.4.2, 0.4.3, 0.5.0, 0.6.0, 0.6.1, 0.7.0, 0.7.1, 0.7.2, 0.7.3, 0.8.0, 0.8.1, 0.9, 0.9.1, 0.10.0, 0.10.1, 0.10.2, 1.0, 1.0.1, 1.0.2,
1.1, 1.1.1, 1.1.2, 1.1.3, 1.2, 1.3, 1.4, 1.4.1, 1.4.2, 2.0, 2.0, 2.0.1, 2.0.1, 2.1, 2.1, 2.1.1, 2.1.1, 2.1.2, 2.1.2, 2.1.3, 2.1.3, 2.1.5, 2.1.5, 2.1.6, 2.1.6, 2.1.7, 2.1.7, 2.1.8, 2.1.8
Skipped pre-versions: 0.1.6a1, 0.9b1, 1.0.dev20160815004752, 1.0.dev20160816073455, 1.0.dev20160822080320, 1.0.dev20160823061102, 1.0.dev20160909040318, 1.0.dev20160909055438, 1.0.dev20160911044410, 1.0.dev20160911051803, 1.0.dev20160917190505, 1.0.dev2016091804123
9, 1.0.dev20160920015515, 1.0.dev20160920070441, 1.0.dev20160921153356, 1.0.dev20160922020647, 2.0.dev20170724064308, 2.0.dev20170725043308, 2.0.dev20170725061556, 2.0.dev20170727024417, 2.0.dev20170727061432, 2.0.dev20170801053013, 2.0.dev20170801053013, 2.0.dev20
170802065539, 2.0.dev20170802065539, 3.0.dev20190817210753, 3.0.dev20190817210753, 3.0a1, 3.0a1, 3.0b0, 3.0b0, 3.0b1, 3.0b1, 3.0b2, 3.0b2, 3.0b3, 3.0b3, 3.0b4, 3.0b4, 3.0b5, 3.0b5, 3.0b6, 3.0b6, 3.0b7, 3.0b7
There are incompatible versions in the resolved dependencies:
  graphene (from -r C:\Users\akuani\AppData\Local\Temp\pipenvnka3svhprequirements\pipenv-frhatc4t-constraints.txt (line 10))
  graphene>=3.0b5 (from graphene-pydantic==0.2.0->-r C:\Users\akuani\AppData\Local\Temp\pipenvnka3svhprequirements\pipenv-frhatc4t-constraints.txt (line 14))

graphql.error.located_error.GraphQLLocatedError: name 'get_hackers' is not defined

I'm sorry for the title being vague but I didn't know how to present to you the problem.

Basically I was trying to hack up an API using FastAPI, Pydantic and GraphQL. I found this repo from an Issue there.
Following the readme, I couldn't query the data using GraphiQl(I hacked starlette and currently running on graphql playground).
for this query

query {
  hackers {
    id
  }
}

I'm getting an error like this

{
  "error": {
    "data": {
      "hackers": null
    },
    "errors": [
      {
        "message": "name 'get_hackers' is not defined",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ],
        "path": [
          "hackers"
        ]
      }
    ]
  }
}

My Pydantic Basemodel and ObjectType as

class HackerModel(BaseModel):
    id: int
    name: str
    college: str
    email: str
    dob: date

class Hacker(PydanticObjectType):
    class Meta:
        model = HackerModel
        only_fields=("name","college","id","email","dob")
    class Config:
        arbitrary_types_allowed = True

And my Query Class Looks like this

class Query(graphene.ObjectType):
    hackers = graphene.List(Hacker)
    def resolve_hackers(self, info):
        return get_hackers()

I only followed your Readme Example . Any idea on How to resolve this?

Restore Graphene 2 support?

I added a comment to an already-merged PR, so creating an issue to keep track of this question.

b043031#r127675726

Is dropping support for Graphene 2 intentional? It looked like there was a decision to specifically support Graphene 2 in 2022: #77

It looks like version 0.4.0 on PyPi supports Graphene 2, but version 0.4.1 does not, and I think it may be due to this change.

Sorry if I'm missing a deliberate choice to drop Graphene 2 support. Thanks!

Support for Mutations / InputObjectTypes

Hi there!

Are there any plans to extend the graphene-pydantic magic to Graphene Mutations, in particular InputObjectTypes? It would be excellent to be able to validate Mutation arguments against Pydantic Models.

Union of Scalar Types

Hi,

I am running into an issue in which my pydantic model defines a union of scalar types, as follows:

class ImageModel(BaseModel):
    url: str
    filename: str
    modified: Union[str, int]
    key: str
    size: int

My graphene object type:

class Image(PydanticObjectType):
    class Meta:
        model = ImageModel

Attempting to generate a graphql schema from this results in the error:
AttributeError: 'GraphQLScalarType' object has no attribute 'graphene_type'
at
graphene/types/typemap.py(251)

The caveats for unions seem to be for custom object types only. Are scalars supported?

Thanks.

EDIT

This may be a graphene specific issue?

Support for Pydantic Dataclass

Dataclasses are really useful in some instances. Pydantic has a drop-in replacement for dataclasses - but does not seem to supported by this library. Not sure how difficult it would be to include it? I know the classes have a __pydantic_model__ variable.

Support for Dict types

In the following example, add the foo: Dict[str, str] member to Person model causes an error. Is there another way that I can use a Dict, which works great in pydantic?

import uuid
from typing import Dict

import pydantic
import graphene
from graphene_pydantic import PydanticObjectType


class PersonModel(pydantic.BaseModel):
    id: uuid.UUID
    first_name: str
    last_name: str
    foo: Dict[str, str]



class Person(PydanticObjectType):
    class Meta:
        model = PersonModel

class Query(graphene.ObjectType):
    people = graphene.List(Person)

    @staticmethod
    def resolve_people(parent, info):
        # fetch actual PersonModels here
        return [PersonModel(id=uuid.uuid4(), first_name="Beth", last_name="Smith")]

Produces:

Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/dylan/.vscode/extensions/ms-python.python-2021.2.582707922/pythonFiles/lib/python/debugpy/__main__.py", line 45, in <module>
    cli.main()
  File "/home/dylan/.vscode/extensions/ms-python.python-2021.2.582707922/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 444, in main
    run()
  File "/home/dylan/.vscode/extensions/ms-python.python-2021.2.582707922/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 285, in run_file
    runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
  File "/usr/lib/python3.8/runpy.py", line 265, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "/usr/lib/python3.8/runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/dylan/work/als-computing/splash-ml/examples/test_graphql.py", line 17, in <module>
    class Person(PydanticObjectType):
  File "/home/dylan/work/als-computing/splash-ml/env/lib/python3.8/site-packages/graphene/types/objecttype.py", line 30, in __new__
    base_cls = super().__new__(
  File "/home/dylan/work/als-computing/splash-ml/env/lib/python3.8/site-packages/graphene/utils/subclass_with_meta.py", line 46, in __init_subclass__
    super_class.__init_subclass_with_meta__(**options)
  File "/home/dylan/work/als-computing/splash-ml/env/lib/python3.8/site-packages/graphene_pydantic/objecttype.py", line 90, in __init_subclass_with_meta__
    construct_fields(
  File "/home/dylan/work/als-computing/splash-ml/env/lib/python3.8/site-packages/graphene_pydantic/objecttype.py", line 46, in construct_fields
    converted = convert_pydantic_field(
  File "/home/dylan/work/als-computing/splash-ml/env/lib/python3.8/site-packages/graphene_pydantic/converters.py", line 130, in convert_pydantic_field
    convert_pydantic_type(
  File "/home/dylan/work/als-computing/splash-ml/env/lib/python3.8/site-packages/graphene_pydantic/converters.py", line 166, in convert_pydantic_type
    raise ConversionError("Don't know how to handle mappings in Graphene.")
graphene_pydantic.converters.ConversionError: Don't know how to handle mappings in Graphene.  

how to pass in a json data struct?

I have the following challenge:

I am getting data from a database in json format / as dict.
This works great when I am directly grabbing the data in the query and "upgrading" it to the defined and in meta class registered pedantic data model.

Example:

class GrapheneModel(PydanticObjectType):
    class Meta:
        model = PydanticModel

...
class Queries(ObjectType):
    examples = List(
        GrapheneModel
    )

    @staticmethod
    def resolve_examples(root, info, **kwargs):
        result = fetch_from_db() # return a list of dicts
        return [PydanticModel.model_validate(q) for q in result]

But I also have other cases where I come through an interface.
I can overlay the is_type_of method to get to the right GrapheneModel, but the data that comes in is then a dict, which does not work... Do you guys have an idea how the data can be upgraded within the Graphene Model?
I cannot do it in the interface as I do not know the type there...
Thanks for any advise.

Support type hinting for InputObjectTypes

Take the following example, adapted from the README:

import uuid

import graphene
import pydantic
from graphene_pydantic import PydanticInputObjectType, PydanticObjectType


class PersonModel(pydantic.BaseModel):
    id: uuid.UUID
    first_name: str
    last_name: str


class PersonInput(PydanticInputObjectType):
    class Meta:
        model = PersonModel
        exclude_fields = ("id",)


class CreatePerson(graphene.Mutation):
    class Arguments:
        person = PersonInput()

    @staticmethod
    def mutate(parent, info, person: PersonInput):  # I'd like to type hint this argument
        personModel = PersonModel(
            id=uuid.uuid4(),
            first_name=person.first_name,
            last_name=person.last_name
        )
        return person

It would be nice to be able to type hint the model properties that are defined on the InputObjectType through introspection. Currently, the mutate method generates type warnings:

Screen Shot 2020-12-02 at 10 09 38 AM

Passing field descriptions to graphiql documentation

I'm trying to pass a description from one of the pydantic fields to the graphiQL documentation. I can't seem to get it to be recognized. It seems to be supported in graphene directly on the fields like this:

class Person(ObjectType):
    first_name = graphene.String(required=True)
    last_name = graphene.Field(String, description='Surname')

In pydantic it's supported like this:

class MainModel(BaseModel):
    """
    This is the description of the main model
    """
    snap: int = Field(
        42,
        title='The Snap',
        description='this is the value of snap',
        gt=30,
        lt=50,
    )

I've implemented the pydantic style description and don't see any change to the GraphiQL documentation on that field. Any ideas how to pass it over, or is this simply not supported with this library? I'm following the excellent tutorial here: https://testdriven.io/blog/fastapi-graphql/ but they don't spend any time on the self-documenting nature of graphql which is something really enjoy.

Thanks!

Enum name conflict for models used in both ObjectType and InputObjectType

If you try to build an ObjectType and InputObjectType for pydantic models with the same enum, it tries to create the same enum twice. (This is not an issue with re-using the same enum in multiple ObjectTypes though.)

Here's a reproducible example:

import enum
import pydantic
import graphene
from graphene_pydantic import PydanticObjectType, PydanticInputObjectType

class Color(enum.Enum):
  red = 1
  green = 2
  blue = 3

class ColorModel(pydantic.BaseModel):
  color: Color

class ColorType(PydanticObjectType):
  class Meta:
    model = ColorModel

class ColorInputType(PydanticInputObjectType
):
  class Meta:
    model = ColorModel

class CreateColor(graphene.Mutation):
  class Arguments:
    data = graphene.Argument(ColorInputType)

  ok = graphene.String()

  def mutate(self, info, data):
    return ColorModel(**data)

class Query(graphene.ObjectType):
  color = graphene.Field(ColorType)

class Mutation(graphene.ObjectType):
  createColor = CreateColor.Field()

schema = graphene.Schema(query=Query, mutation=Mutation)

And the error:

     95             _type = map[type._meta.name]
     96             if isinstance(_type, GrapheneGraphQLType):
---> 97                 assert _type.graphene_type == type, (
     98                     "Found different types with the same name in the schema: {}, {}."
     99                 ).format(_type.graphene_type, type)

AssertionError: Found different types with the same name in the schema: Color, Color.

The workaround is just to manually define the enum type, but thought it would be helpful to know.

For others who got this error:

ColorEnumType = graphene.Enum.from_enum(Color)

class ColorType(PydanticObjectType):
  class Meta:
    model = ColorModel

  color = graphene.Field(ColorEnumType)

class ColorInputType(PydanticInputObjectType
):
  class Meta:
    model = ColorModel

  color = graphene.Argument(ColorEnumType)

Nested PydanticInputObjectType

Hello,

I wanted to use nested pydantic.BaseModel with PydanticInputObjectType, but I struggle to make it work

Something like this:

from pydantic import BaseModel
from graphene_pydantic import PydanticInputObjectType
import graphene

class A(BaseModel):
    x: str

class B(BaseModel):
    a: A

class BInput(PydanticInputObjectType):
    class Meta:
        model = B

class createB(graphene.Mutation):
    class Arguments:
        b = BInput()

    output = graphene.String()

    def mutate(self, info, b):
        return "42"

class Mutation(graphene.ObjectType):
    createB = createB.Field()

schema = graphene.Schema(mutation=Mutation)

Gives me:

Traceback (most recent call last):
  File "[PATH]/lib/python3.9/site-packages/graphene/types/schema.py", line 122, in add_type
    name = graphene_type._meta.name
AttributeError: 'Placeholder' object has no attribute '_meta'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "[PATH]/lib/python3.9/site-packages/graphql/type/definition.py", line 1456, in fields
    fields = resolve_thunk(self._fields)
  File "[PATH]/lib/python3.9/site-packages/graphql/type/definition.py", line 300, in resolve_thunk
    return thunk() if callable(thunk) else thunk
  File "[PATH]/lib/python3.9/site-packages/graphene/types/schema.py", line 312, in create_fields_for_type
    field_type = create_graphql_type(field.type)
  File "[PATH]/lib/python3.9/site-packages/graphene/types/schema.py", line 120, in add_type
    return GraphQLNonNull(self.add_type(graphene_type.of_type))
  File "[PATH]/lib/python3.9/site-packages/graphene/types/schema.py", line 124, in add_type
    raise TypeError(f"Expected Graphene type, but received: {graphene_type}.")
TypeError: Expected Graphene type, but received: Placeholder(<class '__main__.A'>).

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[PATH]/lib/python3.9/site-packages/graphene/types/schema.py", line 440, in __init__
    self.graphql_schema = GraphQLSchema(
  File "[PATH]/lib/python3.9/site-packages/graphql/type/schema.py", line 226, in __init__
    collect_referenced_types(mutation)
  File "[PATH]/lib/python3.9/site-packages/graphql/type/schema.py", line 435, in collect_referenced_types
    collect_referenced_types(arg.type)
  File "[PATH]/lib/python3.9/site-packages/graphql/type/schema.py", line 438, in collect_referenced_types
    for field in named_type.fields.values():
  File "[PATH]/lib/python3.9/functools.py", line 993, in __get__
    val = self.func(instance)
  File "[PATH]/lib/python3.9/site-packages/graphql/type/definition.py", line 1459, in fields
    raise cls(f"{self.name} fields cannot be resolved. {error}") from error
TypeError: BInput fields cannot be resolved. Expected Graphene type, but received: Placeholder(<class '__main__.A'>).

I tried using resolve_placeholders, but then I get an error:

from pydantic import BaseModel
from graphene_pydantic import PydanticInputObjectType
import graphene

class A(BaseModel):
    x: str

class B(BaseModel):
    a: A

class BInput(PydanticInputObjectType):
    class Meta:
        model = B

BInput.resolve_placeholders()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "[PATH]/lib/python3.9/site-packages/graphene_pydantic/inputobjecttype.py", line 152, in resolve_placeholders
    meta.registry.register_object_field(
TypeError: register_object_field() got an unexpected keyword argument 'model'

My versions are:

graphene==3.3
pydantic==2.7.1
graphene_pdantic==0.6.1

Is there any way to use PydanticInputObjectType in nested case?

Pydantic Version Error

When installing graphene-pydantic with pip, I get this error message

ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.

We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.

graphene-pydantic 0.1.0 requires pydantic<=1.6,>=1.0, but you'll have pydantic 1.6.1 which is incompatible.

This does not affect installation in any way but it would help updating the library to use the latest pydantic version

Correct handling of python reserved keywords

Hey.

Is there a way to handle reserved keywords in schema?

Pydantic model that works fine:

class Model(BaseModel):
    class_: str

    class Config:
        fields = {
            'class_': 'class'
        }

But with this model, graphene don't see field named class. Only class_.

Using Optional or Union['Model', None] with self referencing fails

Hey.

Can't make this code work. Fails with error

graphene_pydantic.converters.ConversionError: Don't know how to convert the Pydantic field ModelField(name='nodes', type=Optional[NodeModel], required=False, default=None) (<class 'app.graphql.schemas.blablabla.NodeModel'>)

Code example

from typing import Optional, Union

from pydantic import BaseModel
from graphene_pydantic import PydanticObjectType


class NodeModel(BaseModel):
    id: int
    name: str
    # nodes: Union['NodeModel', None]
    nodes: Optional['NodeModel']


NodeModel.update_forward_refs()


class NodeModelSchema(PydanticObjectType):
    class Meta:  # noqa: too-few-public-methods
        model = NodeModel

    @classmethod
    def is_type_of(cls, root, info):
        return isinstance(root, (cls, NodeModel))


NodeModelSchema.resolve_placeholders()

Am I doing something wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.