Django cho phép thay model User mặc định bằng model của riêng bạn thông qua AUTH_USER_MODEL — nhưng chỉ đổi được trước migration đầu tiên. Cách an toàn nhất: tạo custom User ngay khi khởi tạo project, kể cả khi chưa cần thêm field nào, vì sau này chỉ muốn thêm một trường như số điện thoại thôi cũng phải trả giá bằng migration thủ công.

Vấn đề

Lúc mới bắt đầu, dùng thẳng auth.User của Django là tiện nhất: đăng nhập, admin, phân quyền đều chạy sẵn. Rắc rối chỉ xuất hiện vài tháng sau, khi sếp yêu cầu lưu thêm số điện thoại, ngày sinh hay avatar cho tài khoản. Lúc đó AUTH_USER_MODEL đã “dính” vào hàng loạt migration cũ, và Django không cho đổi model user sau khi database đã migrate. Muốn đổi, bạn phải sửa tay migration, hoặc xóa database làm lại từ đầu — cả hai đều đau, nhất là khi project đã có dữ liệu thật.

# models.py — cách làm "tiện tay" lúc đầu, và là cái bẫy về sau
from django.contrib.auth.models import User
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)

Đoạn code trên chạy tốt hôm nay, nhưng nó khóa cứng project vào auth.User. Mọi ForeignKey trỏ thẳng tới model mặc định sẽ phải sửa lại hết nếu sau này bạn đổi ý.

Giải pháp

Tạo app accounts ngay khi khởi tạo project, trước lần migrate đầu tiên, rồi khai báo model user của riêng bạn:

# settings.py
AUTH_USER_MODEL = "accounts.User"
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
phone = models.CharField(max_length=15, blank=True)
birth_date = models.DateField(null=True, blank=True)

Kế thừa AbstractUser giữ nguyên toàn bộ field và hành vi mặc định — username, mật khẩu đã hash, nhóm, quyền — bạn chỉ việc thêm field mới. Muốn model xuất hiện trong admin thì đăng ký với UserAdmin có sẵn:

# accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)

Mọi chỗ khác trong project trỏ tới user thì dùng settings.AUTH_USER_MODEL trong ForeignKey, và get_user_model() trong code chạy:

# blog/models.py — trỏ tới user bằng AUTH_USER_MODEL, không import trực tiếp
from django.conf import settings
from django.db import models
class Post(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
title = models.CharField(max_length=200)

Chạy makemigrations rồi migrate như bình thường. Từ giờ, muốn thêm field mới cho tài khoản thì chỉ cần thêm một dòng vào model rồi migrate — không còn cảnh sửa tay migration hay xóa database nữa.

Áp dụng vào dự án của bạn

Hãy biến việc này thành bước mặc định trong checklist khởi tạo project: tạo app accounts, khai báo AUTH_USER_MODEL và chạy migration đầu tiên trước khi viết bất kỳ model nào khác. Dù hôm nay bạn chưa cần thêm field nào, chi phí của một model custom “rỗng” gần như bằng không, trong khi chi phí sửa sai sau này có thể là cả buổi chiều ngồi gỡ migration. Quy tắc nhớ: model user là nền móng — đổ móng xong rồi mới xây nhà, đừng xây nhà xong mới nhớ ra cần đổi móng.

Bài viết liên quan

Câu hỏi thường gặp

Đã migrate rồi mới muốn đổi User model thì sao?

Rất khó. Django không hỗ trợ đổi AUTH_USER_MODEL sau khi đã có migration tham chiếu tới user cũ — bạn phải sửa tay các migration, hoặc tạo database mới và migrate lại dữ liệu. Nếu project còn ở giai đoạn dev và chưa có dữ liệu quan trọng, xóa database làm lại là cách sạch nhất.

Nên kế thừa AbstractUser hay AbstractBaseUser?

Dùng AbstractUser cho 99% trường hợp: bạn giữ nguyên username, password, nhóm và quyền, chỉ thêm field. Chỉ dùng AbstractBaseUser khi muốn thay hẳn cách đăng nhập — ví dụ đăng nhập bằng email thay vì username — vì khi đó bạn phải tự viết lại manager, field bắt buộc và form admin.

Dùng get_user_model() hay settings.AUTH_USER_MODEL?

Dùng settings.AUTH_USER_MODEL trong ForeignKey và ManyToManyField để tránh circular import lúc Django load model. Dùng get_user_model() trong code chạy — view, form, signal — khi cần truy cập class User thật. Hai cách này không thay thế nhau.

Có cần viết custom UserManager không?

Không, nếu bạn kế thừa AbstractUser và giữ nguyên username làm trường đăng nhập. Chỉ cần custom manager khi đổi USERNAME_FIELD — ví dụ đăng nhập bằng email — lúc đó bạn phải tự định nghĩa cách tạo user và superuser.

💬 Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.

Nguồn tham khảo