Django rest框架未使用FK创建对象到具有unique = True字段的模型 [英] Django rest framework not creating object with FK to a model with unique=True field
问题描述
我有两个这样的模型:
class Sector(models.Model):
name = models.CharField(max_length=100, db_index=True, unique=True) # HERE IF I REMOVE unique=True, it works correctly
class Address(models.Model):
...
sector = models.ForeignKey(Sector, null=True, blank=True)
地址模型的序列化器:
在视图中,我有以下内容:
In the view, I have this:
address_serialized = AddressSerializer(data=request.data)
if address_serialized.is_valid():
address_serialized.save(client=client)
它永远不会进入 create
函数。我有一个使用create函数进行序列化的代码,如下所示:
It never gets to the create
function. I have a serialized with a create function that looks like this:
class AddressSerializer(serializers.ModelSerializer):
city_gps = CitySerializer(required=False)
sector = SectorSerializer(required=False)
class Meta:
model = Address
fields = (..., "sector")
def create(self, validated_data):
...
sector_dict = validated_data.get("sector", None)
sector = None
if sector_dict and "name" in sector_dict and city_gps:
if Sector.objects.filter(name=sector_dict["name"], city=city_gps).exists():
sector = Sector.objects.get(name=sector_dict["name"], city=city_gps)
# pdb.set_trace()
if "sector" in validated_data:
validated_data.pop("sector")
if "city_gps" in validated_data:
validated_data.pop("city_gps")
address = Address.objects.create(sector=sector, city_gps=city_gps, **validated_data)
return address
代码从不触及此函数, is_valid()
返回False。消息是
The code never touches this function, is_valid()
returns False. And the message is
{ sector:{ name:[具有该名称的扇区已经存在。]}}
{"sector":{"name":["sector with this name already exists."]}}
我需要能够使用FK为现有部门创建一个新地址。我该如何实现?任何建议都会有所帮助。
I need to be able to create a new address with FK to the already existing sector. How can I achieve that? Any advice will help.
编辑
视图如下所示:
class ClientProfileAddressCreateView(APIView):
# throttle_scope = '1persecond'
renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,)
def post(self, request):
try:
client = Client.objects.get(user=request.user)
except ObjectDoesNotExist:
return Response({"error": "A client profile for the logged user does not exit"},
status=status.HTTP_404_NOT_FOUND)
address_serialized = AddressSerializer(data=request.data)
print("address_serialized.is_valid: %s" % address_serialized.is_valid()) # Returns False when unique=True in models
if address_serialized.is_valid():
# print("address_serialized: %s" % address_serialized.data)
address_serialized.save(client=client)
else:
return Response(data=address_serialized.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(data=address_serialized.data, status=status.HTTP_201_CREATED)
推荐答案
这是一个已知问题具有嵌套的序列化程序和独特的约束。
This is a known issue with nested serializers and unique constraints.
真正要做的事情实际上是打印序列化程序-这样可以给您很多额外的信息。
Really awesome thing to always do is actually print the Serializer - that can give you a lot of extra info.
当您拥有这样的json:
When you have a json like this:
{
"Sector": {
"name": "Sector XYZ"
},
"address_line_one": "Some Random Address"
}
Django REST框架不知道您是要创建还是获取Sector对象,因此它会强制对每个请求进行验证。
Django REST framework does not know whether you're creating or getting the Sector object, thus it forces validation on every request.
您需要执行的操作如下:
What you need to do is the following:
class SectorSerializer(serializers.ModelSerializer):
# Your fields.
class Meta:
model = Address
fields = ("Your Fields",)
extra_kwargs = {
'name': {
'validators': [],
}
}
然后处理验证,您将需要重做创建/更新部分以适应唯一性约束并引发异常/验证错误。
Then to handle validation you would need to redo the create/update part to fit the uniqueness constraint and raise exception/validation error.
我希望这会有所帮助。
I hope this helps.
有用的链接:此SO答案和在嵌套序列化程序中使用唯一约束进行处理
编辑:
根据cezar的要求:我将添加重写序列化器的 create
方法的外观。我没有尝试过这段代码,但是逻辑是这样的。
As per cezar's request: I will add how it might look like to override the create
method of the serializer. I have not tried this code, but the logic goes like this.
class SectorSerializer(serializers.ModelSerializer):
# Your fields.
class Meta:
model = Address
fields = ("Your Fields",)
extra_kwargs = {
'name': {
'validators': [],
}
}
def create(self, validated_data):
raise_errors_on_nested_writes('create', self, validated_data)
ModelClass = self.Meta.model
info = model_meta.get_field_info(ModelClass)
many_to_many = {}
for field_name, relation_info in info.relations.items():
if relation_info.to_many and (field_name in validated_data):
many_to_many[field_name] = validated_data.pop(field_name)
# FIELD CHECK
your_field = validated_data.get("your_field","") # or validated_data["your_field"]
try:
YourModel.objects.filter(your_check=your_field).get()
raise ValidationError("Your error")
except YourModel.DoesNotExist:
# if it doesn't exist it means that no model containing that field exists so pass it. You can use YourQuerySet.exists() but then the logic changes
pass
try:
instance = ModelClass.objects.create(**validated_data)
except TypeError:
tb = traceback.format_exc()
msg = (
'Got a `TypeError` when calling `%s.objects.create()`. '
'This may be because you have a writable field on the '
'serializer class that is not a valid argument to '
'`%s.objects.create()`. You may need to make the field '
'read-only, or override the %s.create() method to handle '
'this correctly.\nOriginal exception was:\n %s' %
(
ModelClass.__name__,
ModelClass.__name__,
self.__class__.__name__,
tb
)
)
raise TypeError(msg)
# Save many-to-many relationships after the instance is created.
if many_to_many:
for field_name, value in many_to_many.items():
field = getattr(instance, field_name)
field.set(value)
return instance
这篇关于Django rest框架未使用FK创建对象到具有unique = True字段的模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!