我有以下两个Django模型类(名称已被更改以保护不那么无辜):
class Foo(models.Model)
foo = models.CharField(max_length=25)
class Bar(models.Model):
foo = models.ForeignKey(Foo)
每个模型的Django Rest Framework序列化器:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = ('foo',)
class BarSerializer(serializers.ModelSerializer):
class Meta:
model = Bar
fields = ('foo',)
路线:
urlpatterns = [
url(r'^foo/', ModelListView.as_view(model_class=Foo, serializer_class=FooSerializer), name='foo'),
url(r'^bar/', ModelListView.as_view(model_class=Bar, serializer_class=BarSerializer), name='Bar'),
]
查看:
class ModelListView(ListCreateAPIView):
model_class = None
serializer_class = None
def create(self, request, *args, **kwargs):
data = JSONParser().parse(request)
serializer = self.serializer_class(data=data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
为了说明/bar
路由,Bar
模型及其序列化器的用法,我写了一个测试。我希望能够指定字段foo
的字符串值。 POST请求应使用Foo
的现有实例(如果已存在)。否则它应该创建它。
class BarTest(UnitTest):
def setUp(self):
self.model_class = Bar
self.fields = [{'foo': 'foo1'},
{'foo': 'foo2'}]
self.url = reverse('bar')
def test_post(self):
data = JSONRenderer().render(self.fields)
response = self.client.post(self.url, data=data, content_type='application/json')
self.assertEqual(HTTP_201_CREATED, response.status_code)
result = response.json()
self.assertEqual(self.fields, result)
我在Django REST Framework文档中阅读了Serializer Relations。似乎StringRelatedField
或SlugRelatedField
可能适合我的用例。我不明白这两者之间的区别,也不了解如何使用它们来获得我想要的行为。其中任何一个都可以用于这些目的吗?如果是这样,我该如何使用它们?如果没有,有哪些替代解决方案?
答案 0 :(得分:1)
根据我的个人经验,我发现很多时候使用通用DRF视图和自动序列化器可能有点过于严格。如果您使用SlugRelatedField
,它将尝试使用给定参数查找Foo
项,如果尚不存在,则会失败。
顺便说一句,StringRelatedField
和SlugRelatedField
之间的区别在于前者是只读的,并且总是返回对象的字符串表示,而后者更灵活,可以用于写同样。
要实现您想要的目标,您可以尝试以下内容:
from rest_framework.generics import ListCreateAPIView
# don't need to subclass from ModelSerializer because we won't use
# its `create`/`update` method or automatic field generation
class CreateBarSerializer(serializers.Serializer):
foo = serializers.CharField()
class BarListView(ListCreateAPIView):
model_class = Bar
serializer_class = BarSerializer
def create(self, request, *args, **kwargs):
data = JSONParser().parse(request)
serializer = CreateBarSerializer(data=data, many=True)
if serializer.is_valid():
validated_data = serializer.validated_data
# create a Foo with given name if it doesn't exist yet
foo = Foo.objects.get_or_create(
foo=validated_data['foo']
)
Bar.objects.create(foo=foo)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
答案 1 :(得分:0)
从@ dukebody的回答中,我了解了Model.objects.get_or_create()
。对序列化器的一些进一步研究表明,我可以覆盖to_internal_value()
。
类BarSerializer(serializers.ModelSerializer): 类Meta: model = Bar fields =(' foo',)
def to_internal_value(self, data):
Foo.objects.get_or_create(foo=data['foo'])
return super().to_internal_value(data)
显然,我原始问题中的代码是人为的。在实际项目中,Bar
对应项还有几个字段,因此该解决方案利用ModelSerializer
来管理原始字段。