我正在创建自定义"从csv"上传使用自定义model.Manager
方法在我的Django应用程序中运行,该方法接收在csv数据上应用csv.DictReader
的结果。我对普通模型的测试是可以的,但是对于具有ForeignKey约束的模型,它们会失败。
相关的源代码如下
models.py
class PledgeManager(models.Manager):
"""Custom manager for model Pledge."""
def from_upload(self, data):
"""Process data from Pledge upload."""
for obj in data:
try:
# get the prospect & fund by natural key
obj['prospect'] = Prospect.objects.get_by_natural_key(
obj['prospect'])
obj['pledge_fund'] = Fund.objects.get_by_natural_key(
obj['pledge_fund'])
obj['pledge_date'] = datetime.strptime(
obj['pledge_date'], r'%d/%m/%Y')
try:
with transaction.atomic():
self.create(**obj)
ccall_log.debug('Created Pledge object: %s', obj)
except IntegrityError:
ccall_log.error('Cannot create Pledge object: %s', obj)
except Prospect.DoesNotExist:
# no prospect
ccall_log.error(
'Cannot create Pledge object, no Prospect: %s', obj)
except Fund.DoesNotExist:
# no fund
ccall_log.error(
'Cannot create Pledge object, no Fund: %s', obj)
except BaseException as exc_:
ccall_log.exception(exc_)
ccall_log.error(
'Exception encountered during processing: %s', obj)
class Pledge(models.Model):
"""Model for a Pledge."""
objects = PledgeManager()
pledge_amount = models.DecimalField(
verbose_name='Pledge amount', decimal_places=2,
max_digits=12, validators=[MinValueValidator(0)])
pledge_fund = models.ForeignKey(Fund, on_delete=models.CASCADE)
pledge_date = models.DateField(verbose_name='Pledge date')
prospect = models.ForeignKey(Prospect, on_delete=models.CASCADE)
test_models.py
class TestPledge(TestCase):
"""Test cases for Pledge."""
@classmethod
def setUpTestData(cls):
prospect_obj = {
'ic': 'Test Prospect',
...
}
Prospect.objects.create(**prospect_obj)
Fund.objects.create(name='Test Fund')
cls.pledge_obj_add_1 = {
'prospect': 'Test Prospect',
'pledge_amount': '50',
'pledge_fund': 'Test Fund',
'pledge_date': '01/03/2017'
}
cls.pledge_obj_add_2 = {
'prospect': 'Test Prospect',
'pledge_amount': '100',
'pledge_fund': 'Test Fund',
'pledge_date': '01/03/2016'
}
def test_from_upload_add_pledge(self):
"""Test adding Pledge via custom manager's from_upload()."""
Pledge.objects.from_upload([self.pledge_obj_add_1])
self.assertEqual(Pledge.objects.count(), 1)
def test_from_upload_multiple_pledge(self):
"""Test adding multiple Pledges via custom manager."""
Pledge.objects.from_upload(
[self.pledge_obj_add_1, self.pledge_obj_add_2])
self.assertEqual(Pledge.objects.count(), 2)
第一次测试通过,但第二次测试失败。这是测试输出:
FAIL: test_from_upload_multiple_pledge (ccall.tests.test_models.TestPledge)
Test adding multiple Pledges via custom manager.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/nhanh/phonathon/ccall/tests/test_models.py", line 273, in test_from_upload_multiple_pledge
self.assertEqual(Pledge.objects.count(), 2)
AssertionError: 1 != 2
日志输出:
ERROR:ccall Cannot create Pledge object, no Prospect: {'pledge_amount': '50','prospect': <Prospect: Test Prospect>, 'pledge_date': datetime.datetime(2017, 3, 1, 0, 0), 'pledge_fund': <Fund: Test Fund>}
DEBUG:ccall Created Pledge object: {'pledge_amount': '100', 'prospect': <Prospect: Test Prospect>, 'pledge_date': datetime.datetime(2016, 3, 1, 0, 0),'pledge_fund': <Fund: Test Fund>}
我不明白的是,第一个对象抛出错误,而第二个对象变得很好(它们具有完全相同的ForeignKey字段)。我创建了一个测试来添加一个Pledge对象,它不会抛出错误。我碰巧触发了Django ORM的任何细微差别吗?谢谢你的帮助!
更新:我已将下面两个相关模型的实施纳入其中。
class ProspectManager(models.Manager):
"""Custom manager for model Prospect."""
def get_by_natural_key(self, ic):
return self.get(ic=ic)
def from_upload(self, data):
"""Process data from Prospect upload."""
...
class Prospect(models.Model):
"""Model for a Prospect."""
objects = ProspectManager()
ic = models.CharField(max_length=15, verbose_name='IC', unique=True)
...
def natural_key(self):
return self.ic
class FundManager(models.Manager):
"""Custom manager for model Fund."""
def get_by_natural_key(self, name):
return self.get(name=name)
def from_upload(self, data):
"""Process data from Fund upload."""
...
class Fund(models.Model):
"""Model for a Pledge Fund."""
objects = FundManager()
name = models.CharField(verbose_name='Fund name',
max_length=50, unique=True)
def natural_key(self):
return self.name