0
0
Lập trình
Admin Team
Admin Teamtechmely

Xây dựng REST API với Django REST Framework và MongoDB

Đăng vào 6 tháng trước

• 10 phút đọc

Giới thiệu

REST API (Representational State Transfer API) là một phần thiết yếu trong việc xây dựng các ứng dụng phần mềm và dịch vụ, giúp tối ưu hóa tương tác giữa client và server một cách hiệu quả. REST là một phong cách kiến trúc phần mềm cho phép truy xuất và truyền tải yêu cầu của người dùng từ phía client và thông tin được xử lý từ phía server dưới định dạng JSON hoặc một số định dạng khác.

Nguyên tắc hoạt động của REST API

Các yêu cầu từ client được thực hiện thông qua Giao thức Truyền tải Siêu văn bản (HTTP) với các phương thức HTTP như:

  • GET: Lấy dữ liệu từ server.
  • POST: Gửi và xuất bản thông tin lên server.
  • PUT: Cập nhật thông tin trên server.
  • PATCH: Chỉnh sửa một phần tài nguyên hiện có.
  • DELETE: Xóa thông tin từ server.

REST APIs tuân theo các nguyên tắc:

  • Kiến trúc client-server.
  • Stateless (không duy trì trạng thái).
  • Dữ liệu có thể được cache.
  • Đảm bảo tính đồng nhất giữa các hệ thống.
  • Kiến trúc phân lớp.

REST API được sử dụng phổ biến trong phát triển ứng dụng di động và web vì các ứng dụng di động có thể truy cập chúng ở chế độ nền, trong khi các ứng dụng web có thể hưởng lợi từ nội dung động mà không cần phải tải lại trang. Hơn nữa, các tích hợp bên thứ ba như cổng thanh toán và microservices cũng sử dụng REST APIs để tương tác với các dịch vụ khác trong hệ thống lớn hơn.

Django REST Framework là gì?

Django cung cấp nhiều tùy chọn để xây dựng REST APIs, và một trong số đó là Django REST Framework (DRF) - một bộ công cụ mạnh mẽ và linh hoạt cho việc xây dựng các giao diện lập trình ứng dụng web (API). Gói OAuth của Django REST Framework hỗ trợ cả OAuth1 và OAuth2, hỗ trợ serialization cho cả nguồn dữ liệu ORM và không phải ORM, tài liệu phong phú và nhiều tính năng tùy biến khác.

Trong hướng dẫn này, chúng ta sẽ tìm hiểu cách xây dựng một RESTful API bằng cách sử dụng Django MongoDB Backend chính thức và nhấn mạnh các tính năng có sẵn.

Điều kiện tiên quyết

  • Một cụm MongoDB Atlas với dataset sample_geospatial mặc định - tìm hiểu cách tạo một deployment.
  • Tải xuống Python 3.10 hoặc phiên bản mới hơn.

Cài đặt và cấu hình

Thiết lập môi trường ảo

bash Copy
python -m venv venv
source venv/bin/activate

Cài đặt Django MongoDB Backend và DRF

bash Copy
pip install django-mongodb-backend djangorestframework

Cấu hình kết nối MongoDB

Cấu hình kết nối MongoDB trong file settings.py của dự án Django.

Cấu trúc dự án

Sau khi tạo môi trường ảo, hãy tạo một dự án Django với mẫu dự án Django MongoDB Backend. Mẫu này tương tự như mẫu dự án Django mặc định nhưng bao gồm các migrations đặc thù của MongoDB và sửa đổi file settings.py để cấu hình Django sử dụng ObjectId làm khóa chính cho mỗi mô hình.

Cấu trúc thư mục cuối cùng sẽ như sau:

Copy
myproject/
│
├── shipwrecks_api/
│   ├── migrations/
│   ├── models.py
│   ├── serializers.py
│   ├── views.py
│   └── urls.py
└── manage.py

Tổng quan về mô hình MVC của Django

Kiến trúc dự án Django theo mô hình Model View Controller (MVC) nhưng với một biến thể, nơi bạn có Models, Templates và Views (MTV). Mô hình trong Django bao gồm:

  • Mô hình: Là lớp dữ liệu, định nghĩa cách mà dữ liệu được cấu trúc, kiểm tra tính hợp lệ, và xử lý tương tác với cơ sở dữ liệu MongoDB.
  • View: Mô tả dữ liệu được trình bày cho người dùng, không nhất thiết phải là cách dữ liệu nhìn, mà là dữ liệu nào được trình bày.
  • Template: Là lớp trình bày, bao gồm các file HTML định nghĩa cách mà dữ liệu sẽ xuất hiện với người dùng.

Định nghĩa mô hình

Với Django MongoDB Backend, trường _id của MongoDB được ánh xạ thành trường id truyền thống của Django, nghĩa là bạn sẽ không có cả _idid cùng lúc. Điều này được thực hiện thông qua dòng mã DEFAULT_AUTO_FIELD = 'django_mongodb_backend.fields.ObjectIdAutoField' trong file settings.py.

Mô hình của chúng ta sẽ như sau:

python Copy
# models.py

from django.db import models
from django_mongodb_backend.fields import ArrayField
from django_mongodb_backend.managers import MongoManager

class ShipwreckFeature(models.Model):
    recrd = models.CharField(max_length=200, blank=True)
    vesslterms = models.CharField(max_length=200, blank=True)
    feature_type = models.CharField(max_length=200, blank=True)
    chart = models.CharField(max_length=200, blank=True)
    latdec = models.FloatField(null=True, blank=True)
    londec = models.FloatField(null=True, blank=True)
    gp_quality = models.CharField(max_length=200, blank=True)
    depth = models.CharField(max_length=200, blank=True)
    sounding_type = models.CharField(max_length=200, blank=True)
    history = models.TextField(blank=True)
    quasou = models.CharField(max_length=200, blank=True)
    watlev = models.CharField(max_length=200, blank=True)
    coordinates = ArrayField(
        base_field=models.FloatField(), null=True, blank=True
    )
    objects = MongoManager()

    class Meta:
        db_table = "shipwrecks_api" 
        managed = False

    def __str__(self):
        return f"{self.feature_type} tại ({self.latdec}, {self.londec})"

Serializers

Giới thiệu về serializers trong DRF

Serializers cho phép dữ liệu phức tạp, như queryset và model instances, được chuyển đổi thành các kiểu dữ liệu Python bản địa có thể dễ dàng được chuyển đổi thành JSON, XML hoặc các loại nội dung khác. Serializers cũng cung cấp khả năng deserialization, cho phép dữ liệu đã phân tích được chuyển đổi lại thành các loại phức tạp, sau khi đã xác thực dữ liệu đầu vào.

Trong lớp serializer dưới đây, chúng ta sử dụng hai trường serializer:

  • SerializerMethodField: Là trường chỉ đọc, giá trị của nó được lấy từ một phương thức trên lớp serializer mà nó được gắn vào.
  • HyperlinkedIdentityField: Có thể được áp dụng như một mối quan hệ danh tính, chẳng hạn như trường url trong HyperlinkedModelSerializer.

Tạo serializers

python Copy
# serializers.py

from rest_framework import serializers
from .models import ShipwreckFeature

class ShipwreckFeatureSerializer(serializers.ModelSerializer):
    id = serializers.SerializerMethodField(read_only=True)
    url = serializers.HyperlinkedIdentityField(
        view_name='shipwreck-detail'
    )

    def get_id(self, obj):
        return str(obj.pk) if obj.pk else None

    class Meta:
        model = ShipwreckFeature
        fields = [
           'id', 'url', 'recrd', 'vesslterms', 'feature_type',
            'chart', 'latdec', 'londec',
            'gp_quality', 'depth', 'sounding_type',
            'history', 'quasou', 'watlev',
            'coordinates'
        ]
        read_only_fields = ['id']

Views và cấu hình URL

Hãy tạo các view dựa trên hàm để hiển thị dữ liệu về các vụ đắm tàu.

python Copy
# views.py

from rest_framework import viewsets, filters, permissions
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from bson import ObjectId
from bson.errors import InvalidId
from .models import ShipwreckFeature
from .serializers import ShipwreckFeatureSerializer
from django_filters.rest_framework import DjangoFilterBackend

class ShipwreckFeatureViewSet(viewsets.ModelViewSet):
    queryset = ShipwreckFeature.objects.all()
    serializer_class = ShipwreckFeatureSerializer
    permission_classes = [permissions.AllowAny]
    filter_backends = [DjangoFilterBackend, filters.OrderingFilter]
    lookup_field = 'pk'
    filterset_fields = ['recrd', 'vesslterms', 'feature_type', 'latdec', 'londec']
    ordering_fields = ['latdec', 'londec']
    ordering = ['latdec']

    def get_object(self):
        lookup_value = self.kwargs[self.lookup_field]
        try:
            if not ObjectId.is_valid(lookup_value):
                raise InvalidId("Định dạng ObjectId không hợp lệ")
            object_id = ObjectId(lookup_value)
        except (InvalidId, ValueError):
            from django.http import Http404
            raise Http404("Định dạng ObjectId không hợp lệ")
        queryset = self.filter_queryset(self.get_queryset())
        obj = get_object_or_404(queryset, pk=object_id)
        self.check_object_permissions(self.request, obj)
        return obj

    def destroy(self, request, *args, **kwargs):
        try:
            instance = self.get_object()
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
        except Exception as e:
            return Response({'error': f'Không thể xóa vụ đắm tàu: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

Cấu hình URL để ánh xạ các view trong shipwrecks_api

python Copy
# urls.py - shipwrecks_api

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ShipwreckFeatureViewSet

router = DefaultRouter()
router.register(r'shipwrecks', ShipwreckFeatureViewSet, basename='shipwreck')

urlpatterns = [
    path('', include(router.urls)),
]

Sử dụng shell Python để tạo đối tượng

python Copy
sw1 = ShipwreckFeature.objects.create(
    recrd="R001",
    vesslterms="RMS Titanic",
    feature_type="Wrecks - Submerged, not dangerous",
    chart="US,U1,graph,DNC H140984",
    latdec=10.123456,
    londec=-80.654321,
    gp_quality="A",
    depth="3840 meters",
    sounding_type="approximate",
    history="RMS Titanic chìm vào ngày 15 tháng 4 năm 1912, sau khi va phải một tảng băng.",
    quasou="depth known",
    watlev="always under water/submerged",
    coordinates=[-80.654321, 10.123456]
)

Các thao tác CRUD

Các phương thức CRUD — CREATE, GET/RETRIEVE, UPDATE và DELETE — có thể được thực hiện thông qua API web DRF, Postman và shell Python. Ví dụ, để GET tất cả các vụ đắm tàu, bạn có thể truy cập vào http://127.0.0.1:8000/shipwrecks/.

Phân trang

Phân trang cho phép bạn điều chỉnh cách mà các tập kết quả lớn được chia thành các trang dữ liệu riêng biệt nhằm cải thiện khả năng đọc và hiệu suất. Để sử dụng lớp phân trang mặc định, thêm các dòng mã sau vào settings.py:

python Copy
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20
}

Kiểm thử

python Copy
from django.test import TestCase
from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse
from .models import ShipwreckFeature

class ShipwreckFeatureModelTest(TestCase):

    def setUp(self):
        self.shipwreck = ShipwreckFeature.objects.create(
            recrd="R001",
            vesslterms="test_vessel",
            feature_type="wreck",
            chart="test_chart",
            latdec=40.7128,
            londec=-74.0060,
            history="Test shipwreck for testing",
            coordinates=[-74.0060, 40.7128]
        )

    def test_shipwreck_creation(self):
        self.assertEqual(self.shipwreck.recrd, "R001")

class ShipwreckFeatureAPITest(APITestCase):
    def setUp(self):
        self.shipwreck = ShipwreckFeature.objects.create(
            recrd="R002",
            vesslterms="api_test_vessel",
            feature_type="wreck",
            latdec=41.0,
            londec=-75.0,
            history="API test shipwreck"
        )
        self.list_url = reverse('shipwreck-list')

    def test_get_shipwreck_list(self):
        response = self.client.get(self.list_url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Tài liệu

Sử dụng các công cụ như Swagger UI, Postman, OpenAPI Generator để tài liệu hóa các điểm cuối API. Để cài đặt Swagger với Django:

bash Copy
pip install drf-spectacular

Thêm 'drf_spectacular' vào INSTALLED_APPS trong settings.py.

Kết luận

Trong hướng dẫn này, chúng ta đã tìm hiểu về kiến trúc REST API, phương thức HTTP, Django REST Framework, cách cài đặt và cấu hình dự án API, định nghĩa mô hình, URL, views, kiểm thử và tài liệu hóa API bằng Swagger.

Bạn đã có những kiến thức cần thiết để tạo một REST API với Django REST Framework và Django MongoDB Backend. Hãy thử nghiệm và để lại phản hồi về trải nghiệm của bạn nhé!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào