Django测试场景下如何正确配置ManyToManyField多对多关联
问题根因
你遇到的问题来自两个认知误区:
ManyRelatedManager.set()方法本身无返回值,它的作用是直接修改数据库中的多对多关联关系,执行完成后不会返回关联对象的查询集,所以你将方法执行结果赋值给reader变量,拿到的必然是None,哪怕是没有自定义中间表的普通多对多字段也是如此。- 你的
Book.readers字段指定了through='UserBookRelation'自定义中间表,这种场景下Django会禁用set()/add()/create()/remove()这类多对多快捷操作方法——因为自定义中间表通常会存在两个关联外键之外的其他必填字段,快捷方法无法为这些字段赋值,必须通过直接操作中间表的方式建立关联。
正确实现方式
测试用例中的关联配置
直接创建中间表实例即可建立多对多关联,需要获取关联用户列表时,单独调用all()方法查询:
from .models import Book, UserBookRelation class BookSerializerTestCase(TestCase): def setUp(self): self.user = User.objects.create(username='test_username') self.book_1 = Book.objects.create(name='Test book 1', price=750, author_name='Author 1', owner=self.user) self.book_2 = Book.objects.create(name='Test book 2', price=650, author_name='Author 1', owner=self.user) def test_readers(self): # 直接创建中间表记录建立关联,如果中间表有其他必填字段要一并传入 UserBookRelation.objects.create(book=self.book_1, user=self.user) # 主动查询关联的读者列表 readers = self.book_1.readers.all() print(f'Readers: {readers}') # 可以加断言校验关联是否生效 self.assertEqual(readers.count(), 1) self.assertEqual(readers.first(), self.user)
序列化器create方法适配
如果要在序列化器创建Book实例时自动处理readers关联,需要重写序列化器的create方法,在保存Book实例后手动创建中间表记录,示例:
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = '__all__' def create(self, validated_data): readers_data = validated_data.pop('readers', []) book = Book.objects.create(**validated_data) # 批量创建中间表关联 for user in readers_data: UserBookRelation.objects.create(book=book, user=user) return book
注意事项
如果你的UserBookRelation中间表存在除book、user外的其他非空字段(比如评分、是否点赞、阅读进度等),创建中间表记录时必须传入对应字段的值,否则会触发数据库非空约束报错。
内容的提问来源于stack exchange,提问作者Mikhail Tochenov
相关产品推荐
相关产品推荐

