Sử dụng Django API dễ dàng với mẫu sẵn có

Bạn có thể dễ dàng sử dụng các API đơn giản mà không cần cấu hình một frontend bên ngoài. Dưới đây là cách dùng các mẫu Django cho lập trình API.

Django API

Khi dùng công nghệ backend hay framework như Django, Laravel hoặc Node.js để viết REST API, bạn cần có thêm kỹ năng frontend bằng cách dùng những framework như React, Angular, Vue để dùng các API endpoint. Thế nhưng không phải trường hợp nào cũng như vậy, bạn có thể dùng API trong Django bằng các mẫu sẵn có.

Thiết lập dự án Django và API Endpoint

Bước đầu tiên là tạo thư mục dự án. Mở terminal của bạn và tạo một thư mục cho dự án.

mkdir payment_wallet_project
cd payment_wallet_project

Ở hướng dẫn này, bạn sẽ xây dựng API cho ví thanh toán.

Bắt đầu bằng cách tạo một môi trường ảo. Trong trường hợp này, bạn sẽ dùng thư viện Pipenv.

pipenv install django djangorestframework

Lệnh này cài đặt các thư viện cần thiết cũng như tạo một môi trường ảo. Kích hoạt môi trường ảo bằng lệnh bên dưới:

pipenv shell

Tạo dự án Django mới tên PayApp.

pipenv shell

Dùng dấu chấm (.) ở cuối lệnh django-admin đảm bảo dự án này tránh tạo một thư mục trùng lặp trong danh mục dự án.

Tạo app Django mới trong thư mục dự án.

python manage.py startapp wallet

Giờ, tiếp tục xây dựng app API bằng những bước bên dưới.

Tạo một ví thanh toán bằng REST API

Mở file wallet/models.py và xác định ví và các mẫu giao dịch.

from django.db import models

class Wallet(models.Model):
   user = models.CharField(max_length=100)
   balance = models.DecimalField(max_digits=10, decimal_places=2)
   date_created = models.DateTimeField(auto_now_add=True)
   date_modified = models.DateTimeField(auto_now=True)

   def __str__(self):
       return self.user

class Transaction(models.Model):
   wallet = models.ForeignKey(Wallet, on_delete=models.CASCADE)
   amount = models.DecimalField(max_digits=10, decimal_places=2)
   timestamp = models.DateTimeField(auto_now_add=True)

Trong thư mục wallet, tạo file mới serializers.py, lập trình ví và các serializer mô hình giao dịch.

from rest_framework import serializers
from .models import Wallet, Transaction

class WalletSerializer(serializers.ModelSerializer):
   class Meta:
       model = Wallet
       fields = '__all__'
class TransactionSerializer(serializers.ModelSerializer): 
   class Meta:
       model = Transaction 
       fields = '__all__'

Serializer này xem xét tất cả các trường trong ví và mô hình giao dịch.

Trong wallet/views.py, viết các trình xem để xử lý logic triển khai chức năng ví, bao gồm gửi và rút tiền.

from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.decorators import action
from decimal import Decimal
from .models import Wallet, Transaction
from .serializers import WalletSerializer, TransactionSerializer

class WalletViewSet(viewsets.ModelViewSet):
   queryset = Wallet.objects.all()
   serializer_class = WalletSerializer

 @action(detail=True, methods=['post'])
   def deposit(self, request, pk=None):
       wallet = self.get_object()
       amount = Decimal(request.data['amount'])
       wallet.balance += amount
       wallet.save() 
       serializer = WalletSerializer(wallet)
       return Response(serializer.data)
      

 @action(detail=True, methods=['post'])
   def withdraw(self, request, pk=None):
       wallet = self.get_object()
       amount = Decimal(request.data['amount'])
       if wallet.balance < amount:
           return Response({'error': 'Insufficient funds'},
                           status=status.HTTP_400_BAD_REQUEST)
       wallet.balance -= amount
       wallet.save()
       serializer = WalletSerializer(wallet)
       return Response(serializer.data)'

class TransactionViewSet(viewsets.ModelViewSet):
   queryset = Transaction.objects.all()
   Serializer_class = TransactionSerializer

Tiếp theo, xác định tuyến URL cho API bằng cách tạo file wallet/urls.py:

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import WalletViewSet, TransactionViewSet, wallet_view

router = DefaultRouter()
router.register(r'wallets', WalletViewSet, basename='wallets')
router.register(r'transactions', TransactionViewSet, basename='transactions')

urlpatterns = [
   path('api/', include(router.urls)),
   path('wallets/<int:pk>/deposit/', WalletViewSet.as_view({'post': 'deposit'}),
        name='wallet-deposit'),
   path('wallets/<int:pk>/withdraw/', WalletViewSet.as_view({'post': 'withdraw'}),
        name='wallet-withdraw'),

]

Trong urls.py của dự án, bao gồm URL của ứng dụng:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
   path('admin/', admin.site.urls),
   path('', include('wallet.urls')),
]

Trong file PayApp/settings.py, thêm walletrest_framwork apps vào danh sách INSTALLED_APPS.

INSTALLED_APPS = [

"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",

"rest_framework", # new
"wallet", # new

]

Điều này sẽ đăng ký app wallet và rest_framework cho ứng dụng dự án Django.

Dùng API với mẫu Django

Giờ, bạn sẽ dùng các mẫu Django để tạo một frontend đơn giản cho việc sử dụng API. Tạo file wallet.html trong thư mục wallet/templates/ và thêm code HTML bên dưới:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title>Wallet</title>
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/
   css/bootstrap.min.css">
</head>
<body>
   <div class="container">
       <h1>Wallets</h1>
       <table class="table">
           <thead>
               <tr>
                   <th>User</th>
                   <th>Balance</th>
                   <th>Actions</th>
               </tr>
           </thead>
           <tbody>
               <tr>
                   <td>{{ wallet.user }}</td>
                   <td id="balance">{{ wallet.balance }}</td>
                   <td>
                       <div id="loading-indicator" class="d-none">
                           <div class="spinner-border text-primary" role="status">
                               <span class="sr-only">Loading...</span>
                           </div>
                           <p>Please wait while the deposit is being processed.</p>
                       </div>
                       <form id="deposit-form" method="post">
                           {% csrf_token %}
                           <input type="number" name="amount" step="0.01" min="0" required>
                           <button type="submit" class="btn btn-success">Deposit</button>
                       </form>
                       <form method="post" id="withdraw-form">
                           {% csrf_token %}
                           <input type="number" name="amount" step="0.01" min="0" required>
                           <button type="submit" class="btn btn-danger">Withdraw</button>
                       </form>
                   </td>
               </tr>
           </tbody>
       </table>
   </div>

</body>
</html>

File HTML này hiện API chuyển và rút tiền trong giao diện người dùng đẹp mắt được thiết kế bởi Bootstrap.

Tương tác người dùng với biểu mẫu

Trong file HTML, tạo tag script và thêm code sau vào trình xử lý sự kiện gửi biểu mẫu chuyển tiền.

<script>
document.querySelector('#deposit-form').addEventListener('submit', function (event) {
           event.preventDefault();
           document.querySelector('#loading-indicator').classList.remove('d-none');
           const amount = parseFloat(document.querySelector("#deposit-form " +
               "input[name='amount']").value);
           fetch("{% url 'wallet-deposit' wallet.id %}", {
               method: "POST",
               headers: {
                   "Content-Type": "application/json",
                   "X-CSRFToken": getCookie("csrftoken")
               },
               body: JSON.stringify({ amount: amount })
           })
               .then(response => response.json())
               .then(data => {
                   console.log(data);
                   if (data.balance !== undefined) {
                       // Chuyển đổi số và định dạng
                       const newBalance = parseFloat(data.balance).toFixed(2); 
                       document.querySelector("#balance").textContent = newBalance;
                       document.querySelector('#loading-indicator').classList.
                       add('d-none');
                   }
               })
               .catch(error => {
                   console.error("Error:", error);
                   document.querySelector('#loading-indicator')
                       .classList.add('d-none');
               });
       });
</script>

Tiếp theo, thêm event listener cho việc gửi biểu mẫu rút tiền bằng code bên dưới:

<script>
document.querySelector('#withdraw-form').addEventListener('submit', function (event) {
   event.preventDefault();
   document.querySelector('#loading-indicator').classList.remove('d-none');
   const amount = parseFloat(document.querySelector("#withdraw-form " +
       "input[name='amount']").value);
   fetch("{% url 'wallet-withdraw' wallet.id %}", {
       method: "POST",
       headers: {
           "Content-Type": "application/json",
           "X-CSRFToken": getCookie("csrftoken")
       },
       body: JSON.stringify({ amount: amount })
   })
       .then(response => response.json())
       .then(data => {
           console.log(data);
           if (data.balance !== undefined) { // Change to 'balance' for withdrawal
               const newBalance = parseFloat(data.balance).toFixed(2);
               document.querySelector("#balance").textContent = newBalance;
               document.querySelector('#loading-indicator').classList.add('d-none');
           }
       })
       .catch(error => {
           console.error("Error:", error);
           document.querySelector('#loading-indicator').classList.add('d-none');
       });
});
</script>

Trình nghe sự kiện chịu trách nhiệm xử lý gửi biểu mẫu chuyển và rút tiền (#deposit-form & #withdraw-form).

URL tìm nạp truy vấn là dành cho việc kết hợp URL cho các hành động chuyển và rút tiền.

JSON phản hồi các tác vụ chuyển và rút tiền sau đó được phân tích để cập nhật số dư (data.balance). Tiếp theo, chúng sẽ được định dạng và hiện trên trang này.

Trong wallet/views.py, thêm update sau để hiện trang wallet.html:

from django.shortcuts import render

def wallet_view(request):
   # Retrieve the wallet to display
   wallet = Wallet.objects.first() 
   return render(request, 'wallet.html', {'wallet': wallet})

Ở ví dụ này, bạn sẽ dùng phương thức truy vấn first() để chọn ví của một người dùng cho mục đích minh họa.

Update file urls.py bằng cách thêm đường dẫn tới wallet_view như sau:

from .views import wallet_view

urlpatterns = [
   ...
     path('home/', wallet_view, name='wallet-page'),
]

Truy cập trang của ví từ: http://127.0.0.1:8000/home/.

Với mọi thứ đã thiết lập và hoạt động như mong đợi, thực thi lệnh makemigrationsmigrate. Cuối cùng, chạy ứng dụng:

python manage.py makemigrations
python manage.py migrate

python manage.py runserver

Để truy cập các enpoint API, điều hướng tới http://127.0.0.1:8000/api/.

Kết quả như mong đợi:

Kết quả như mong đợi

Di chuyển tới localhost để tương tác với ví.

Kết quả:

Kết quả dùng mẫu Django

Ví này hiện số dư tài khoản và cho bạn lựa chọn gửi hoặc rút tiền.

Một số hạn chế khi dùng API của cấc mẫu Django:

  • Hạn chế tính linh hoạt.
  • Không hỗ trợ truy vấn bất đồng bộ.
  • Xử lý lỗi hạn chế.

Nhìn chung, các mẫu Django cho phép lập trình viên tập trung vào việc sáng tạo code có thể tái sử dụng và duy trì. Tuy nhiên, do những hạn chế của chúng, mẫu Django có thể không phải lựa chọn tốt nhất khi sử dụng API trên quy mô lớn. Các framework client như React vẫn hữu ích trong việc xây dựng các ứng dụng có thể mở rộng.

Thứ Bảy, 30/09/2023 13:34
52 👨 598
0 Bình luận
Sắp xếp theo