在Django 2.0项目中,我的单元测试存在以下问题,但我找不到原因。
- 更新:我使用的是Postgres 10.1。当我切换到sqlite3
时,不会发生此问题我正在实施一个跟踪另一个模型的任何变化的模型
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Investment(models.Model):
"""the main model"""
status = models.IntegerField()
class InvestmentStatusTrack(models.Model):
"""track every change of status on an investment"""
investment = models.ForeignKey(Investment, on_delete=models.CASCADE)
status = models.IntegerField()
modified_on = models.DateTimeField(
blank=True, null=True, default=None, verbose_name=_('modified on'), db_index=True
)
modified_by = models.ForeignKey(
User, blank=True, null=True, default=None, verbose_name=_('modified by'), on_delete=models.CASCADE
)
class Meta:
ordering = ('-modified_on', )
def __str__(self):
return '{0} - {1}'.format(self.investment, self.status)
@receiver(post_save, sender=Investment)
def handle_status_track(sender, instance, created, **kwargs):
"""add a new track every time the investment status change"""
request = get_request() # a way to get the current request
modified_by = None
if request and request.user and request.user.is_authenticated:
modified_by = request.user
InvestmentStatusTrack.objects.create(
investment=instance, status=instance.status, modified_on=datetime.now(), modified_by=modified_by
)
我的大多数单元测试都失败并带有以下追溯
Traceback (most recent call last):
File "/env/lib/python3.6/site-packages/django/test/testcases.py", line 209, in __call__
self._post_teardown()
File "/env/lib/python3.6/site-packages/django/test/testcases.py", line 893, in _post_teardown
self._fixture_teardown()
File "/env/lib/python3.6/site-packages/django/test/testcases.py", line 1041, in _fixture_teardown
connections[db_name].check_constraints()
File "/env/lib/python3.6/site-packages/django/db/backends/postgresql/base.py", line 235, in check_constraints
self.cursor().execute('SET CONSTRAINTS ALL IMMEDIATE')
File "/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
File "/env/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/env/lib/python3.6/site-packages/django/db/backends/utils.py", line 83, in _execute
return self.cursor.execute(sql)
django.db.utils.IntegrityError: insert or update on table "investments_investmentstatustrack" violates foreign key constraint "investments_investme_modified_by_id_3a12fb21_fk_auth_user"
DETAIL: Key (modified_by_id)=(1) is not present in table "auth_user".
任何想法,如何解决这个问题?
- 更新:2单元测试,显示问题。
单独执行时两者都是成功的。似乎问题发生在单元测试tearDown上。外键约束此时失败,因为用户已被删除。
class TrackInvestmentStatusTest(ApiTestCase):
def login(self, is_staff=False):
password = "abc123"
self.user = mommy.make(User, is_staff=is_staff, is_active=True)
self.user.set_password(password)
self.user.save()
self.assertTrue(self.client.login(username=self.user.username, password=password))
def test_add_investment(self):
"""it should add a new investment and add a track"""
self.login()
url = reverse('investments:investments-list')
data = {}
response = self.client.post(url, data=data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(1, Investment.objects.count())
investment = Investment.objects.all()[0]
self.assertEqual(investment.status, Investment.STATUS_IN_PROJECT)
self.assertEqual(1, InvestmentStatusTrack.objects.count())
track = InvestmentStatusTrack.objects.all()[0]
self.assertEqual(track.status, investment.status)
self.assertEqual(track.investment, investment)
self.assertEqual(track.modified_by, self.user)
self.assertEqual(track.modified_on.date(), date.today())
def test_save_status(self):
"""it should modify the investment and add a track"""
self.login()
investment_status = Investment.STATUS_IN_PROJECT
investment = mommy.make(Investment, asset=asset, status=investment_status)
investment_id = investment.id
self.assertEqual(1, InvestmentStatusTrack.objects.count())
track = InvestmentStatusTrack.objects.all()[0]
self.assertEqual(track.status, investment.status)
self.assertEqual(track.investment, investment)
self.assertEqual(track.modified_by, None)
self.assertEqual(track.modified_on.date(), date.today())
url = reverse('investments:investments-detail', args=[investment.id])
data = {
'status': Investment.STATUS_ACCEPTED
}
response = self.client.patch(url, data=data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(1, Investment.objects.count())
investment = Investment.objects.all()[0]
self.assertEqual(investment.id, investment_id)
self.assertEqual(investment.status, Investment.STATUS_ACCEPTED)
self.assertEqual(2, InvestmentStatusTrack.objects.count())
track = InvestmentStatusTrack.objects.all()[0]
self.assertEqual(track.status, Investment.STATUS_ACCEPTED)
self.assertEqual(track.investment, investment)
self.assertEqual(track.modified_by, self.user)
self.assertEqual(track.modified_on.date(), date.today())
track = InvestmentStatusTrack.objects.all()[1]
self.assertEqual(track.status, Investment.STATUS_IN_PROJECT)
self.assertEqual(track.investment, investment)
self.assertEqual(track.modified_by, None)
self.assertEqual(track.modified_on.date(), date.today())
答案 0 :(得分:2)
我通过重构代码来解决问题。
现在我不在投资的GridLayoutManager manager = new GridLayoutManager(getContext(), 3, LinearLayoutManager.VERTICAL, false);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return position == 3 ? 3 : 1;
}
});
方法内或save
信号处理程序内创建轨道,而是在明确调用的方法中
我的代码如下:
models.py
post_save
<强> views.py 强>
class Investment(models.Model):
"""the main model"""
status = models.IntegerField()
def handle_status_track(self):
"""add a new track every time the investment status change"""
request = get_request() # a way to get the current request
modified_by = None
if request and request.user and request.user.is_authenticated:
modified_by = request.user
InvestmentStatusTrack.objects.create(
investment=self, status=self.status, modified_on=datetime.now(), modified_by=modified_by
)
class InvestmentStatusTrack(models.Model):
"""track every change of status on an investment"""
investment = models.ForeignKey(Investment, on_delete=models.CASCADE)
status = models.IntegerField()
modified_on = models.DateTimeField(
blank=True, null=True, default=None, verbose_name=_('modified on'), db_index=True
)
modified_by = models.ForeignKey(
User, blank=True, null=True, default=None, verbose_name=_('modified by'), on_delete=models.CASCADE
)
class Meta:
ordering = ('-modified_on',)
问题是它不完全相同:我需要在保存对象的任何时候处理对方法的调用。我仍然想知道为什么class InvestmentViewSet(ViewSet):
model = Investment
serializer_class = InvestmentSerializer
def perform_create(self, serializer):
"""save"""
investment = serializer.save()
investment.handle_status_track()
def perform_update(self, serializer):
"""save"""
investment = serializer.save()
investment.handle_status_track()
信号会导致此错误。