# Reading data from Odoo

Querying Odoo models with search\_read - domains, field lists and paging through large result sets.

The workhorse of Odoo's external API is **search\_read** - it filters a model with a domain, returns only the fields you ask for and pages through large result sets with **limit** and **offset**. This chapter uses the Odoo.Sample connection defined in the [main Odoo chapter](https://zato.io/docs/dev/odoo/index.html).

## Domains and field lists {#domains-and-field-lists}

A domain is a list of conditions, each a tuple of field name, operator and value. The example below reads customers from a given city, returning only the fields the integration actually needs:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class GetCustomers(Service):
    name = 'demo.odoo.get-customers'

    input = 'city'

    def handle(self) -> 'None':

        # Connection to use
        conn_name = 'Odoo.Sample'

        # Obtain a client connected to the system
        with self.outgoing.odoo.get(conn_name).conn.client() as client:

            # Point the client to the model we want to query
            model = client.get_model('res.partner')

            # Customers from the input city only
            domain = [
                ('customer_rank', '>', 0),
                ('city', '=', self.request.input.city),
            ]

            # Fields to retrieve
            fields = ['name', 'email', 'phone', 'street']

            # Read the matching records
            customers = model.search_read(domain, fields)

            # Return them to the caller
            self.response.payload = {'customers': customers}
```

Each record arrives as a regular Python dict, with the **id** field always included:

```text
{'id': 42, 'name': 'Deco Addict', 'email': 'deco.addict82@example.com',
 'phone': '(603)-996-3829', 'street': '325 Elsie Drive'}
```

## Paging through large sets {#paging-through-large-sets}

Large models are read page by page - **limit** caps the size of each page and **offset** skips the records already processed:

```python
# How many records to read per page
page_size = 200

# Where the current page starts
offset = 0

while True:

    # Read one page of records
    records = model.search_read(domain, fields, offset=offset, limit=page_size)

    # An empty page means the whole result set is exhausted
    if not records:
        break

    # Process the current page
    for record in records:
        self.logger.info('Received -> %s', record['name'])

    # Move on to the next page
    offset += page_size
```

To learn only how many records match, without transferring any of them, use **search\_count**:

```python
how_many = model.search_count(domain)
```

## Learn more {#learn-more}

- [Odoo integrations](https://zato.io/docs/dev/odoo/) - Odoo models from Python services, over XML-RPC and JSON-RPC
- [Creating records](https://zato.io/docs/dev/odoo/creating-records/) - New partners, leads and orders, with their related records
- [Updating and deleting](https://zato.io/docs/dev/odoo/updating-records/) - Writing to many records at once and removing them
- [Odoo calls Zato](https://zato.io/docs/dev/odoo/odoo-calls-zato/) - Automation rules and webhooks that invoke your services
- [Odoo Online](https://zato.io/docs/dev/odoo/odoo-online/) - What differs when the instance is hosted by Odoo
