-
Notifications
You must be signed in to change notification settings - Fork 828
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
[WIP] Initial work refactoring docs/ examples/ class Meta
to class arguments, implementing explicit imports.
#992
base: next/master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ Let’s build a basic GraphQL schema from scratch. | |
Requirements | ||
------------ | ||
|
||
- Python (2.7, 3.4, 3.5, 3.6, pypy) | ||
- Python (3.6+, pypy) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3.5 should be supported too (until 3.8 is released in October). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we might want to clarify the versions supported and the plan in ROADMAP.md There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went with 3.6+ after looking at what we're testing for in CI. This is absolutely open to discussion! |
||
- Graphene (2.0) | ||
|
||
Project setup | ||
|
@@ -35,15 +35,17 @@ one field: ``hello`` and an input name. And when we query it, it should return ` | |
|
||
.. code:: python | ||
|
||
import graphene | ||
from graphene import ObjectType, Schema, String | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what I mean about adding imports in docs - we should be doing it consistently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. See above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a sphinx or other tool for actually embedding live code from, say, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might be what you're looking for @changeling https://quick-sphinx-tutorial.readthedocs.io/en/latest/code.html This option |
||
|
||
class Query(graphene.ObjectType): | ||
hello = graphene.String(argument=graphene.String(default_value="stranger")) | ||
|
||
class Query(ObjectType): | ||
hello = String(argument=String(default_value="stranger")) | ||
|
||
def resolve_hello(self, info, argument): | ||
return 'Hello ' + argument | ||
return f"Hello {argument}" | ||
|
||
|
||
schema = graphene.Schema(query=Query) | ||
schema = Schema(query=Query) | ||
|
||
Querying | ||
-------- | ||
|
@@ -52,11 +54,11 @@ Then we can start querying our schema: | |
|
||
.. code:: python | ||
|
||
result = schema.execute('{ hello }') | ||
print(result.data['hello']) # "Hello stranger" | ||
result = schema.execute("{ hello }") | ||
print(result.data["hello"]) # "Hello stranger" | ||
|
||
# or passing the argument in the query | ||
result = schema.execute('{ hello (argument: "graph") }') | ||
print(result.data['hello']) # "Hello graph" | ||
print(result.data["hello"]) # "Hello graph" | ||
|
||
Congrats! You got your first graphene schema working! |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,12 @@ subclass of ``relay.ClientIDMutation``. | |
.. code:: python | ||
|
||
class IntroduceShip(relay.ClientIDMutation): | ||
|
||
class Input: | ||
ship_name = graphene.String(required=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I like this better without Did we have a discussion with folks to confirm that everyone is generally on the same page? The current convention is followed in all kinds of places so it will take some doing to change all the code examples. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm all for this. It makes the code much easier to read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dvndrsn We had a brief discussion on Slack about this. One reason for this PR as WIP is to further the conversation. See: https://graphenetools.slack.com/archives/CGR4VCHUL/p1559524058000700 @jkimbo I feel the same way. |
||
faction_id = graphene.String(required=True) | ||
ship_name = String(required=True) | ||
faction_id = String(required=True) | ||
|
||
ship = graphene.Field(Ship) | ||
faction = graphene.Field(Faction) | ||
ship = Field(Ship) | ||
faction = Field(Faction) | ||
|
||
@classmethod | ||
def mutate_and_get_payload(cls, root, info, **input): | ||
|
@@ -28,22 +27,20 @@ subclass of ``relay.ClientIDMutation``. | |
faction = get_faction(faction_id) | ||
return IntroduceShip(ship=ship, faction=faction) | ||
|
||
|
||
|
||
Accepting Files | ||
--------------- | ||
|
||
Mutations can also accept files, that's how it will work with different integrations: | ||
|
||
.. code:: python | ||
|
||
class UploadFile(graphene.ClientIDMutation): | ||
class Input: | ||
pass | ||
# nothing needed for uploading file | ||
class UploadFile(ClientIDMutation): | ||
class Input: | ||
pass | ||
# nothing needed for uploading file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is unrelated to the changes that you did: It feels weird to have example code with commented out bits. I'd prefer to have a viable example which uses comments to highlight the important things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a good conversation to have. We need to start a doc/channel to discuss these things. One thought: Any code we use in the docs that mirrors actual code in the examples, IMHO, ought to be taken as is from the example code. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made a note in my local TODO of this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really like this idea to extract most examples from a working API. I had a few ideas that I brainstormed in this issue: |
||
|
||
# your return fields | ||
success = graphene.String() | ||
# your return fields | ||
success = String() | ||
|
||
@classmethod | ||
def mutate_and_get_payload(cls, root, info, **input): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,12 +14,10 @@ Example usage (taken from the `Starwars Relay example`_): | |
|
||
.. code:: python | ||
|
||
class Ship(graphene.ObjectType): | ||
'''A ship in the Star Wars saga''' | ||
class Meta: | ||
interfaces = (relay.Node, ) | ||
class Ship(ObjectType, interfaces=(relay.Node,)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you prefer this way of supplying meta args? I've not used it much myself since I'm used to Meta inner class with Django. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've not used this way before either but it's quite nice. I guess since we're dropping Python 2 support we should just go all out on Python 3 features. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dvndrsn I like this for It's offered in Also, https://stackoverflow.com/search?q=%5Bgraphene-python%5D+class+Meta There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's true. Especially with Graphene-Django and Graphene-SQLAlchemy. Both of those projects have a lot of use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like docs should at least present both approaches. As I tend to use |
||
"""A ship in the Star Wars saga""" | ||
|
||
name = graphene.String(description='The name of the ship.') | ||
name = String(description="The name of the ship.") | ||
|
||
@classmethod | ||
def get_node(cls, info, id): | ||
|
@@ -45,26 +43,22 @@ Example of a custom node: | |
|
||
.. code:: python | ||
|
||
class CustomNode(Node): | ||
|
||
class Meta: | ||
name = 'Node' | ||
|
||
class CustomNode(Node, name="Node"): | ||
@staticmethod | ||
def to_global_id(type, id): | ||
return '{}:{}'.format(type, id) | ||
return "{}:{}".format(type, id) | ||
|
||
@staticmethod | ||
def get_node_from_global_id(info, global_id, only_type=None): | ||
type, id = global_id.split(':') | ||
type, id = global_id.split(":") | ||
if only_type: | ||
# We assure that the node type that we want to retrieve | ||
# is the same that was indicated in the field type | ||
assert type == only_type._meta.name, 'Received not compatible node.' | ||
assert type == only_type._meta.name, "Received not compatible node." | ||
|
||
if type == 'User': | ||
if type == "User": | ||
return get_user(id) | ||
elif type == 'Photo': | ||
elif type == "Photo": | ||
return get_photo(id) | ||
|
||
|
||
|
@@ -94,7 +88,7 @@ Example usage: | |
|
||
.. code:: python | ||
|
||
class Query(graphene.ObjectType): | ||
class Query(ObjectType): | ||
# Should be CustomNode.Field() if we want to use our custom Node | ||
node = relay.Node.Field() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whenever I read docs examples like this I always ask "where the hell do I import that from though?".
I feel like if we remove the
graphene
bit we need to show people where it is imported from. Because copy/pasting this won't work.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something I've been meaning to bring up. I think we should, wherever possible, make snippets in the docs complete. That is, again as much as possible, allow them to be copy/pasted into a
.py
file and actually work. I echo your thought here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on including imports. I always get confused with that too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also importing a package would be a better approach rather than import each class separately. Makes reading code much easier if each external reference is easily identifiable.