【发布时间】:2016-11-14 03:54:33
【问题描述】:
我想要一个完全只读的ModelSerializer,即只列出/检索方法
最好的方法是什么?
【问题讨论】:
-
这是你想要的还是你想从视图集中删除创建/更新?
-
好点@Linovia,实际上我正在寻找ReadOnlyModelViewSet
标签: python django-rest-framework
我想要一个完全只读的ModelSerializer,即只列出/检索方法
最好的方法是什么?
【问题讨论】:
标签: python django-rest-framework
您确实想在视图(或视图集)级别执行此操作,您可以使用 ReadOnlyModelViewSet 来执行此操作。
(您在评论中提到了这一点,但我将其作为答案以提高知名度)。
例如(来自文档):
from rest_framework import viewsets
class AccountViewSet(viewsets.ReadOnlyModelViewSet):
"""
A simple ViewSet for viewing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer
【讨论】:
如果您确实需要一个只读的序列化程序,覆盖 init 方法是最简洁和稳定的选项:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
setattr(self.Meta, 'read_only_fields', [*self.fields])
与上述比较:
编辑更好的解决方案: 您可以更新 def get_fields 方法而不是 init 方法并创建一个抽象序列化程序:
class ReadOnlyModelSerializer(serializers.ModelSerializer):
def get_fields(self, *args, **kwargs):
fields = super().get_fields(*args, **kwargs)
for field in fields:
fields[field].read_only = True
return fields
要使用它,只需从抽象序列化器继承即可:
def MySerializer(ReadOnlyModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
【讨论】:
如果您使用的是ModelSerializer,您可以通过以下方式实现:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
...
class Meta:
model = MyModel
fields = ("field_1", "field_2", etc)
read_only_fields = [f.name for f in MyModel._meta.get_fields()]
此解决方案使用 django 模型的私有 _meta 接口,但它相当稳定,并且被开发人员广泛使用。
在这里,我们只是生成相关模型中所有字段的列表,并将其应用于django-rest-framework 中的read_only_fields 选项。
我更喜欢在序列化程序级别而不是视图级别执行此操作,因为它不会束缚您使用 mgalgs 建议的 Viewset。
如果您想更进一步,您甚至可以将该功能包装到一个 mixin 中,该 mixin 会添加到您的类的 Meta 选项中。比如:
from rest_framework import serializers
from rest_framework.fields import Field
class ReadOnlyMixin(Field):
def __new__(cls, *args, **kwargs):
setattr(
cls.Meta,
"read_only_fields",
[f.name for f in cls.Meta.model._meta.get_fields()],
)
return super(ReadOnlyMixin, cls).__new__(cls, *args, **kwargs)
class MyModelSerializer(ReadOnlyMixin, serializers.ModelSerializer):
...
class Meta:
model = MyModel
fields = ("field_1", "field_2", etc)
【讨论】:
您唯一需要做的就是创建一个这样的序列化程序。 序列化器.py
class YourdataSerializer(serializers.ModelSerializer):
class Meta:
model = Yourdata
# some data
fields = ('id', 'city', 'pincode', 'created')
read_only_fields = ('id', 'city', 'pincode', 'created')
查看类似的内容
class YourdataList(APIView):
def get(self, request, format=None):
yourdata = YourdataList.objects.all()
serializer = YourdataSerializer(yourdata, many=True)
return Response(serializer.data)
详细视图
class YourdataDetail(APIView):
def get_object(self, pk):
try:
return Yourdata.objects.get(pk=pk)
except Yourdata.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = YourdataSerializer(snippet)
return Response(serializer.data)
这样就可以了。
【讨论】:
Meta 中添加read_only_fields = fields。
好的,亲爱的 Mohsen,要使用 BaseSerializer 类实现只读序列化程序,我们只需要重写 .to_representation() 方法。我们来看一个使用简单 Django 模型的例子:
class HighScore(models.Model):
created = models.DateTimeField(auto_now_add=True)
player_name=models.CharField(max_length=10)
score = models.IntegerField()
创建用于将 HighScore 实例转换为原始数据类型的只读序列化程序很简单。
class HighScoreSerializer(serializers.BaseSerializer):
def to_representation(self, obj):
return { 'score': obj.score, 'player_name': obj.player_name }
【讨论】:
两个解决方案?
from rest_framework.utils import model_meta
class CustomSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
model = getattr(self.Meta, 'model')
declared_fields = copy.deepcopy(self._declared_fields)
info = model_meta.get_field_info(model)
field_names = self.get_field_names(declared_fields, info)
_, hidden_fields = self.get_uniqueness_extra_kwargs(
field_names, declared_fields, extra_kwargs)
fields_names.extend(hidden_fields.keys())
setattr(self.Meta, 'read_only_fields', list(field_names))
或
class CustomSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__()
setattr(self.Meta, 'read_only_fields', list(self.fields))
【讨论】:
read_only_fields = fields 如果字段不是'all',这也有效