Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/i18n/rules/vi/DOL008.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# DOL008 — Dùng `flat=True` khi gọi `.values_list()` với một trường duy nhất

**Mức độ mặc định:** info · **Khả năng áp dụng:** safe · **Danh mục:** queryset

Phát hiện `.values_list("field")` được gọi với đúng một đối số vị trí và không có từ khóa nào khác. Khi không có `flat=True`, Django trả về một queryset gồm các tuple một phần tử — `[(1,), (2,), (3,)]` — trong khi gần như mọi trường hợp đều muốn có danh sách phẳng: `[1, 2, 3]`. Dạng tuple một phần tử hầu như không bao giờ là chủ ý khi chỉ yêu cầu một trường; nó chỉ tạo thêm công unpacking hoặc gây lỗi âm thầm khi kết quả được truyền vào lookup `__in` hay các phép tập hợp.

QuickFix tự động thêm `flat=True` và được đánh dấu safe vì ngữ nghĩa hoàn toàn giống nhau với lời gọi một trường: query không thay đổi, chỉ có wrapper Python thay đổi.

## Sai

```python
ids = User.objects.values_list("id")
# ids == [(1,), (2,), (3,)] — có thể không phải kết quả bạn mong muốn
```

## Đúng

```python
ids = User.objects.values_list("id", flat=True)
# ids == [1, 2, 3]
```

## Bỏ qua (Suppress)

```python
# django-orm-lens-disable-next-line DOL008
```

Hoặc theo từng workspace trong `.vscode/settings.json`: `{"djangoOrmLens.rules": {"DOL008": "off"}}`.
29 changes: 29 additions & 0 deletions docs/i18n/rules/vi/DOL021.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# DOL021 — Dùng `timezone.now()` thay vì `datetime.now()` trong dự án Django

**Mức độ mặc định:** warning · **Khả năng áp dụng:** safe · **Danh mục:** datetime

Phát hiện các lời gọi `datetime.datetime.now()` hoặc `datetime.now()` (không có đối số `tz`) trong dự án Django. Khi `USE_TZ = True` (mặc định của Django từ 5.0; template `startproject` đã đặt thành `True` từ 4.0), datetime naive không thể so sánh với datetime aware và sẽ bị từ chối bởi `DateTimeField` khi lưu. Dùng `timezone.now()` từ `django.utils.timezone` luôn trả về datetime aware theo UTC bất kể giá trị của `USE_TZ`, khiến nó trở thành lựa chọn an toàn mặc định trong mọi codebase Django.

QuickFix viết lại lời gọi và thêm import nếu còn thiếu; được đánh dấu safe vì kiểu đầu ra thay đổi từ naive sang aware — đây chính là kiểu đúng cho mọi trường datetime trong Django.

## Sai

```python
from datetime import datetime
created_at = datetime.now()
```

## Đúng

```python
from django.utils import timezone
created_at = timezone.now()
```

## Bỏ qua (Suppress)

```python
# django-orm-lens-disable-next-line DOL021
```

Hoặc theo từng workspace trong `.vscode/settings.json`: `{"djangoOrmLens.rules": {"DOL021": "off"}}`.
27 changes: 27 additions & 0 deletions docs/i18n/rules/vi/DOL022.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# DOL022 — Dùng `timezone.now()` để so sánh với các giá trị `DateTimeField`

**Mức độ mặc định:** warning · **Khả năng áp dụng:** safe · **Danh mục:** datetime

Phát hiện các biểu thức filter so sánh một lookup `DateTimeField` với `datetime.now()` hoặc `date.today()` mà không có timezone awareness. So sánh các giá trị aware từ DB với datetime naive của Python sẽ gây `TypeError` ở runtime (hoặc với một số backend, trả về kết quả sai một cách âm thầm). `timezone.now()` luôn là aware và luôn đúng cho mục đích này.

## Sai

```python
from datetime import datetime
ExpiredToken.objects.filter(expires_at__lt=datetime.now())
```

## Đúng

```python
from django.utils import timezone
ExpiredToken.objects.filter(expires_at__lt=timezone.now())
```

## Bỏ qua (Suppress)

```python
# django-orm-lens-disable-next-line DOL022
```

Hoặc theo từng workspace trong `.vscode/settings.json`: `{"djangoOrmLens.rules": {"DOL022": "off"}}`.
31 changes: 31 additions & 0 deletions docs/i18n/rules/vi/DOL031.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# DOL031 — Dùng `get_object_or_404()` thay vì `.get()` trực tiếp trong view

**Mức độ mặc định:** info · **Khả năng áp dụng:** safe · **Danh mục:** forms-views

Phát hiện các lời gọi `.get()` bên trong hàm view Django hoặc method của class-based view mà không được bọc trong khối `try/except ObjectDoesNotExist` và không được thay bằng `.get_object_or_404()`. Một `.get()` trần sẽ raise `Model.DoesNotExist` (subclass của `ObjectDoesNotExist`) khi không tìm thấy row phù hợp — exception handler mặc định của Django sẽ chuyển điều này thành lỗi 500, làm lộ stack trace ở chế độ DEBUG và trả về lỗi không rõ nghĩa ở production. `get_object_or_404()` chuyển điều kiện tương tự thành response 404 gọn gàng.

QuickFix thay `.get(...)` bằng `get_object_or_404(Model, ...)` và tự thêm import.

## Sai

```python
def post_detail(request, pk):
post = Post.objects.get(pk=pk) # raise 500 nếu không tìm thấy
```

## Đúng

```python
from django.shortcuts import get_object_or_404

def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
```

## Bỏ qua (Suppress)

```python
# django-orm-lens-disable-next-line DOL031
```

Hoặc theo từng workspace trong `.vscode/settings.json`: `{"djangoOrmLens.rules": {"DOL031": "off"}}`.
29 changes: 29 additions & 0 deletions docs/i18n/rules/vi/DOL032.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# DOL032 — Tránh truyền dữ liệu thô từ `request.GET` / `request.POST` trực tiếp vào queryset

**Mức độ mặc định:** warning · **Khả năng áp dụng:** unsafe · **Danh mục:** forms-views

Phát hiện các giá trị `request.GET` hoặc `request.POST` được truyền trực tiếp vào đối số `.filter()` hoặc `.exclude()` của queryset mà không qua Django Form hoặc danh sách cho phép (allow-list) tường minh. Dữ liệu request thô là không đáng tin: người gọi có thể gửi tên trường bất ngờ để duyệt qua các quan hệ (`__user__password`), kích hoạt các join tốn kém, hoặc làm lộ dữ liệu thông qua field enumeration. Pattern đúng là validate và lọc các trường qua `Form` hoặc `FilterSet` trước khi tạo queryset.

Khả năng áp dụng là `unsafe` vì cần hiểu rõ bề mặt filter mong muốn.

## Sai

```python
Post.objects.filter(**request.GET.dict()) # mọi key người gọi gửi đều trở thành filter
```

## Đúng

```python
form = PostFilterForm(request.GET)
if form.is_valid():
Post.objects.filter(**form.cleaned_data)
```

## Bỏ qua (Suppress)

```python
# django-orm-lens-disable-next-line DOL032
```

Hoặc theo từng workspace trong `.vscode/settings.json`: `{"djangoOrmLens.rules": {"DOL032": "off"}}`.
65 changes: 40 additions & 25 deletions docs/i18n/rules/vi/README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,49 @@
# Tài liệu Quy tắc
# Tài liệu tham khảo quy tắc

Mỗi trang dưới đây mô tả một quy tắc cụ thể (hiện tại danh mục chứa 7 quy tắc): mã quy tắc, độ nghiêm trọng mặc định, khả năng áp dụng, các ví dụ về code sai/đúng, và cách bỏ qua (suppress) nếu cần.
Mỗi mã `DOL` là một rule lint mà extension VS Code áp dụng ngay khi bạn gõ code. Các lệnh CLI (`nplusone`, `migration-risk`, `blast-radius`) chạy cùng phân tích đó ở quy mô toàn dự án.

> The remaining rules — `DOL011`–`DOL032`, plus `migrations.md` and `nplusone.md` — are not translated yet; see the [English reference](../../../rules/README.md). This note is in English because it was written by someone who does not speak Vietnamese; a translation of it is welcome.
## Quy tắc queryset

## Queryset

| Mã | Tóm tắt | Mức độ | Áp dụng |
|----|---------|--------|---------|
| Mã | Tiêu đề | Mức độ | Khả năng áp dụng |
|----|---------|--------|------------------|
| [DOL001](DOL001.md) | Ưu tiên `.exists()` thay vì `.count() > 0` | info | safe |
| [DOL002](DOL002.md) | Ưu tiên `not .exists()` thay vì `.count() == 0` | info | safe |
| [DOL003](DOL003.md) | Ưu tiên `not .exists()` thay vì `.first() is None` | info | safe |
| [DOL004](DOL004.md) | Ưu tiên `.exists()` thay vì `.first() is not None` | info | safe |
| [DOL005](DOL005.md) | Cân nhắc dùng `Q(...)` thay vì chuỗi `.filter().exclude()` | hint | suggestion |
| [DOL006](DOL006.md) | Bỏ `list()` bọc ngoài QuerySet trong vòng lặp for | info | safe |
| [DOL002](DOL002.md) | Ưu tiên `.count()` thay vì `len(queryset)` | info | safe |
| [DOL003](DOL003.md) | Tránh `list(queryset)` trong ngữ cảnh boolean | info | safe |
| [DOL004](DOL004.md) | Dùng `.only()` / `.defer()` để giới hạn trường được tải | info | unsafe |
| [DOL005](DOL005.md) | Tránh gọi `.all()` trước `.filter()` | info | safe |
| [DOL006](DOL006.md) | Dùng `.iterator()` cho queryset lớn | warning | unsafe |
| [DOL007](DOL007.md) | Có thể xảy ra N+1: truy cập thuộc tính bên trong vòng lặp for | warning | unsafe |
| [DOL008](DOL008.md) | Dùng `flat=True` khi gọi `.values_list()` với một trường duy nhất | info | safe |

## Quy tắc định nghĩa model

| Mã | Tiêu đề | Mức độ | Khả năng áp dụng |
|----|---------|--------|------------------|
| [DOL011](DOL011.md) | Thêm `db_index=True` cho trường FK dùng trong filter | warning | unsafe |
| [DOL012](DOL012.md) | Thêm `db_index=True` cho trường dùng trong `order_by()` | info | unsafe |
| [DOL013](DOL013.md) | Dùng `select_related` cho các truy cập FK trong serializer | warning | unsafe |
| [DOL014](DOL014.md) | Dùng `prefetch_related` cho các truy cập FK ngược / M2M | warning | unsafe |
| [DOL015](DOL015.md) | Tránh lưu dữ liệu lớn trực tiếp trên model | info | unsafe |

## Quy tắc datetime

| Mã | Tiêu đề | Mức độ | Khả năng áp dụng |
|----|---------|--------|------------------|
| [DOL021](DOL021.md) | Dùng `timezone.now()` thay vì `datetime.now()` | warning | safe |
| [DOL022](DOL022.md) | Dùng `timezone.now()` để so sánh với `DateTimeField` | warning | safe |

## Mức độ nghiêm trọng
## Quy tắc forms / views

| Từ khóa | Ý nghĩa |
|---------|----------|
| `error` | Luôn sai; ưu tiên sửa ngay |
| `warning` | Rất có thể sai; cần xem xét |
| `info` | Viết lại an toàn nhưng không bắt buộc |
| `hint` | Gợi ý cải thiện; cần đánh giá từng trường hợp |
| Mã | Tiêu đề | Mức độ | Khả năng áp dụng |
|----|---------|--------|------------------|
| [DOL031](DOL031.md) | Dùng `get_object_or_404()` thay vì `.get()` trực tiếp | info | safe |
| [DOL032](DOL032.md) | Tránh truyền dữ liệu request thô vào queryset | warning | unsafe |

## Khả năng áp dụng
## Công cụ phân tích CLI

| Từ khóa | Ý nghĩa |
|---------|----------|
| `safe` | QuickFix có thể áp dụng tự động |
| `suggestion` | Cần kiểm tra trước khi áp dụng |
| `unsafe` | Không có QuickFix; phải sửa thủ công |
| Lệnh | Chức năng |
|------|-----------|
| [nplusone](nplusone.md) | Phát hiện các pattern N+1 trên toàn dự án |
| [migration-risk](migrations.md) | Đánh giá file migration theo 16 quy tắc an toàn |
| [blast-radius](blast-radius.md) | Kết hợp migration risk với phân tích tham chiếu toàn codebase |
| [drift](drift.md) | Phát hiện khi `db_table` hoặc tên cột lệch khỏi convention Django |
Loading
Loading