Graphql Django best way to get nested object property - python

Instead of doing the query like this
query {
allObjectC {
id # self ID()
nestedB_set {
id
nestedA_set {
id # ID() we want to get
}
}
}
}
New Graphql Query
query {
allObjectC {
id # self ID()
nestedA_id # nested nested ID() we want to get
}
}
Any best pratice or ideas for this ?
Thanks in advance .

We change the model like this // Models.py
class ObjectA(models.Model):
...
class ObjectB(models.Model):
propertyB = models.ForeignKey(ObjectA, on_delete=models.CASCADE, related_name='nestedB')
class ObjectC(models.Model):
propertyC = models.ForeignKey(ObjectB, on_delete=models.CASCADE, related_name='nestedC')
#property # new property created
def nestedB_nestedA_id(self):
return self.propertyC.nested_objectB.nested_objectA.id
We change the graphql schema like this // schema.py
class ObjectCNode(DjangoObjectType):
nestedB_nestedA_id = graphene.Int(source='nestedB_nestedA_id') # as proxy
nestedA_id = graphene.ID() # globalID of ObjectANode we want to get
def resolve_nestedB_nestedA_id(self, info, **kwargs):
return self.nestedB_nestedA_id
def resolve_nestedA_id(self, info, **kwargs):
return relay.Node.to_global_id(ObjectANode._meta.name, self.nestedB_nestedA_id)
And now query
query {
allObjectC {
id # self ID()
nestedA_id # nested nested ID() we want to get
}
}
Others suggestions ?

Related

django-filter IN lookup filter and list of strings

Using Graphene in Django to create Gql schema, now trying to filter Foreign Keys with list of strings. It kinda works, but not exactly.
schema.py
class CharInFilter(BaseInFilter, CharFilter):
pass
class ProductFilter(FilterSet):
softwares__name = CharInFilter(field_name="softwares__name", lookup_expr="in")
class Meta:
model = Product
fields = {"name": ["exact", "icontains"]}
class ProductType(DjangoObjectType):
class Meta:
model = Product
filterset_class = ProductFilter
interfaces = (graphene.relay.Node,)
query
query authorPageProducts {
user(slug: "john") {
productSet(softwares_Name: "Blender") {
edges {
node {
name
softwares {
name
}
}
}
}
}
}
Here is what works and what not:
softwares_Name: "Blender" -> correct
softwares_Name: "Houdini" -> correct
softwares_Name: "Blender,Houdini" -> empty result, not correct
I am passing string separated with comma. Can/should I pass list of strings in Gql query? Im not sure if its possible/necessary.
I do have Products that have both Foreign Keys with values "Houdini" and "Blender", so query with "Blender,Houdini" shouldn't be empty.
I tried this query in shell, and its correct. Here I used list of strings.
u = User.objects.get(id=2)
p = u.product_set.filter(softwares__name__in=["Blender", "Houdini"])
Here is some info from Django Debug Toolbar, to see SQL expression for third case.
SELECT COUNT(*) AS "__count"
FROM "shop_product"
INNER JOIN "shop_product_softwares"
ON ("shop_product"."id" = "shop_product_softwares"."product_id")
INNER JOIN "shop_software"
ON ("shop_product_softwares"."software_id" = "shop_software"."id")
WHERE ("shop_product"."author_id" = 2 AND "shop_software"."name" IN ('Blender,Houdini'))
I cannot figure out where is the issue, while querying with comma separated string, as docs suggest.
Thank you

How to declare variables in a GraphqQL query using the gql client?

I'm new with GraphQL schemas and I would like to do a mutation using the gql client. The query below works like a charme in the graphql web interface after replacing the 5 variables with the corresponding strings and integers.
But when I put a $ before every variables in the query, as mentionned in the documentation, it throws an error saying Variable '$w' is not defined by operation 'createMutation'.
What am'I missing ?
transport = AIOHTTPTransport(url="http://x.x.x.x:8000/graphql")
client = Client(transport=transport, fetch_schema_from_transport=True)
query = gql(
"""
mutation createMutation {
createTarget(targetData: {weight: $w, dt: $dt,
exchangeId: $exchangeId,
strategyId: $strategyId,
marketId:$marketId
}) {
target {
dt,
weight,
market,
exchange,
strategy
}
}
}
"""
)
params = {"w": self.weight,
"dt": self.dt,
"exchangeId": self.exchange.pk,
"strategyId": self.strategy.pk,
"marketId": self.market.pk
}
result = client.execute(query, variable_values=params)
When I remove the $ it says Float cannot represent non numeric value: w.
And this is how the graphene code looks like at the server side :
class TargetInput(graphene.InputObjectType):
weight = graphene.Float()
dt = graphene.DateTime()
strategy_id = graphene.Int()
exchange_id = graphene.Int()
market_id = graphene.Int()
class CreateTarget(graphene.Mutation):
class Arguments:
target_data = TargetInput(required=True)
target = graphene.Field(CustomObject)
#staticmethod
def mutate(root, info, target_data):
target = Target.objects.create(**target_data)
return CreateTarget(target=target)
class Mutation(graphene.ObjectType):
create_target = CreateTarget.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
There is also another question related to gql variables but it doesn't solve my problem.
I have found the answer to my own question. When using variables it's necessary to declare each of them between ( and ) at the beggining of the query, as stipulated here.
So in my case the correct query was:
query = gql(
"""
mutation createMutation ($w: Float, $dt: DateTime, $exchangeId: Int, $strategyId: Int, $marketId: Int){
createTarget(targetData: {weight: $w, dt: $dt,
exchangeId: $exchangeId,
strategyId: $strategyId,
marketId: $marketId
}) {
target {
dt
}
}
}
"""
)

Can't access nested dictionary data **kwargs python and best practices for nested data mutation in GraphQL

In Graphene-Django and GraphQL I am trying to create a resolve_or_create method for nested data creation inside my mutations.
I'm trying to pass a dictionary with the input from the user as a **kwarg to my resolve_or_create function, and though I can see "location" in the variable watcher (in VSCode), I continuously am getting the error 'dict' object has no attribute 'location'
Here's my resolve_or_create method:
def resolve_or_create(*args, **kwargs):
input = {}
result = {}
input.location = kwargs.get('location', None)
if input.location is not None:
input.location = Location.objects.filter(pk=input.location.id).first()
if input.location is None:
location = Location.objects.create(
location_city = input.location.location_city,
location_state = input.location.location_state,
location_sales_tax_rate = input.location.location_sales_tax_rate
)
if location is None:
return None
result.location = location
return result
and my CreateCustomer definition, where this method is being called
class CreateCustomer(graphene.Mutation):
class Arguments:
input = CustomerInput(required=True)
ok = graphene.Boolean()
customer = graphene.Field(CustomerType)
#staticmethod
def mutate(root, info, input=None):
ok = True
resolved = resolve_or_create(**{'location':input.customer_city})
customer_instance = Customer(
customer_name = input.customer_name,
customer_address = input.customer_address,
customer_city = resolved.location,
customer_state = input.customer_state,
customer_zip = input.customer_zip,
customer_email = input.customer_email,
customer_cell_phone = input.customer_cell_phone,
customer_home_phone = input.customer_home_phone,
referred_from = input.referred_from
)
customer_instance.save()
return CreateCustomer(ok=ok, customer=customer_instance)
Here is an example mutation that would create a new customer with an existing location
mutation createCustomer {
createCustomer(input: {
customerName: "Ricky Bobby",
customerAddress: "1050 Airport Drive",
customerCity: {id:1},
customerState: "TX",
customerZip: "75222",
customerEmail: "mem",
customerCellPhone: "124567894",
referredFrom: "g"
}) {
ok,
customer{
id,
customerName,
customerAddress,
customerCity {
id
},
customerState,
customerZip,
customerEmail,
customerCellPhone,
referredFrom
}
}
}
and here is an example mutation that would create a customer with a new location
mutation createCustomer {
createCustomer(input: {
customerName: "Ricky Bobby",
customerAddress: "1050 Airport Drive",
customerCity: {locationCity: "Dallas", locationState: "TX", locationSalesTaxRate:7.77},
customerState: "TX",
customerZip: "75222",
customerEmail: "mem",
customerCellPhone: "124567894",
referredFrom: "g"
}) {
ok,
customer{
id,
customerName,
customerAddress,
customerCity {
id
},
customerState,
customerZip,
customerEmail,
customerCellPhone,
referredFrom
}
}
}
So my question is two-fold.
First, how can I retrieve my passed in location dict from kwargs?
Second, is there a better way to be resolving and creating nested data than this? Would this be expected behavior in a best-practice GraphQL API?
I have also tried resolve_or_create(location=input.customer_city})
I realized my syntax for dictionary assignment and access was wrong.
Corrected function:
def resolve_or_create(*args, **kwargs):
input = {}
result = {}
candidate = None
input['location'] = kwargs['location']
input['customer'] = kwargs.get('customer', None)
input['technician'] = kwargs.get('tech', None)
if input['location'] is not None:
if 'id' in input['location']:
candidate = Location.objects.filter(pk=input['location']['id']).first()
result['location'] = candidate
if candidate is None:
result['location'] = Location.objects.create(
location_city = input['location']['location_city'],
location_state = input['location']['location_state'],
location_sales_tax_rate = input['location']['location_sales_tax_rate']
)
if result['location'] is None:
return None
return result
I would still like to discuss the most effective way to accomplish created and mutating nested data in graphene-django. Is there a DRYer way to achieve what I'm looking to do or something I'm missing?

Setting up a plain graphene nested query

I have successfully created an all graphene query that responds to
query {
person (id: "Mary") {
id
name
}
}
I now want to extend this to be able to loop through all people and return similar data for each.
query {
people {
count
allPersons {
name
}
}
}
How do I get the resolve_allPersons resolver in people to call the person resolver for each person?
Second query you've described can be done with custom type, for example:
class AllPeopleType(graphene.ObjectType):
count = graphene.Int()
all_persons = graphene.List(YourPersonType)
def resolve_count(self, info, **kwargs):
# assumed that django used on backend
return Person.objects.count()
def resolve_all_persons(self, info, **kwargs):
return Person.objects.all()
and query:
class YourQuery(object):
# person = ...
people = graphene.Field(AllPeopleType)
def resolve_people(self, info):
return AllPeopleType()

how to use aliases in graphene-django

I'm using python 3.5 with
Django==1.11.6
graphene==2.0.dev20170802065539
graphene-django==2.0.dev2017083101
graphql-core==2.0.dev20171009101843
graphql-relay==0.4.5
I have a schema that fetch's single objects like this:
class Query(graphene.AbstractType):
story = graphene.Field(storyType, category=graphene.String(), id=graphene.Int())
def resolve_story(self, info, **kwargs):
category = kwargs.get('category')
id = kwargs.get('id')
if category is not None:
return models.story.objects.get(category=models.category.objects.get(name=category))
if id is not None:
return models.story.objects.get(pk=id)
return None
My problem is that I can't use both story(category:"category") and (id:"id") in one query. I read here that I should use aliases but I don't know how.
Thank's for any help.
Is as simple as the example just:
{
storyId: story(id: 1) {
name
}
storyCategory: story(category: "category_name") {
name
}
}
but you can use filter if that is what you mean doing something like:
if category and id is not None:
return models.story.objects.filter(id=id, category=models.category.objects.get(name=category))
and in the query something like:
story(id: 1, name: "category_name") {
name
}

Categories