Tạo các models trong Django và thao tác CRUD hiệu quả

Models là thành phần cốt lõi, được xem như xương sống giúp lưu trữ và quản lý toàn bộ dữ liệu trong một ứng dụng Django. Tuy nhiên, nhiều người mới bắt đầu thường cảm thấy bối rối khi phải tìm hiểu và xây dựng models một cách chính xác. Việc định nghĩa sai cấu trúc models có thể dẫn đến những lỗi khó lường và ảnh hưởng đến toàn bộ dự án sau này. Hiểu được điều đó, bài viết này được tạo ra để trở thành một cuốn cẩm nang toàn diện, giúp bạn nắm vững mọi thứ về models. Chúng ta sẽ cùng nhau khám phá từ khái niệm cơ bản, cách tạo và khai báo các trường dữ liệu, cho đến việc thực hiện các thao tác CRUD một cách thành thạo.

Khái quát về models trong Django và vai trò của chúng

Trước khi đi vào chi tiết kỹ thuật, chúng ta cần hiểu rõ bản chất và tầm quan trọng của models trong hệ sinh thái Django. Đây là nền tảng giúp ứng dụng của bạn tương tác với cơ sở dữ liệu một cách trực quan và hiệu quả.

Models là gì trong Django?

Trong Django, một model là một lớp Python (class) đại diện cho một bảng trong cơ sở dữ liệu của bạn. Mỗi thuộc tính (attribute) của lớp model sẽ tương ứng với một cột trong bảng đó. Ví dụ, nếu bạn có một ứng dụng blog, bạn có thể tạo một model tên là Post để lưu trữ các bài viết. Model này sẽ có các thuộc tính như title (tiêu đề), content (nội dung), và publication_date (ngày xuất bản), tương ứng với các cột title, content, và publication_date trong bảng Post của cơ sở dữ liệu.

Điểm đặc biệt của Django là cơ chế ORM (Object-Relational Mapping). ORM là một cây cầu nối liền thế giới lập trình hướng đối tượng (Python code) và thế giới cơ sở dữ liệu quan hệ (SQL tables). Nhờ có ORM, bạn không cần phải viết các câu lệnh SQL phức tạp. Thay vào đó, bạn chỉ cần thao tác với các đối tượng Python, và Django sẽ tự động chuyển đổi chúng thành các truy vấn SQL tương ứng. Models chính là trung tâm của cơ chế ORM này.

Vai trò quan trọng của models trong phát triển Django

Models không chỉ đơn thuần là nơi định nghĩa cấu trúc dữ liệu, mà còn đóng nhiều vai trò quan trọng giúp quá trình phát triển trở nên nhanh chóng và bảo mật hơn.

Đầu tiên, models giúp quản lý dữ liệu một cách linh hoạt theo hướng đối tượng. Thay vì nghĩ về các bảng và các hàng, bạn có thể tư duy theo các đối tượng và thuộc tính của chúng. Điều này giúp mã nguồn của bạn trở nên trong sáng, dễ đọc và dễ bảo trì hơn, đặc biệt khi dự án phát triển lớn mạnh.

Thứ hai, Django models tự động hóa việc tạo và quản lý cấu trúc cơ sở dữ liệu. Khi bạn định nghĩa hoặc thay đổi một model, bạn chỉ cần chạy hai câu lệnh đơn giản (makemigrationsmigrate), Django sẽ tự động tạo ra các file migration và cập nhật schema của database. Bạn không cần phải lo lắng về việc viết các lệnh CREATE TABLE hay ALTER TABLE phức tạp và dễ sai sót.

Cuối cùng, models cung cấp một API mạnh mẽ để thực hiện các thao tác CRUD (Create, Read, Update, Delete) một cách dễ dàng. Bạn có thể thêm, truy vấn, cập nhật và xóa dữ liệu chỉ bằng vài dòng code Python, giúp giảm thiểu đáng kể thời gian phát triển và tăng cường tính bảo mật bằng cách ngăn chặn các cuộc tấn công SQL injection.

Hình minh họa

Cách tạo models trong Django bằng Python

Bây giờ chúng ta sẽ bắt tay vào phần thực hành. Việc tạo models trong Django hoàn toàn được thực hiện bằng code Python, giúp bạn quản lý mọi thứ trong cùng một môi trường làm việc.

Tạo file models.py và khai báo class model

Trong mỗi “app” của Django, có một tệp tin mặc định tên là models.py. Đây chính là nơi bạn sẽ định nghĩa tất cả các model liên quan đến app đó. Để tạo một model, bạn chỉ cần khai báo một lớp Python mới và cho nó kế thừa từ lớp django.db.models.Model.

Hãy xem một ví dụ đơn giản. Giả sử chúng ta đang xây dựng một app blog và cần một model để lưu trữ thông tin về tác giả. Chúng ta sẽ mở tệp blog/models.py và thêm đoạn mã sau:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.name

Trong ví dụ trên, chúng ta đã tạo một model tên là Author. Lớp này kế thừa từ models.Model, điều này cho Django biết rằng đây là một model cần được ánh xạ vào cơ sở dữ liệu. Phương thức __str__ là một phương thức đặc biệt trong Python, giúp hiển thị tên của đối tượng một cách thân thiện hơn khi chúng ta xem nó trong Django Admin hoặc trên terminal.

Định nghĩa các trường dữ liệu (Fields) trong models

Sau khi khai báo lớp model, bước tiếp theo là định nghĩa các trường dữ liệu (fields). Mỗi trường là một thuộc tính của lớp, đại diện cho một cột trong bảng dữ liệu. Django cung cấp rất nhiều loại trường khác nhau để phù hợp với các kiểu dữ liệu đa dạng.

Dưới đây là một số loại trường phổ biến nhất:

  • CharField: Dùng để lưu trữ các chuỗi văn bản ngắn, như tiêu đề, tên, v.v. Bắt buộc phải có tham số max_length.
  • TextField: Dùng cho các đoạn văn bản dài không giới hạn, như nội dung bài viết.
  • IntegerField: Dùng để lưu các số nguyên.
  • FloatField: Dùng để lưu các số thực.
  • BooleanField: Lưu giá trị đúng/sai (True/False).
  • DateTimeField: Lưu trữ thông tin ngày và giờ. Có thể tự động ghi lại thời điểm tạo (auto_now_add=True) hoặc cập nhật (auto_now=True).
  • DateField: Chỉ lưu trữ thông tin ngày.
  • EmailField: Tương tự CharField nhưng có sẵn validation để kiểm tra định dạng email.
  • ForeignKey: Dùng để tạo mối quan hệ nhiều-một (many-to-one) với một model khác. Ví dụ, một bài viết có một tác giả.

Mỗi trường còn có thể nhận các tham số bổ sung để tùy chỉnh hành vi:

  • max_length: Độ dài tối đa của chuỗi (bắt buộc cho CharField).
  • default: Giá trị mặc định cho trường nếu không có giá trị nào được cung cấp.
  • null=True: Cho phép cột trong database nhận giá trị NULL.
  • blank=True: Cho phép trường này được để trống trong các form (ví dụ: Django Admin).
  • unique=True: Đảm bảo giá trị trong cột này là duy nhất trên toàn bộ bảng.
  • verbose_name: Tên hiển thị thân thiện cho trường, thường dùng trong Django Admin.

Hãy mở rộng model Post của chúng ta với nhiều trường hơn:

from django.db import models
from django.utils import timezone

# Giả sử model Author đã được định nghĩa ở trên
# from .models import Author

class Post(models.Model):
    title = models.CharField(max_length=200, verbose_name="Tiêu đề")
    content = models.TextField(verbose_name="Nội dung")
    published_date = models.DateTimeField(default=timezone.now, verbose_name="Ngày xuất bản")
    author = models.ForeignKey(Author, on_delete=models.CASCADE, verbose_name="Tác giả")
    is_published = models.BooleanField(default=False, verbose_name="Đã xuất bản?")

    def __str__(self):
        return self.title

Trong ví dụ này, Post có tiêu đề, nội dung, ngày xuất bản, trạng thái và một mối quan hệ ForeignKey với model Author. Tham số on_delete=models.CASCADE có nghĩa là nếu một tác giả bị xóa, tất cả các bài viết của tác giả đó cũng sẽ bị xóa theo.

Hình minh họa

Thực hiện các thao tác CRUD thông qua models

Sau khi đã định nghĩa models, sức mạnh thực sự của Django ORM được thể hiện qua việc bạn có thể dễ dàng tương tác với cơ sở dữ liệu mà không cần viết một dòng SQL nào. Các thao tác này được gọi chung là CRUD: Create (Tạo), Read (Đọc), Update (Cập nhật), và Delete (Xóa).

Tạo và lưu dữ liệu mới (Create)

Để tạo một bản ghi mới trong cơ sở dữ liệu, bạn chỉ cần khởi tạo một đối tượng từ lớp model của mình và gọi phương thức .save(). Django sẽ tự động chuyển đổi đối tượng Python này thành một câu lệnh INSERT trong SQL.

Giả sử chúng ta đã có một tác giả trong database. Bây giờ, chúng ta muốn tạo một bài viết mới cho tác giả đó:

# Đầu tiên, lấy đối tượng tác giả
from .models import Author, Post
author_duc = Author.objects.get(name="Bùi Mạnh Đức")

# Cách 1: Tạo đối tượng rồi lưu
new_post = Post(
    title="Bài viết đầu tiên của tôi",
    content="Đây là nội dung chi tiết của bài viết.",
    author=author_duc
)
new_post.save() # Lưu vào cơ sở dữ liệu

# Cách 2: Sử dụng phương thức .create()
another_post = Post.objects.create(
    title="Bài viết thứ hai",
    content="Nội dung khác cho bài viết này.",
    author=author_duc
)

print(f"Đã tạo thành công bài viết: {new_post.title}")
print(f"Đã tạo thành công bài viết: {another_post.title}")

Cả hai cách trên đều có kết quả tương tự: một hàng mới được thêm vào bảng Post trong cơ sở dữ liệu. Phương thức create() là một cách viết tắt tiện lợi, giúp bạn tạo và lưu đối tượng chỉ trong một bước.

Hình minh họa

Đọc, cập nhật và xóa dữ liệu (Read, Update, Delete)

Việc truy vấn, chỉnh sửa và xóa dữ liệu cũng được thực hiện một cách rất trực quan thông qua các phương thức được cung cấp bởi “manager” của model (chính là Model.objects).

Đọc (Read)

Django cung cấp một API truy vấn mạnh mẽ để lấy dữ liệu bạn cần.

  • Để lấy tất cả các đối tượng của một model:
    all_posts = Post.objects.all()
    for post in all_posts:
        print(post.title)
    
  • Để lấy một đối tượng duy nhất dựa trên một điều kiện (ví dụ: ID hoặc một trường unique):
    post_by_id = Post.objects.get(pk=1) # pk là viết tắt của primary key (khóa chính)
    print(post_by_id.title)
    

    Lưu ý: get() sẽ gây ra lỗi DoesNotExist nếu không tìm thấy đối tượng nào, hoặc MultipleObjectsReturned nếu tìm thấy nhiều hơn một.

  • Để lọc các đối tượng dựa trên một hoặc nhiều điều kiện:
    published_posts = Post.objects.filter(is_published=True)
    duc_posts = Post.objects.filter(author__name="Bùi Mạnh Đức", is_published=True)
    

    filter() luôn trả về một QuerySet, ngay cả khi không có kết quả nào (QuerySet rỗng).

Cập nhật (Update)

Để cập nhật một bản ghi, bạn chỉ cần lấy đối tượng đó ra, thay đổi giá trị thuộc tính của nó, và gọi lại phương thức .save().

post_to_update = Post.objects.get(pk=1)
post_to_update.title = "Tiêu đề mới đã được cập nhật"
post_to_update.content = "Nội dung cũng đã được làm mới."
post_to_update.save() # Lưu lại các thay đổi vào DB

Xóa (Delete)

Việc xóa một bản ghi cũng đơn giản không kém. Bạn lấy đối tượng cần xóa và gọi phương thức .delete().

post_to_delete = Post.objects.get(pk=2)
post_to_delete.delete()
print("Bài viết đã được xóa thành công.")

Hình minh họa

Quản lý và sử dụng models hiệu quả trong dự án Django

Việc tạo models chỉ là bước khởi đầu. Để duy trì một dự án Django lớn mạnh và dễ bảo trì, bạn cần biết cách quản lý và sử dụng chúng một cách hiệu quả.

Sử dụng migration để đồng bộ database

Mỗi khi bạn tạo một model mới hoặc thay đổi một model hiện có (thêm/xóa/sửa trường), bạn cần thông báo cho Django biết để nó cập nhật cấu trúc cơ sở dữ liệu. Quá trình này được gọi là “migration”.

  1. Tạo file migration: Sau khi thay đổi tệp models.py, bạn chạy lệnh sau trong terminal:
    python manage.py makemigrations
    

    Django sẽ phân tích các thay đổi của bạn và tạo ra một tệp tin Python trong thư mục migrations của app. Tệp này chứa các chỉ dẫn để cập nhật cơ sở dữ liệu.

  2. Áp dụng migration vào database: Sau khi đã có tệp migration, bạn chạy lệnh sau để thực thi nó:
    python manage.py migrate
    

    Django sẽ đọc tệp migration và áp dụng các thay đổi vào cơ sở dữ liệu (ví dụ: tạo bảng mới, thêm cột mới). Luôn nhớ rằng các tệp migration nên được đưa vào hệ thống quản lý phiên bản (như Git) để các thành viên khác trong nhóm có thể đồng bộ cơ sở dữ liệu của họ.

Hình minh họa

Tổ chức models khi dự án lớn

Khi dự án của bạn phát triển, số lượng models có thể tăng lên nhanh chóng. Việc tổ chức chúng một cách khoa học là rất quan trọng.

  • Phân chia models theo app hoặc module: Nguyên tắc cơ bản của Django là chia dự án thành các “app” nhỏ, mỗi app đảm nhiệm một chức năng cụ thể. Hãy đặt các models vào đúng app của nó. Ví dụ, User, Profile nên ở trong app accounts, trong khi Post, Category nên ở trong app blog.
  • Xây dựng relationships và model inheritance: Tận dụng các mối quan hệ như ForeignKey, ManyToManyField, và OneToOneField để liên kết các models với nhau một cách logic. Ngoài ra, bạn có thể sử dụng kế thừa model (model inheritance) để tái sử dụng các trường chung. Ví dụ, bạn có thể tạo một model cơ sở TimestampedModel chứa các trường created_atupdated_at, sau đó cho các model khác kế thừa từ nó.

Sử dụng Django Admin để quản lý dữ liệu từ models

Một trong những tính năng mạnh mẽ nhất của Django là trang quản trị (Admin) được tạo tự động. Nó cho phép bạn thực hiện các thao tác CRUD trên dữ liệu của mình thông qua một giao diện web thân thiện mà không cần viết thêm bất kỳ code nào cho giao diện.

Để hiển thị một model trên trang Admin, bạn chỉ cần đăng ký nó trong tệp admin.py của app tương ứng:

from django.contrib import admin
from .models import Author, Post

# Đăng ký model Author
admin.site.register(Author)

# Đăng ký model Post
admin.site.register(Post)

Sau khi đăng ký, bạn có thể truy cập trang Admin, đăng nhập và bắt đầu quản lý dữ liệu của mình một cách dễ dàng. Django Admin là một công cụ tuyệt vời để quản lý nội dung, kiểm tra dữ liệu và thực hiện các tác vụ quản trị nhanh chóng.

Hình minh họa

Các vấn đề thường gặp khi tạo và sử dụng models

Trong quá trình làm việc với models, bạn có thể sẽ gặp phải một số lỗi phổ biến. Biết cách nhận diện và xử lý chúng sẽ giúp bạn tiết kiệm rất nhiều thời gian.

Lỗi khi migration do sai khai báo trường dữ liệu

Đây là một trong những lỗi phổ biến nhất, đặc biệt khi bạn thay đổi một trường đã có dữ liệu.

  • Vấn đề: Bạn thay đổi một trường CharField thành IntegerField hoặc thêm một trường mới mà không cung cấp giá trị mặc định (default). Khi chạy migrate, Django sẽ báo lỗi vì nó không biết phải làm gì với các hàng dữ liệu hiện có.
  • Cách kiểm tra và sửa lỗi:
    1. Đọc kỹ thông báo lỗi trên terminal. Django thường sẽ gợi ý cho bạn giải pháp.
    2. Nếu bạn thêm một trường mới vào model đã có dữ liệu, hãy cung cấp cho nó một giá trị default.
    3. Hoặc, bạn có thể cho phép trường đó nhận giá trị null bằng cách thêm null=True. Tuy nhiên, hãy cẩn thận với lựa chọn này vì nó có thể ảnh hưởng đến logic của ứng dụng.
    4. Trong trường hợp phức tạp hơn, bạn có thể cần tạo một “data migration” để viết code Python tùy chỉnh, giúp Django chuyển đổi dữ liệu cũ sang định dạng mới.

Hình minh họa

Vấn đề dữ liệu trùng lắp hoặc không hợp lệ

Đảm bảo tính toàn vẹn của dữ liệu là vô cùng quan trọng. Models trong Django cung cấp các công cụ để bạn thực hiện điều này.

  • Vấn đề: Người dùng có thể nhập hai email giống hệt nhau cho hai tài khoản khác nhau, hoặc nhập một giá trị không nằm trong phạm vi cho phép.
  • Giải pháp – Áp dụng ràng buộc và validation:
    1. Sử dụng ràng buộc ở cấp độ database: Thêm unique=True vào trường mà bạn muốn giá trị của nó là duy nhất (ví dụ: email trong model User). Cơ sở dữ liệu sẽ tự động từ chối bất kỳ nỗ lực nào để chèn một giá trị trùng lặp.
    2. Sử dụng validators: Django cho phép bạn viết các hàm hoặc lớp validator để kiểm tra dữ liệu trước khi nó được lưu. Bạn có thể gắn validator vào một trường cụ thể trong model. Ví dụ, bạn có thể viết một validator để đảm bảo rằng một trường số nguyên phải là số dương.
      from django.core.exceptions import ValidationError
      from django.db import models
      
      def validate_positive_number(value):
          if value <= 0:
              raise ValidationError(f'{value} is not a positive number!')
      
      class Product(models.Model):
          price = models.IntegerField(validators=[validate_positive_number])
      

      Bằng cách này, bạn có thể đảm bảo dữ liệu luôn hợp lệ ngay từ tầng model.

Hình minh họa

Best Practices khi làm việc với models trong Django

Để viết code chuyên nghiệp và dễ bảo trì, bạn nên tuân theo một số quy tắc và thực hành tốt nhất khi làm việc với models.

  • Luôn định nghĩa verbose_namehelp_text cho trường dữ liệu:
    verbose_name cung cấp một tên hiển thị thân thiện cho trường trong trang Admin. help_text cung cấp một đoạn văn bản mô tả ngắn gọn, giúp người quản trị hiểu rõ hơn về ý nghĩa của trường đó. Điều này làm cho giao diện Admin trở nên chuyên nghiệp và dễ sử dụng hơn rất nhiều.

    title = models.CharField(
        max_length=200,
        verbose_name="Tiêu đề bài viết",
        help_text="Nhập tiêu đề không quá 200 ký tự."
    )
    
  • Sử dụng model methods để định nghĩa logic liên quan đến dữ liệu: Nếu bạn có một logic nào đó liên quan trực tiếp đến một đối tượng model, hãy viết nó thành một phương thức ngay trong lớp model đó. Ví dụ, thay vì tính toán số ngày kể từ khi bài viết được xuất bản ở trong view, bạn có thể tạo một phương thức trong model Post.
    from django.utils import timezone
    
    class Post(models.Model):
        # ... các trường khác
        published_date = models.DateTimeField()
    
        def days_since_published(self):
            diff = timezone.now() - self.published_date
            return diff.days
    
  • Tránh đặt logic xử lý dữ liệu trong views; hãy tận dụng model managers: Views nên được giữ gọn nhẹ, chỉ đảm nhiệm việc xử lý request và response. Mọi logic truy vấn dữ liệu phức tạp nên được chuyển vào model managers. Điều này giúp tái sử dụng code và giữ cho code của bạn có tổ chức. Ví dụ, thay vì viết Post.objects.filter(is_published=True) ở nhiều nơi, bạn có thể tạo một custom manager.
    class PublishedManager(models.Manager):
        def get_queryset(self):
            return super().get_queryset().filter(is_published=True)
    
    class Post(models.Model):
        # ... các trường
        objects = models.Manager() # Manager mặc định
        published = PublishedManager() # Manager tùy chỉnh
    
    # Trong view, bạn chỉ cần gọi:
    # published_posts = Post.published.all()
    
  • Không hardcode giá trị, sử dụng choices hoặc constants: Nếu một trường chỉ có thể nhận một vài giá trị cố định (ví dụ: trạng thái bài viết là 'Draft', 'Published', 'Archived'), đừng hardcode các chuỗi này. Hãy sử dụng tùy chọn choices của trường.
    class Post(models.Model):
        STATUS_DRAFT = 'draft'
        STATUS_PUBLISHED = 'published'
        STATUS_CHOICES = [
            (STATUS_DRAFT, 'Bản nháp'),
            (STATUS_PUBLISHED, 'Đã xuất bản'),
        ]
        status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=STATUS_DRAFT)
    
  • Thường xuyên tạo và áp dụng migration đúng lúc: Hãy tạo thói quen chạy makemigrationsmigrate ngay sau mỗi lần bạn thay đổi model. Đừng để dồn quá nhiều thay đổi vào một lần migration, vì nó sẽ khiến việc gỡ lỗi trở nên khó khăn hơn nếu có sự cố xảy ra.

Hình minh họa

Kết luận

Qua bài viết này, chúng ta đã cùng nhau đi qua một hành trình chi tiết về models trong Django. Từ việc hiểu rõ vai trò của chúng như một cầu nối giữa code Python và cơ sở dữ liệu, đến cách tạo ra các model, định nghĩa trường, thực hiện các thao tác CRUD, và quản lý chúng một cách hiệu quả. Models không chỉ là nơi định nghĩa dữ liệu, mà còn là trái tim của logic nghiệp vụ, giúp cho ứng dụng của bạn trở nên có cấu trúc, ổn định và dễ dàng bảo trì.

Việc thực hành và tuân thủ các best practices khi làm việc với models sẽ đặt một nền móng vững chắc cho bất kỳ dự án Django nào, dù lớn hay nhỏ. Hãy bắt đầu áp dụng những kiến thức này vào dự án của bạn ngay hôm nay. Bước tiếp theo cho hành trình của bạn là tìm hiểu sâu hơn về các mối quan hệ phức tạp giữa các model (ManyToManyField, GenericForeignKey) và khám phá sức mạnh của Django Signals để thực thi các hành động tự động khi dữ liệu thay đổi. Chúc bạn thành công trên con đường chinh phục Django!

Hình minh họa

Đánh giá
Tác giả

Mạnh Đức

Có cao nhân từng nói rằng: "Kiến thức trên thế giới này đầy rẫy trên internet. Tôi chỉ là người lao công cần mẫn đem nó tới cho người cần mà thôi !"

Chia sẻ
Bài viết liên quan