Django+pytest测试报错:ManyRelatedManager无title属性求助
解决Django多对多关系下pytest测试的AttributeError问题
问题场景
使用pytest测试Django API时触发错误:AttributeError: 'ManyRelatedManager' object has no attribute 'title'。错误出现在测试用例中直接访问order_created.product.title,因为Order与Product是多对多关联关系。
测试代码
import pytest from core.orders.factories import * @pytest.fixture def order_created(): user1 = UserFactory(username='Lucas') category = CategoryFactory(title='teste') product1 = ProductFactory(title='teste_product') return OrderFactory(customer=user1) @pytest.mark.django_db def test_order_customer_name(order_created): assert order_created.customer.username == 'Lucas' @pytest.mark.django_db def test_order_product_name(order_created): assert order_created.product.title == 'teste_product'
模型代码
from django.db import models from django.contrib.auth.models import User class Category(models.Model): title = models.CharField(max_length=200) slug = models.SlugField(unique=True) description = models.TextField(max_length=200, blank=True, null=True) active = models.BooleanField(default=True) def __str__(self): return self.title class Product(models.Model): title = models.CharField(max_length=200) description = models.TextField(max_length=500, blank=True, null=True) price = models.PositiveIntegerField(null=True) active = models.BooleanField(default=True) category = models.ManyToManyField(Category, blank=True) def __str__(self): return self.title class Order(models.Model): product = models.ManyToManyField(Product, blank=False) customer = models.ForeignKey(User, on_delete=models.CASCADE, null=False) def __str__(self): return self.customer.name
错误原因
Django中多对多字段默认返回ManyRelatedManager对象,而非单个模型实例。直接访问order.product.title相当于在管理器对象上调用title属性,自然会触发属性不存在的错误。另外当前fixture创建的Order并未关联任何Product(违反了模型中blank=False的约束),这也是潜在问题。
解决方案
1. 修复Fixture,关联产品到订单
修改order_created fixture,在创建Order时关联之前生成的product1:
@pytest.fixture def order_created(): user1 = UserFactory(username='Lucas') category = CategoryFactory(title='teste') product1 = ProductFactory(title='teste_product') # 创建订单并关联产品 order = OrderFactory(customer=user1) order.product.add(product1) return order
如果使用FactoryBoy,也可以直接通过字段传值关联:
# 假设OrderFactory支持多对多字段传值 return OrderFactory(customer=user1, product=[product1])
2. 修改测试用例的断言逻辑
根据业务需求选择合适的断言方式:
- 检查是否存在指定产品:适用于不关心数量只关心是否关联的场景
@pytest.mark.django_db def test_order_product_name(order_created): assert order_created.product.filter(title='teste_product').exists()
- 获取单个产品断言:确定订单只关联一个产品时使用
@pytest.mark.django_db def test_order_product_name(order_created): assert order_created.product.first().title == 'teste_product'
- 检查所有关联产品的标题:适用于需要验证多个产品的场景
@pytest.mark.django_db def test_order_product_name(order_created): product_titles = [p.title for p in order_created.product.all()] assert 'teste_product' in product_titles
内容的提问来源于stack exchange,提问作者Mateus Diniz
相关产品推荐
相关产品推荐

