tortoise / tortoise/tortoise-orm
Computed value with deeply nested foreign keys fails with AttributeError
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 5.6k
- Forks
- 516
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 9
Description
For our application we need to calculate an aggregated value in a relation (Batch in the example) from a field in a relation that is four levels of foreign keys removed. Aggregates for a field three levels removed work fine, but with an additional level it fails with `AttributeError: 'QuerySet' object has no attribute 'value'.
I have a minimal example that demonstrates the problem. With line 18 commented-out, the result is as expected, but with line 18 active, the AttributeError is raised.
from tortoise import fields, run_async, Tortoise
from tortoise.contrib.pydantic import pydantic_model_creator
from tortoise.models import Model
class Batch(Model):
actions: fields.ReverseRelation["Action"]
name: str = fields.TextField()
def names(self) -> list[str]:
return [action.task.job.name for action in self.actions]
def values(self) -> list[int]:
return [action.task.job.node.value for action in self.actions]
class PydanticMeta:
computed = (
# "values", # Uncomment this to trigger the error
"names",
)
class Action(Model):
batch: fields.ForeignKeyRelation["Batch"] = fields.ForeignKeyField("models.Batch", related_name="actions")
task: fields.ForeignKeyRelation["Task"] = fields.ForeignKeyField("models.Task", related_name="actions")
class Task(Model):
job: fields.ForeignKeyRelation["Job"] = fields.ForeignKeyField("models.Job", related_name="tasks")
actions: fields.ReverseRelation["Action"]
class Job(Model):
node: fields.ForeignKeyRelation["Node"] = fields.ForeignKeyField("models.Node", related_name="jobs")
tasks: fields.ReverseRelation["Task"]
name: str | None = fields.TextField(null=True)
class Node(Model):
jobs: fields.ReverseRelation["Job"]
value: int = fields.IntField()
async def run():
await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]})
await Tortoise.generate_schemas()
Batch_Pydantic = pydantic_model_creator(Batch)
node_1 = await Node.create(value=4)
node_2 = await Node.create(value=5)
job_1 = await Job.create(node=node_1, name="First job")
job_2 = await Job.create(node=node_2, name="Second job")
task_1 = await Task.create(job=job_1)
task_2 = await Task.create(job=job_2)
batch = await Batch.create(name="batch 1")
await Action.create(task=task_1, batch=batch)
await Action.create(task=task_2, batch=batch)
p = await Batch_Pydantic.from_tortoise_orm(batch)
print(p.json(indent=4))
if __name__ == "__main__":
run_async(run())
Probably related to this is the fact that the json print on line 61 does not include the referenced Node relation for the Job table rows:
{
"id": 1,
"name": "batch 1",
"actions": [
{
"id": 1,
"task": {
"id": 1,
"job": {
"id": 1,
"name": "First job"
}
}
},
{
"id": 2,
"task": {
"id": 2,
"job": {
"id": 2,
"name": "Second job"
}
}
}
],
"names": [
"First job",
"Second job"
]
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the minimal example, especially pydantic_model_creator(Batch) and Batch_Pydantic.from_tortoise_orm(batch), then trace how the computed values field accesses the four-level relation chain. Reproduce the AttributeError with the values entry enabled and verify that serialization completes and includes the referenced Node data.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sqlite
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100