首页 > 解决方案 > Django 管理员和 on_delete=PROTECTED 的单元测试之间的结果不一致

问题描述

我正在 Django 中制作一个“简单”的零件库存应用程序。

我有这个作为我的models.py。

from django.db import models


# Create your models here.
class Site(models.Model):
    create_date = models.DateField(auto_now_add=True)
    name = models.TextField(max_length=150,  null=False, unique=True, verbose_name="Site name")
    update_date = models.DateField(auto_now=True)

    def __str__(self):
        return self.name


class Location(models.Model):
    create_date = models.DateField(auto_now_add=True)
    name = models.TextField(max_length=150,  null=False, unique=False)
    site = models.ForeignKey(Site, null=False, on_delete=models.PROTECT)
    update_date = models.DateField(auto_now=True)

    @property
    def site_name(self):
        try:
            return self.site.name
        except Exception as ex:
            return 'Unknown'

    def __str__(self):
        return self.name


class Part(models.Model):
    create_date = models.DateField(auto_now_add=True)
    name = models.TextField(max_length=150, null=False, unique=True)
    verbose_name = models.TextField(max_length=150, null=True)
    update_date = models.DateField(auto_now=True)

    def __str__(self):
        return self.name


class PartLocation(models.Model):
    create_date = models.DateField(auto_now_add=True)
    location = models.ForeignKey(Location, null=False, on_delete=models.PROTECT)
    number_of_parts = models.IntegerField(null=False)
    part = models.ForeignKey(Part, null=False, on_delete=models.PROTECT)
    update_date = models.DateField(auto_now=True)

    @property
    def site(self):
        try:
            return self.location.site.name
        except Exception as ex:
            return 'Unknown'

    def __str__(self):
        return f'{self.part.name}/{self.location.name}/{self.site}/{self.number_of_parts}'

我添加了一个如下所示的 admin.py:

from django.contrib import admin
from .models import Location, Part, PartLocation, Site


class PartLocationAdmin(admin.ModelAdmin):

    ordering = ('id', 'location', 'part', 'location__site__name', 'number_of_parts',)
    list_display = ('id', 'part', 'get_part_verbose_name', 'location', 'site', 'number_of_parts',)
    list_filter = ('id', 'part', 'location', 'location__site__name', 'number_of_parts',)
    search_fields = ('id', 'part__name', 'location__name', 'location__site__name', 'number_of_parts',)

    def get_part_verbose_name(self,obj):
        if obj.part.verbose_name is None:
            return "-"
        return obj.part.verbose_name
    get_part_verbose_name.command_order_field  = 'Verbose name'  #Allows column order sorting
    get_part_verbose_name.short_description = 'Verbose name'  #Renames column head


class LocationAdmin(admin.ModelAdmin):
    ordering = ('id', 'name', 'site',)
    list_display = ('id', 'name', 'site',)
    search_fields = ('id', 'name', 'site__name',)


class PartAdmin(admin.ModelAdmin):
    ordering = ('id', 'name', 'verbose_name')
    list_display = ('id', 'name', 'verbose_name')
    search_fields = ('id', 'name', 'verbose_name')


# Register your models here.
admin.site.register(Site)
admin.site.register(Location, LocationAdmin)
admin.site.register(Part, PartAdmin)
admin.site.register(PartLocation, PartLocationAdmin)

我在我的tests.py中添加了单元测试:

from django.test import TestCase, TransactionTestCase
import django.db.utils
from .models import Location, Part, PartLocation, Site
from django.db.models import ProtectedError


class ModelTestCases(TransactionTestCase):

    def add_and_delete(self):
        '''
        In this test we will add a site, location, part and partlocation information into the database.
        Then we will ensure that one cannot delete a site until all sub records have also been deleted.
        :return:
        '''

        # Let's create a site.
        site_1 = Site.objects.create(name='site_1')

        # Let's demostrate that you cannot create a location without a cite.
        try:
            loc_1 = Location.objects.create(name='loc_1')
        except django.db.utils.IntegrityError as ex:
            self.assertEqual('null value in column "site_id" violates not-null constraint', ex.args[0].split('\n')[0])

        # Create a Location at site_1
        loc_1 = Location.objects.create(name='loc_1', site=site_1)

        # Create a Part
        part_1 = Part.objects.create(name='part_1', verbose_name='Part One')

        # Let's add inventory or part_1 in loc_1.
        part_loc_1 = PartLocation(location=loc_1, part=part_1, number_of_parts=5)

        # Let's demostrate that one cannot remove site_1 because it has a location.
        try:
            site_1.delete()
            self.fail('Deletion of site not prevented.')
        except ProtectedError as ex:
            pass

        # Let's demostrate that one cannot remove loc_1 because it has a PartLocation record.
        try:
            loc_1.delete()
            self.assertEqual(0, Location.objects.count())
            self.fail('Deletion of location not prevented.')
        except ProtectedError as ex:
            pass

如果我在管理面板中转到我的位置列表,我会看到这个......

在此处输入图像描述

...然后我单击“开始”按钮删除所选位置,我看到:

在此处输入图像描述

这是我想要的行为。但是我的单元测试允许删除包含部分的位置并引发此错误。

AssertionError: Deletion of location not prevented.

为什么我的管理代码阻止删除而我的单元测试却没有?

我的 Django 版本是:

django.VERSION
(2, 1, 7, 'final', 0)

标签: pythondjango

解决方案


哦!我总是犯这个错误!在我的单元测试中,我有这个:

part_loc_1 = PartLocation(location=loc_1, part=part_1, number_of_parts=5)

当我应该写这个时:

part_loc_1 = PartLocation.objects.create(location=loc_1, part=part_1, number_of_parts=5)


推荐阅读