# Tất cả những gì cần biết về lọc database trong Odoo

> dbfilter của Odoo chọn database nào mà một request được truy cập ra sao, những cái bẫy regex cần tránh, và cách module dbfilter_from_header của OCA giúp Nginx gắn tên miền riêng với từng database.

**Date:** 2020-03-26
**Source:** <https://trobz.com/vi/insights/odoo-dbfilter/>

---


Odoo là hệ thống multi-tenant: một Odoo server có thể chạy và phục vụ nhiều database. Tùy chọn `dbfilter` quyết định mỗi request được truy cập những database nào.

[Tài liệu triển khai chính thức](https://www.odoo.com/documentation/19.0/administration/on_premise/deploy.html#dbfilter) có phần giới thiệu khá tốt về lọc database. Bài viết này đi sâu vào cách nó hoạt động trong thực tế, những cái bẫy cần tránh, và cách xử lý khi tên miền không khớp với tên database.

## dbfilter hoạt động như thế nào?

`dbfilter` là một biểu thức chính quy (regex) được dựng động cho từng request. Bạn có thể dùng các biến `%d` và `%h` để điều chỉnh regex theo header `Host` của HTTP request:

- `%d` được thay bằng subdomain đầu tiên sau `www`:
  - `https://demo.example.com`: `%d` = `demo`
  - `https://www.demo.example.com`: `%d` = `demo`
- `%h` được thay bằng toàn bộ tên miền, bỏ phần port nếu có:
  - `https://demo.example.com`: `%h` = `demo.example.com`
  - `https://demo.example.com:8080`: `%h` = `demo.example.com`

Vì vậy, nếu đặt `dbfilter = ^%d.*\Z`, người dùng truy cập `https://demo.example.com` sẽ vào được các database khớp với `^demo.*\Z`, chẳng hạn:

- `demo`
- `demo_backup_20180404`

## Cẩn thận với regex quá rộng

Odoo lọc danh sách database bằng `re.match`:

```python
dbs = [i for i in dbs if re.match(r, i)]
```

`re.match` neo regex ở đầu tên nhưng không neo ở cuối, nên rất dễ viết ra một regex rộng hơn dự định. Ví dụ, nếu đặt `dbfilter = demo_*`, regex này cũng khớp với `demoinefromage_prod`.

## Khi tên miền và tên database không khớp: dbfilter_from_header

Không phải lúc nào cũng suy ra được tên database từ tên miền.

Ví dụ, một instance Odoo có thể phục vụ hai database:

- `customer1_prod`
- `customer2_prod`

Nhưng mỗi khách hàng lại muốn dùng tên miền riêng:

- `https://back.customer1.com`
- `https://app.customer2.com`

Để xử lý trường hợp này, bạn có thể dùng module [`dbfilter_from_header`](https://github.com/OCA/server-tools/tree/9.0/dbfilter_from_header) của OCA.

### Cài đặt

```bash
git clone https://github.com/OCA/server-tools -b 9.0 --no-checkout --single-branch --depth 1
cd server-tools
git config core.sparsecheckout true
echo "dbfilter_from_header" > .git/info/sparse-checkout
git read-tree -mu HEAD
```

### Cấu hình

Ở phía Nginx, cần khai báo một bộ lọc database riêng cho từng virtual host. Trong ví dụ này, với `https://back.customer1.com`:

```nginx
server_name back.customer1.com;

(...)

location / {

    (...)

    proxy_set_header X-Odoo-dbfilter ^customer1_.*\Z;
}
```

Hai điểm cần lưu ý:

- trước khi chuyển request cho Odoo, Nginx lấy tất cả HTTP header, chuyển thành chữ thường và đổi dấu gạch ngang thành dấu gạch dưới;
- chúng tôi dùng `\Z` thay cho `$` để khớp với cuối chuỗi, vì Nginx không escape được `$`.

## Hai tầng lọc

`dbfilter_from_header` tôn trọng `dbfilter` đã khai báo ở cấp Odoo, nên có thể thiết lập hai tầng lọc. Trong cấu hình Odoo:

```ini
dbfilter = ^.+prod.*$
```

Và trong Nginx:

```nginx
proxy_set_header X-Odoo-dbfilter ^.*customer1.*\Z;
```

