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.
First: Why related_name doesn’t show up in migrations or the 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.
How related_name works in your example
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
3. What happens if you don’t set related_name?
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_nameis a convenience for reverse queries at the ORM level—it doesn’t affect your database schema.- Use unique
related_namevalues when multiple foreign keys point to the same model to avoid naming conflicts. - You can use the
related_nameattribute on the parent model instance with any Tortoise query methods (.all(),.filter(),.get(), etc.) to fetch related child records.
内容的提问来源于stack exchange,提问作者Lin

