Django Rest Framework - Cách thêm trường tùy chỉnh trong ModelSerializer


89

Tôi đã tạo ModelSerializervà muốn thêm trường tùy chỉnh không thuộc mô hình của tôi.

Tôi đã tìm thấy mô tả để thêm các trường bổ sung ở đây và tôi đã thử những cách sau:

customField = CharField(source='my_field')

Khi tôi thêm trường này và gọi validate()hàm của mình thì trường này không phải là một phần của attrdict. attrchứa tất cả các trường mô hình được chỉ định ngoại trừ các trường bổ sung. Vì vậy, tôi không thể truy cập trường này trong xác thực đã ghi đè của mình, có thể không?

Khi tôi thêm trường này vào danh sách trường như sau:

class Meta:
    model = Account
    fields = ('myfield1', 'myfield2', 'customField')

sau đó tôi gặp lỗi vì customFieldkhông phải là một phần của mô hình của tôi - điều gì đúng vì tôi muốn thêm nó chỉ cho bộ nối tiếp này.

Có cách nào để thêm trường tùy chỉnh không?


Bạn có thể mở rộng thêm về "Nhưng khi trường của tôi không nằm trong danh sách trường mô hình được chỉ định trong bộ tuần tự thì nó không phải là một phần của từ điển validate () attr.", Tôi không chắc điều đó rất rõ ràng.
Tom Christie

Ngoài ra "nó phàn nàn - một cách chính xác - rằng tôi không có trường customField trong mô hình của mình.", Bạn có thể nói rõ về ngoại lệ mà bạn nhìn thấy - cảm ơn! :)
Tom Christie

Tôi đã cập nhật bài đăng của mình và hy vọng nó rõ ràng hơn bây giờ. Tôi chỉ muốn biết làm thế nào tôi có thể thêm một lĩnh vực mà không phải là một phần của mô hình của tôi ...
Ron


Câu trả lời:


63

Bạn đang làm đúng, ngoại trừ trường CharField(và các trường đã nhập khác) dành cho các trường có thể ghi.

Trong trường hợp này, bạn chỉ muốn một trường chỉ đọc đơn giản, vì vậy thay vào đó chỉ cần sử dụng:

customField = Field(source='get_absolute_url')

4
cảm ơn, nhưng tôi muốn một trường có thể ghi. Tôi chuyển một mã thông báo người dùng nhất định để xác định người dùng của tôi. nhưng trong mô hình của tôi, tôi có người dùng chứ không phải mã thông báo. vì vậy tôi muốn chuyển mã thông báo và "chuyển đổi" nó thành đối tượng người dùng thông qua truy vấn.
Ron

điều tiếp theo là nguồn đó cần nhắm mục tiêu một thuộc tính mô hình, phải không? trong trường hợp của tôi, tôi không có thuộc tính để chỉ vào.
Ron

Tôi không hiểu người dùng / bit mã thông báo của nhận xét. Nhưng nếu bạn muốn bao gồm một trường trong bộ tuần tự bị loại bỏ trước khi bạn khôi phục vào một cá thể mô hình, bạn có thể sử dụng .validate()phương pháp để loại bỏ thuộc tính. Hãy xem: django-rest-framework.org/api-guide/serializers.html#validation Điều đó sẽ làm những gì bạn cần, mặc dù tôi không thực sự hiểu trường hợp sử dụng hoặc tại sao bạn muốn một trường có thể ghi được gắn với một read-only bất động sản get_absolute_url?
Tom Christie

quên về việc get_absolute_urltôi chỉ sao chép và dán nó từ tài liệu. Tôi chỉ muốn một trường có thể ghi bình thường mà tôi có thể truy cập trong validate(). Tôi chỉ đoán rằng tôi cần sourcethuộc tính ...
Ron

Điều đó có ý nghĩa hơn. :) Giá trị sẽ được chuyển qua để xác thực, vì vậy tôi sẽ kiểm tra kỹ cách bạn khởi tạo bộ tuần tự và nếu giá trị đó thực sự đang được cung cấp.
Tom Christie

82

Trong thực tế, có một giải pháp mà không cần chạm vào mô hình nào cả. Bạn có thể sử dụng SerializerMethodFieldnó cho phép bạn cắm bất kỳ phương pháp nào vào bộ nối tiếp của mình.

class FooSerializer(ModelSerializer):
    foo = serializers.SerializerMethodField()

    def get_foo(self, obj):
        return "Foo id: %i" % obj.pk

6
Như OP đề cập trong nhận xét này , họ muốn có một lĩnh vực có thể ghi, mà SerializerMethodFields không
Esmail

14

... để rõ ràng, nếu bạn có Phương pháp mô hình được xác định theo cách sau:

class MyModel(models.Model):
    ...

    def model_method(self):
        return "some_calculated_result"

Bạn có thể thêm kết quả của việc gọi phương thức đã nói vào bộ tuần tự của mình như sau:

class MyModelSerializer(serializers.ModelSerializer):
    model_method_field = serializers.CharField(source='model_method')

ps Vì trường tùy chỉnh không thực sự là một trường trong mô hình của bạn, nên bạn thường muốn đặt nó ở chế độ chỉ đọc, như vậy:

class Meta:
    model = MyModel
    read_only_fields = (
        'model_method_field',
        )

3
Nếu tôi muốn nó có thể ghi được thì sao?
Csaba Toth

1
@Csaba: Bạn sẽ chỉ cần ghi tùy chỉnh tiết kiệm và móc xóa cho nội dung bổ sung: Xem "Lưu và móc xóa" dưới "Phương pháp" ( Ở đây ) Bạn sẽ cần phải ghi tùy chỉnh perform_create(self, serializer), perform_update(self, serializer), perform_destroy(self, instance).
Lindauson

13

đây câu trả lời cho câu hỏi của bạn. bạn nên thêm vào Tài khoản mô hình của mình:

@property
def my_field(self):
    return None

bây giờ bạn có thể sử dụng:

customField = CharField(source='my_field')

nguồn: https://stackoverflow.com/a/18396622/3220916


6
Tôi đã sử dụng cách tiếp cận này khi nó có ý nghĩa nhưng không tuyệt khi thêm mã bổ sung vào các mô hình cho những thứ chỉ thực sự được sử dụng cho các lệnh gọi API cụ thể.
Andy Baker

1
Bạn có thể viết một mô hình proxy cho các
ashwoods

10

Để hiển thị self.author.full_name, tôi gặp lỗi với Field. Nó hoạt động với ReadOnlyField:

class CommentSerializer(serializers.HyperlinkedModelSerializer):
    author_name = ReadOnlyField(source="author.full_name")
    class Meta:
        model = Comment
        fields = ('url', 'content', 'author_name', 'author')

6

Với phiên bản mới nhất của Django Rest Framework, bạn cần tạo một phương thức trong mô hình của mình với tên trường bạn muốn thêm.

class Foo(models.Model):
    . . .
    def foo(self):
        return 'stuff'
    . . .

class FooSerializer(ModelSerializer):
    foo = serializers.ReadOnlyField()

    class Meta:
        model = Foo
        fields = ('foo',)

3

Tôi đang tìm kiếm giải pháp để thêm trường tùy chỉnh có thể ghi vào bộ tuần tự mô hình. Tôi đã tìm thấy cái này, chưa được đề cập trong các câu trả lời cho câu hỏi này.

Có vẻ như bạn thực sự cần viết Serializer đơn giản của riêng mình.

class PassThroughSerializer(serializers.Field):
    def to_representation(self, instance):
        # This function is for the direction: Instance -> Dict
        # If you only need this, use a ReadOnlyField, or SerializerField
        return None

    def to_internal_value(self, data):
        # This function is for the direction: Dict -> Instance
        # Here you can manipulate the data if you need to.
        return data

Bây giờ bạn có thể sử dụng Serializer này để thêm các trường tùy chỉnh vào ModelSerializer

class MyModelSerializer(serializers.ModelSerializer)
    my_custom_field = PassThroughSerializer()

    def create(self, validated_data):
        # now the key 'my_custom_field' is available in validated_data
        ...
        return instance

Điều này cũng hoạt động, nếu Model MyModelthực sự có một thuộc tính được gọi my_custom_fieldnhưng bạn muốn bỏ qua các trình xác nhận của nó.


vì vậy nó không hoạt động nếu my_custom_field không phải là tài sản của MyModel phải không? Tôi gặp lỗi Trường bộ tuần tự hóa có thể được đặt tên không chính xác và không khớp với bất kỳ thuộc tính hoặc khóa nào trên MyModelphiên bản.
Sandeep Balagopal

2

Sau khi đọc tất cả các câu trả lời ở đây, kết luận của tôi là không thể làm điều này một cách sạch sẽ. Bạn phải chơi bẩn và làm điều gì đó khó hiểu như tạo trường write_only và sau đó ghi đè các phương thức validateto_representation. Đây là những gì làm việc cho tôi:

class FooSerializer(ModelSerializer):

    foo = CharField(write_only=True)

    class Meta:
        model = Foo
        fields = ["foo", ...]

    def validate(self, data):
        foo = data.pop("foo", None)
        # Do what you want with your value
        return super().validate(data)

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data["foo"] = whatever_you_want
        return data
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.