Django - 在设置外键对象确实存在的 ManyToMany 属性时,序列化程序抛出 "Invalid pk - object does not exist"
Django - Serializer throwing "Invalid pk - object does not exist" when setting ManyToMany attribute where foreign keyed object does exist
所以下面我有一些代码来测试某人创建 post 并且 post 有一个 hash_tag
的功能,在这种情况下是“#video”。该代码采用 Post
body
并使用正则表达式查找以“#”开头的任何单词。如果是,则它从 HashTag
table 创建或获取 HashTag
。然后将 HashTag
的列表设置为 Post
.
下的 hash_tags
属性
出于某种原因,CreatePostSerializer
序列化程序抛出了一个没有意义的异常。序列化程序抛出异常 ValidationError({'hash_tags': [ErrorDetail(string='Invalid pk "[\'video\']" - object does not exist.', code='does_not_exist')]})
。这没有意义的原因是因为当我调试并在 views.py
下的 except Exception as e
之后设置断点时,这就是我得到的
>>>e
ValidationError({'hash_tags': [ErrorDetail(string='Invalid pk "[\'video\']" - object does not exist.', code='does_not_exist')]})
>>>HashTag.objects.get(pk='video')
<HashTag: HashTag object (video)>
>>>request.data['hash_tags']
['video']
所以 >>>
代表我输入调试器的内容。我基本上停在 return Response...
行,我们可以看到 e
是我提到的 ValidationError
,但我们可以看到它声称不存在的对象确实存在。 为什么序列化程序抛出“ValidationError - 对象不存在”?
注意: 我有另一个测试做完全相同的事情并通过,除了没有视频文件被传递这让我相信 Django 在这种情况下做了不同的事情传入的正文是 multi-part
。我还在实例中尝试了只有一个散列标签来设置 hash_tags
= 而不是列表并且它有效。这是一个 hack,更干净的解决方案是首选。
helpers.py
import re
def extract_hashtags(text):
regex = "#(\w+)"
return re.findall(regex, text)
test.py
def test_real_image_upload_w_hash_tag(self):
image_file = retrieve_test_image_upload_file()
hash_tag = 'video'
response = self.client.post(reverse('post'),
data={'body': f'Some text and an image #{hash_tag}',
'images': [image_file]},
**{'HTTP_AUTHORIZATION': f'bearer {self.access_token}'})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
views.py
def set_request_data_for_post(request, user_uuid: str):
request.data['creator'] = user_uuid
post_text = request.data['body']
hash_tags_list = extract_hashtags(post_text)
hash_tags = [HashTag.objects.get_or_create(hash_tag=ht)[0].hash_tag for ht in hash_tags_list]
if len(hash_tags) > 0:
request.data['hash_tags'] = hash_tags
return request
def create_post(request):
user_uuid = str(request.user.uuid)
request = set_request_data_for_post(request=request, user_uuid=user_uuid)
try:
serializer = CreatePostSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
post_obj = serializer.save()
except Exception as e:
return Response(dict(error=str(e),
user_message=error_message_generic),
status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.data, status=status.HTTP_201_CREATED)
serializer.py
from rest_framework import serializers
from cheers.models import Post
class CreatePostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('creator', 'body', 'uuid', 'created', 'updated_at', 'hash_tags')
model.py
class Post(models.Model):
# ulid does ordered uuid creation
uuid = models.UUIDField(primary_key=True, default=generate_ulid_as_uuid, editable=False)
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
body = models.CharField(max_length=POST_MAX_LEN, validators=[MinLengthValidator(POST_MIN_LEN)])
hash_tags = models.ManyToManyField(HashTag, blank=True)
class HashTag(models.Model):
hash_tag = models.CharField(max_length=HASH_TAG_MAX_LEN, primary_key=True, validators=[
MinLengthValidator(HASH_TAG_MIN_LEN)])
在您的 test/__init__.py
下,您必须添加这些行
from django.db.backends.postgresql.features import DatabaseFeatures
DatabaseFeatures.can_defer_constraint_checks = False
有一些奇怪的内部错误,如果你在一个 table 上操作很多,有很多不同的 TestCase
类 然后它会在结束后进行数据库检查它已被拆除,它会导致错误。
我也在使用 factory boy (https://factoryboy.readthedocs.io/en/stable/orms.html) 来生成我的测试数据库,这是出现此问题的主要原因。我相信这是因为我只使用 <model>.objects.create()
就关闭了 factory boy 并且我的测试不再失败。
所以下面我有一些代码来测试某人创建 post 并且 post 有一个 hash_tag
的功能,在这种情况下是“#video”。该代码采用 Post
body
并使用正则表达式查找以“#”开头的任何单词。如果是,则它从 HashTag
table 创建或获取 HashTag
。然后将 HashTag
的列表设置为 Post
.
hash_tags
属性
出于某种原因,CreatePostSerializer
序列化程序抛出了一个没有意义的异常。序列化程序抛出异常 ValidationError({'hash_tags': [ErrorDetail(string='Invalid pk "[\'video\']" - object does not exist.', code='does_not_exist')]})
。这没有意义的原因是因为当我调试并在 views.py
下的 except Exception as e
之后设置断点时,这就是我得到的
>>>e
ValidationError({'hash_tags': [ErrorDetail(string='Invalid pk "[\'video\']" - object does not exist.', code='does_not_exist')]})
>>>HashTag.objects.get(pk='video')
<HashTag: HashTag object (video)>
>>>request.data['hash_tags']
['video']
所以 >>>
代表我输入调试器的内容。我基本上停在 return Response...
行,我们可以看到 e
是我提到的 ValidationError
,但我们可以看到它声称不存在的对象确实存在。 为什么序列化程序抛出“ValidationError - 对象不存在”?
注意: 我有另一个测试做完全相同的事情并通过,除了没有视频文件被传递这让我相信 Django 在这种情况下做了不同的事情传入的正文是 multi-part
。我还在实例中尝试了只有一个散列标签来设置 hash_tags
=
helpers.py
import re
def extract_hashtags(text):
regex = "#(\w+)"
return re.findall(regex, text)
test.py
def test_real_image_upload_w_hash_tag(self):
image_file = retrieve_test_image_upload_file()
hash_tag = 'video'
response = self.client.post(reverse('post'),
data={'body': f'Some text and an image #{hash_tag}',
'images': [image_file]},
**{'HTTP_AUTHORIZATION': f'bearer {self.access_token}'})
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
views.py
def set_request_data_for_post(request, user_uuid: str):
request.data['creator'] = user_uuid
post_text = request.data['body']
hash_tags_list = extract_hashtags(post_text)
hash_tags = [HashTag.objects.get_or_create(hash_tag=ht)[0].hash_tag for ht in hash_tags_list]
if len(hash_tags) > 0:
request.data['hash_tags'] = hash_tags
return request
def create_post(request):
user_uuid = str(request.user.uuid)
request = set_request_data_for_post(request=request, user_uuid=user_uuid)
try:
serializer = CreatePostSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
post_obj = serializer.save()
except Exception as e:
return Response(dict(error=str(e),
user_message=error_message_generic),
status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.data, status=status.HTTP_201_CREATED)
serializer.py
from rest_framework import serializers
from cheers.models import Post
class CreatePostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('creator', 'body', 'uuid', 'created', 'updated_at', 'hash_tags')
model.py
class Post(models.Model):
# ulid does ordered uuid creation
uuid = models.UUIDField(primary_key=True, default=generate_ulid_as_uuid, editable=False)
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
body = models.CharField(max_length=POST_MAX_LEN, validators=[MinLengthValidator(POST_MIN_LEN)])
hash_tags = models.ManyToManyField(HashTag, blank=True)
class HashTag(models.Model):
hash_tag = models.CharField(max_length=HASH_TAG_MAX_LEN, primary_key=True, validators=[
MinLengthValidator(HASH_TAG_MIN_LEN)])
在您的 test/__init__.py
下,您必须添加这些行
from django.db.backends.postgresql.features import DatabaseFeatures
DatabaseFeatures.can_defer_constraint_checks = False
有一些奇怪的内部错误,如果你在一个 table 上操作很多,有很多不同的 TestCase
类 然后它会在结束后进行数据库检查它已被拆除,它会导致错误。
我也在使用 factory boy (https://factoryboy.readthedocs.io/en/stable/orms.html) 来生成我的测试数据库,这是出现此问题的主要原因。我相信这是因为我只使用 <model>.objects.create()
就关闭了 factory boy 并且我的测试不再失败。