Closed lcasassa closed 4 years ago
Maybe the multi-table-hop is not supported? if so, any recommendations on how to implement this? I could give it a try and do a PR.
This has something to do with the prefetch in django.
>>> q=Part.objects.prefetch_related(Prefetch('part_mappings', to_attr='pms'))
>>> q[0].pms
[<PartMapping: KH1234>]
>>> q=Part.objects.prefetch_related(Prefetch('part_mappings__part_supplier_links', to_attr='pms_psls'))
>>> q[0].pms_psls
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Part' object has no attribute 'pms_psls'
>>>
It is not able to do double reverse hops.
I will continue debugging here as this could be helpful to someone else. Hope you don't mind.
>>> import logging
>>> l = logging.getLogger('django.db.backends')
>>> l.setLevel(logging.DEBUG)
>>> l.addHandler(logging.StreamHandler())
>>> from supplychain.models import Part, Supplier
>>> from django.db.models import Prefetch
>>> q=Part.objects.prefetch_related(Prefetch('part_mappings__part_supplier_links__supplier_mapping__supplier', to_attr='s'))
>>> def get_data(q):
... for part in q:
... print("get another part")
... for part_mapping in part.part_mappings.all():
... print("get another part mapping")
... for part_supplier_link in part_mapping.part_supplier_links.all():
... print("get another part supplier link")
... print(part_supplier_link.supplier_mapping.supplier.name)
>>> get_data(q)
(0.000) SELECT "supplychain_part"."id", "supplychain_part"."name" FROM "supplychain_part"; args=()
(0.000) SELECT "supplychain_partmapping"."id", "supplychain_partmapping"."name", "supplychain_partmapping"."part_id" FROM "supplychain_partmapping" WHERE "supplychain_partmapping"."part_id" IN (1, 2); args=(1, 2)
(0.000) SELECT "supplychain_partsupplierlink"."id", "supplychain_partsupplierlink"."part_mapping_id", "supplychain_partsupplierlink"."supplier_mapping_id" FROM "supplychain_partsupplierlink" WHERE "supplychain_partsupplierlink"."part_mapping_id" IN (1, 2); args=(1, 2)
(0.000) SELECT "supplychain_suppliermapping"."id", "supplychain_suppliermapping"."name", "supplychain_suppliermapping"."supplier_id" FROM "supplychain_suppliermapping" WHERE "supplychain_suppliermapping"."id" IN (1); args=(1,)
(0.000) SELECT "supplychain_supplier"."id", "supplychain_supplier"."name" FROM "supplychain_supplier" WHERE "supplychain_supplier"."id" IN (1); args=(1,)
get another part
get another part mapping
get another part supplier link
(0.000) SELECT "supplychain_supplier"."id", "supplychain_supplier"."name" FROM "supplychain_supplier" WHERE "supplychain_supplier"."id" = 1 LIMIT 21; args=(1,)
Super
get another part
get another part mapping
get another part supplier link
Super
>>>
You can see that everything is prefetched except the name of the supplier. yahoo. let's see how to prefetch it.
Testing the same with more data:
>>> from django.db import reset_queries
>>> reset_queries()
>>> q=Part.objects.prefetch_related(Prefetch('part_mappings__part_supplier_links__supplier_mapping__supplier', to_attr='s'))
>>> get_data(q)
(0.000) SELECT "supplychain_part"."id", "supplychain_part"."name" FROM "supplychain_part"; args=()
(0.000) SELECT "supplychain_partmapping"."id", "supplychain_partmapping"."name", "supplychain_partmapping"."part_id" FROM "supplychain_partmapping" WHERE "supplychain_partmapping"."part_id" IN (1, 2); args=(1, 2)
(0.000) SELECT "supplychain_partsupplierlink"."id", "supplychain_partsupplierlink"."part_mapping_id", "supplychain_partsupplierlink"."supplier_mapping_id" FROM "supplychain_partsupplierlink" WHERE "supplychain_partsupplierlink"."part_mapping_id" IN (1, 2, 3); args=(1, 2, 3)
(0.000) SELECT "supplychain_suppliermapping"."id", "supplychain_suppliermapping"."name", "supplychain_suppliermapping"."supplier_id" FROM "supplychain_suppliermapping" WHERE "supplychain_suppliermapping"."id" IN (1, 2, 3); args=(1, 2, 3)
(0.000) SELECT "supplychain_supplier"."id", "supplychain_supplier"."name" FROM "supplychain_supplier" WHERE "supplychain_supplier"."id" IN (1, 2, 3); args=(1, 2, 3)
get another part
get another part mapping
get another part supplier link
(0.000) SELECT "supplychain_supplier"."id", "supplychain_supplier"."name" FROM "supplychain_supplier" WHERE "supplychain_supplier"."id" = 1 LIMIT 21; args=(1,)
Super
get another part
get another part mapping
get another part supplier link
Super
get another part supplier link
(0.000) SELECT "supplychain_supplier"."id", "supplychain_supplier"."name" FROM "supplychain_supplier" WHERE "supplychain_supplier"."id" = 2 LIMIT 21; args=(2,)
Duper
get another part mapping
get another part supplier link
(0.000) SELECT "supplychain_supplier"."id", "supplychain_supplier"."name" FROM "supplychain_supplier" WHERE "supplychain_supplier"."id" = 3 LIMIT 21; args=(3,)
Triple
>>>
I can confirm: You can see that everything is prefetched except the name of the supplier.
It's working!!
The solution is "Prefetch" in cascade :)
def prefetch_suppliers(info, *_args, **_kwargs):
return Prefetch(
"part_mappings", queryset=PartMapping.objects.all().prefetch_related(
Prefetch(
"part_supplier_links", queryset=PartSupplierLink.objects.all().prefetch_related(
Prefetch(
"supplier_mapping", queryset=SupplierMapping.objects.all().prefetch_related(
Prefetch(
"supplier", queryset=Supplier.objects.all()
)
)
)
)
)
)
)
class PartType(gql_optimizer.OptimizedDjangoObjectType):
class Meta:
model = Part
fields = ("id", "name")
suppliers = gql_optimizer.field(
graphene.List(lambda: SupplierType), prefetch_related=prefetch_suppliers
)
def resolve_suppliers(part, _info, **_kwargs):
suppliers = []
for part_mapping in part.part_mappings.all():
for part_supplier_link in part_mapping.part_supplier_links.all():
suppliers.append(part_supplier_link.supplier_mapping.supplier)
return suppliers
Source of the solution was found on the last comment of: https://www.reddit.com/r/django/comments/evjmx2/filtering_prefetch_object_using_outerref_and/
How can I get the to_attr to work? It does not set the new attribute. I tried several ways including the one on the main README (docs).
models.py:
shema.py
resolve_suppliers on shema.py is not finding the attribute prefetched_suppliers set by the to_attr on the prefetch.
Versions used:
What am I doing wrong? Is this a bug?
Thanks