You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tortoise-ORM中ForeignKeyField的related_name关键字用法疑问及示例分析

Let’s break down exactly how related_name works in your code, and why you aren’t seeing it in migrations or your Postgres database.

The short answer: related_name is purely an ORM-level construct, not a database-level one.

Your database only cares about the foreign key columns (like owner_id and builder_id in the Toy table) that store the ID of the associated Person. The related_name is just a name Tortoise-ORM uses to create a convenient way to query backwards from Person to Toy—it doesn’t create any extra tables, columns, or database constraints. That’s why you won’t find it in Aerich migrations or when inspecting your Postgres tables.

In your model, you’ve defined two foreign keys from Toy to Person:

  • owner = ForeignKeyField("models.Person", related_name='toys_owner')
  • builder = ForeignKeyField("models.Person", related_name='toys_builder')

Each related_name creates a dynamic attribute on Person instances that lets you fetch all Toy instances linked through that specific foreign key. Here’s what that looks like in practice:

1. Querying toys built by a Person

For person_a (Gepheto, who built Pinocchio), you can use the toys_builder attribute to get all toys he’s built:

gepheto_built_toys = await person_a.toys_builder.all()
# This returns a queryset containing the Pinocchio Toy instance

2. Querying toys owned by a Person

For person_b (Fairy, who owns Pinocchio), use the toys_owner attribute to get all toys she owns:

fairy_owned_toys = await person_b.toys_owner.all()
# This also returns the Pinocchio Toy instance

If you omitted related_name, Tortoise-ORM would use a default name: {model_name}_set (so toy_set for your Toy model). But since you have two separate foreign keys pointing to Person, using the default would cause a conflict—Tortoise wouldn’t know which toy_set corresponds to owner vs builder. That’s why setting unique related_name values is mandatory here.

Example of using this in your main function

Let’s modify your main function to demonstrate the reverse queries:

async def main():
    await tortoise.Tortoise.init(
        db_url='sqlite://:memory:',
        modules={'models': ["__main__"]}
    )
    await tortoise.Tortoise.generate_schemas()
    person_a = await Person.create(name="Gepheto")
    person_b = await Person.create(name="Fairy")
    person_c = await Person.create(name="Nobody")
    pinocchio = await Toy.create(name="Pinocchio", builder=person_a, owner=person_b)
    
    # Reverse query examples
    gepheto_toys = await person_a.toys_builder.all()
    print(f"Gepheto built: {[toy.name for toy in gepheto_toys]}")  # Output: Gepheto built: ['Pinocchio']
    
    fairy_toys = await person_b.toys_owner.all()
    print(f"Fairy owns: {[toy.name for toy in fairy_toys]}")  # Output: Fairy owns: ['Pinocchio']
    
    nobody_toys = await person_c.toys_owner.all() + await person_c.toys_builder.all()
    print(f"Nobody has: {[toy.name for toy in nobody_toys]}")  # Output: Nobody has: []
    
    await tortoise.Tortoise.close_connections()

Key Takeaways

  • related_name is a convenience for reverse queries at the ORM level—it doesn’t affect your database schema.
  • Use unique related_name values when multiple foreign keys point to the same model to avoid naming conflicts.
  • You can use the related_name attribute on the parent model instance with any Tortoise query methods (.all(), .filter(), .get(), etc.) to fetch related child records.

内容的提问来源于stack exchange,提问作者Lin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 21:17:28