tortoise / tortoise/tortoise-orm

Examples of using OuterRef and Subquery

Open
#337 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.6k
Forks
516
Avg merge
2d 21h
Merged PRs (30d)
9

Description

These are a few examples related to conversations #336 and #236 Assume the following models:

class Product(models.Model):
    class Meta:
        db_table = "store_product"
        ordering = ['date_modified', ]

    id = fields.IntegerField(primary_key=True)
    name = fields.CharField(max_length=255)

    images = fields.ManyToManyField('store.Image', through='store.ProductImage',)
    brand = fields.ForeignKey('store.Brand', on_delete=fields.CASCADE, null=True,
        default=None, related_name='products')

class Image(models.Model):
    class Meta:
        db_table = "store_image"
        ordering = ['id', ]

    id = fields.IntegerField(primary_key=True)
    src = CharField(max_length=255)

class Brand(models.Model):
    class Meta:
        db_table = "store_brand"
        ordering = ['id', ]

    id = fields.IntegerField(primary_key=True)
    name = fields.CharField(max_length=255)

Note Product is in ManyToMany relationship with Image and ForeignKey (or OneToMany) relationship with Brand.

Also note that for the through field my code allows the model reference to be used, (tortoise only allows actual database table name). I have skipped the definition of ProductImage model here.

Now the following code prefetches only the first four images of a product. Notice the OuterRef and Prefetch on a ManyToMany field.

async def list_products_limit_images():
    subquery = Image.filter(products=OuterRef('products')).limit(4).values_list('id', flat=True)
    prefetch = Prefetch('images', queryset=Image.filter(id__in=Subquery(subquery)))

    async for product in Product.all().limit(5).prefetch_related(prefetch):
        print(f"{product.id} {product.name}")
        async for image in product.images:
            print(f"\timage: {image.id} {image.src}")

The following method, is almost the same thing. It prefetches the first six products for any brand. Notice the OuterReft and Prefetch on a ForeignKey field.

async def list_brands_prefetch_limited_products():
    subquery = Product.filter(brand=OuterRef('brand')).limit(6).values_list('id', flat=True)
    prefetch = Prefetch('products', queryset=Product.filter(id__in=Subquery(subquery)))

    async for brand in Brand.all().prefetch_related(prefetch):
        print(f"{brand.id} {brand.name}")
        for product in brand.products:
            print(f"\t{product.id} {product.name}")

hope this helps.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No file or test is identified. Start by locating the documentation entry points for OuterRef, Subquery, and Prefetch, then compare them with the Product, Image, and Brand examples in the issue. Done means the relevant documentation includes clear examples for limited ManyToMany and ForeignKey prefetches, if maintainers confirm that is the intended change.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
databases
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.