Я делаю «простое» приложение для инвентаризации деталей в 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
Если я захожу в свой список Locations в панели администратора, я вижу это ...
... и затем я нажимаю кнопку «Перейти», чтобы удалить выбранное местоположение. Я вижу это:
Какое поведение я хочу.Но мой модульный тест позволяет удалить местоположение, в котором есть части, и выдает эту ошибку.
AssertionError: Deletion of location not prevented.
Почему мой код администратора препятствует удалению, а мой модульный тест - нет?
Мой Djangoверсия:
django.VERSION
(2, 1, 7, 'final', 0)