12/08/2018, 16:33

Advanced Django REST framework

Ở bài trước mình đã giới thiệu cho các bạn cách tạo ra một REST API căn bản từ 1 project django. Link tại đây https://viblo.asia/p/gioi-thieu-django-rest-framework-Eb85oJb2l2G Bài này mình sẽ giới thiệu tiếp 1 số tính năng thêm của django REST framework. Gồm có: Customize serializer ...

Ở bài trước mình đã giới thiệu cho các bạn cách tạo ra một REST API căn bản từ 1 project django.

Link tại đây https://viblo.asia/p/gioi-thieu-django-rest-framework-Eb85oJb2l2G

Bài này mình sẽ giới thiệu tiếp 1 số tính năng thêm của django REST framework.

Gồm có:

  • Customize serializer
  • Overide API's functions

Customize serializer

Customize field

Ở phần trước trong serializer của Post Model mình để fields = '__all__' thực ra chỉ để cho nhanh. trong thực tế mình sẽ ko dùng hết tất cả các field của model:

# serializer.py
# Định nghĩa model cần serialize và các trường:
class PostListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        # ko dùng đến updated và created
        fields = ('title', 'content', 'draft', 'read_time', )
        # định nghĩa trường chỉ cho phép đọc
        read_only_fields = ('draft', 'read_time')             
        

Ở đây read_only_fields chỉ những trường chỉ cho phép đọc. Tức là chỉ dùng phương thức GET. Còn cụ thể dùng như thế nào, trong trường hợp nào thì mình sẽ nói ở bên dưới phần CustomAPI

Trong thực tế ta sẽ dùng một số custom field. Trong trường hợp này ta sẽ sử dùng property của Model hoặc nếu chỉ dùng trường đó cho riêng API thì ta dùng serializers.SerializerMethodField. giả sử trong trường hợp này ta format lại trường created.

# serializer.py
class PostListSerializer(serializers.ModelSerializer):
	 # khai báo trường custom
	created_formated = serializers.SerializerMethodField(read_only=True)
       
    # hàm này để get dữ liệu cho trường created_formated
    def get_created_formated(post)
    	# return dd-mm-yyyy format
    	return post.created.strftime("%d-%m-%Y") 
       
    class Meta:
        model = Post
        fields = ('title', 'content', 'draft', 'read_time', 'created_formated')
        read_only_fields = ('draft', 'read_time')
       

Trong trường hợp có quá nhiều trường mà ta chỉ bỏ 1 hoặc 2 trường. Thì việc liệt kê tất cả các trường ra là một cực hình. ta có thể dùng exclude thay cho fields, để chỉ ra những trường nào ko dùng trong model

class PostListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        # ko dùng đến updated và created
        exclude = ('updated', 'created',)
        # định nghĩa trường chỉ cho phép đọc
        read_only_fields = ('draft', 'read_time') 

Validate Field

Việc validate một trường có thể được viết trong model nhưng đối với một trường custom thì ta phải validate ở đây. hoặc trong trường hợp ta muốn chỉ validate cho riêng API này thôi. Vì viết trong model sẽ validate ở tất cả những nơi dùng model đó.

ví dụ ta muốn validate trường title: đơn giản ta khai báo một hàm có tên validate_title. cú pháp chung sẽ là validate_+têntrường:

class PostListSerializer(serializers.ModelSerializer):
    def validate_title(self, value):
        """
        Kiểm tra trong tiêu đề có từ django hay không. không có sẽ báo lỗi.
        """
        if 'django' not in value.lower():
        	raise serializers.ValidationError("Blog post is not about Django")
        return value

Validate Object

Trong trường hợp ta muốn validate trên nhiều field cùng lúc. ta có thể dùng hàm validate trong serializer

class PostListSerializer(serializers.ModelSerializer):
    
    start = serializers.DateTimeField()
    finish = serializers.DateTimeField()

    def validate(self, data):
        """
        Kiểm tra nếu start lớn hơn stop.
        """
        if data['start'] > data['finish']:
            raise serializers.ValidationError("finish must occur after start")
        return data

NOTE: ta có thể validate ở tầng model hoặc tầng serializer. Trong mọi trường hợp thì việc validate của Model sẽ trả về lỗi trước. sau đó mới đến phần validate ở Serializer. REST API của ta sẽ ko trả về validate của tất cả các fields cùng một lúc. Sẽ khó khăn cho việc frontend hiển thị lỗi. Nên mình khuyến nghị chỉ validate ở một nơi thôi.

Dùng Validator

Trong nhiều trường hợp ta có vể viết một hàm validate để có thể sử dụng cho nhiều field ở nhiều nơi khác nhau. Cú pháp như sau:

def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
	# NOTE: là một list các validator => có thể là nhiều loại validate.
    score = IntegerField(validators=[multiple_of_ten])

Serialier lồng nhau.

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    # user = UserSerializer(required=False) => trong trường hợp người dùng ẩn danh hoặc ko muốn đăng ký.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

Một số thư viện bên thứ 3 cho serializer

  • django-rest-marshmallow
  • django-rest-framework-mongoengine
  • django-rest-framework-gis
  • django-rest-framework-hstore
  • dynamic-rest
  • drf-dynamic-fields
  • django-rest-framework-serializer-extensions
  • html-json-forms
  • DRF-Base64
  • drf-writable-nested

Phần này cũng đã khá dài. còn phần Custom API mình sẽ để dành cho bài tiếp theo. Bài viết chắc có nhiều thiếu xót. mọi người cứ thoải mái blame nhé.

From Cherry with Love.

Thanks for watching ~!

0