django-ninja
django-ninja copied to clipboard
Async reverse relationship
Expectation: To be able to run this view async, should this be possible?
Output:
Traceback (most recent call last):
File "/Users/christophercarvalho/ninja/env/lib/python3.11/site-packages/ninja/operation.py", line 282, in run
return self._result_to_response(request, result, temporal_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/christophercarvalho/ninja/env/lib/python3.11/site-packages/ninja/operation.py", line 208, in _result_to_response
validated_object = response_model.model_validate(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/christophercarvalho/ninja/env/lib/python3.11/site-packages/pydantic/main.py", line 532, in model_validate
return cls.__pydantic_validator__.validate_python(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for NinjaResponseSchema
response
Error extracting attribute: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async. [type=get_attribute_error, input_value=<DjangoGetter: <ninja.ope... object at 0x10a4fbe10>>, input_type=DjangoGetter]
For further information visit https://errors.pydantic.dev/2.7/v/get_attribute_error
Schema
class CardOut(Schema):
id: int
title: str
uuid: uuid.UUID
price: float | None
ext_file: str | None
comp_file: str | None
# image: str | None
image_file: str | None
quantity: int | None
# includes: str | None
inc_list: list[str]
desc: str | None
song: SongOut | None
cardhold_set: list[CardToCardHoldOut] | None
artist_set: List[ArtistOut] | None
View
@sync_to_async
def get_cards():
queryset = (
Card.objects.prefetch_related("cardhold_set")
)
return queryset
@api.get("/get-cards", response=List[CardOut])
@decorate_view(cache_page(7 * 24 * 60 * 60))
async def list_cards(request):
cards = await get_cards()
return cards
Models
class Card(models.Model):
title = models.CharField(max_length=128)
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
image = models.FileField(null=True, blank=True)
desc = models.TextField(null=True, blank=True)
includes = models.CharField(max_length=256, null=True, blank=True)
song = models.ForeignKey(Song, null=True, on_delete=models.CASCADE)
inc_media = models.URLField(null=True, blank=True)
g_folder_id = models.CharField(max_length=42, blank=True)
price = models.FloatField(null=True, blank=True)
quantity = models.IntegerField(default=1)
ext_file = models.URLField(blank=True, null=True)
comp_file = models.URLField(blank=True, null=True)
def __str__(self):
return self.title
class Artist(models.Model):
name = models.CharField(max_length=128)
slug = models.SlugField(unique=True)
about = models.TextField(null=True, blank=True)
email = models.EmailField(null=True)
instagram = models.CharField(max_length=30, blank=True)
cards = models.ManyToManyField(Card, null=True, blank=True)
def __str__(self):
return self.name
class CardHold(models.Model):
collector = models.ForeignKey(Collector, on_delete=models.CASCADE)
card = models.ForeignKey(Card, on_delete=models.CASCADE)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.collector.name} collected {self.card.title} at {self.updated}"
@carvalhochris
try pre fetching also artist_set
Card.objects.prefetch_related("cardhold_set", "artist_set")
@carvalhochris
try pre fetching also artist_set
Card.objects.prefetch_related("cardhold_set", "artist_set")
Hi @vitalik thanks for your response.
I tried that and get the following:
Traceback (most recent call last):
File "/Users/christophercarvalho/ninja/env/lib/python3.11/site-packages/ninja/operation.py", line 282, in run
return self._result_to_response(request, result, temporal_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/christophercarvalho/ninja/env/lib/python3.11/site-packages/ninja/operation.py", line 208, in _result_to_response
validated_object = response_model.model_validate(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/christophercarvalho/ninja/env/lib/python3.11/site-packages/pydantic/main.py", line 532, in model_validate
return cls.__pydantic_validator__.validate_python(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for NinjaResponseSchema
response
Error extracting attribute: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async. [type=get_attribute_error, input_value=<DjangoGetter: <ninja.ope... object at 0x1136c23d0>>, input_type=DjangoGetter]
For further information visit https://errors.pydantic.dev/2.7/v/get_attribute_error
yeah, maybe prefetch_related does not support async yet..
try:
@api.get("/get-cards", response=List[CardOut])
@decorate_view(cache_page(7 * 24 * 60 * 60))
async def list_cards(request):
queryset = Card.objects.prefetch_related("cardhold_set", "artist_set")
cards = await sync_to_async(list)(queryset) # force executing db query here by converting to list
return cards
yeah, maybe prefetch_related does not support async yet..
try:
@api.get("/get-cards", response=List[CardOut]) @decorate_view(cache_page(7 * 24 * 60 * 60)) async def list_cards(request): queryset = Card.objects.prefetch_related("cardhold_set", "artist_set") cards = await sync_to_async(list)(queryset) # force executing db query here by converting to list return cards
I think you might be right, it doesn't work when I include artist_set, song, or cardhold_set in the schema
This issue is actually quite annoying, can we get it handled by django-ninja @vitalik?