Моя конфигурация ниже (django 3.02):
Модели:
class Assets(models.Model):
assettag = models.CharField()
...
class Item(models.Model):
asset = models.OneToOneField('Assets')
...
def __str__(self):
return (f"{self.rfid_tag}" + " - " + f"{self.asset.assettag}")
class Comments(models.Model): # I know, it should not be plural.
item = models.ForeignKey('Item',
default=None,
null=True,
on_delete=models.CASCADE,
)
text = models.TextField()
...
Форма:
class ItemInsertForm(forms.ModelForm):
rfid_tag = forms.CharField(label='RFID Tag',
widget=forms.TextInput(attrs={"placeholder":"96 bit EPC code",
"pattern":"^[a-fA-F0-9]{24}$",
"size":"25",
"id":"rfid_tag",
}
)
)
asset = forms.CharField(label='AssetTag',
widget=forms.TextInput(attrs={"placeholder":"Item Inventory Tag",
"pattern":"{some regex}",
"size":"25",
"id":"asset",
}
)
)
comments = forms.CharField(label='Comments',
required=False,
widget=forms.Textarea(attrs={"rows":5,
"cols":50,
"id":"comments",
}
)
)
class Meta:
model = Item
fields = [
'rfid_tag',
'asset',
'image',
'date',
]
def clean_rfid_tag(self, *args, **kwargs):
return self.cleaned_data.get("rfid_tag").lower()
def clean_asset(self, *args, **kwargs):
AssetTag = self.cleaned_data.get("asset")
try:
_asset = Assets.objects.get(assettag = AssetTag)
except Assets.DoesNotExist:
raise forms.ValidationError("Asset does not exist !")
self.Asset = _asset
return self.Asset
Вид:
class InsertView(SuccessMessageMixin, AuditMixin, generic.CreateView):
template_name = '{some template}'
success_message = "Item succesfully created"
form_class = ItemInsertForm
def form_valid(self, form):
item = form.save(commit=False)
_comment = form.cleaned_data.get('comments')
item = form.save()
if _comment:
item.comments_set.create(text =_comment)
self.log_insert(item)
return super().form_valid(form)
Тест:
class TestViews(TestCase):
@classmethod
def setUpTestData(cls) -> None:
# create dummy assets
cls.asset = Assets.objects.create(assettag="{some assettag #1}",)
cls.asset2 = Assets.objects.create(assettag="{some assettag #2}")
# create dummy item
cls.item = Item.objects.create(rfid_tag='abcdef012345678900000000', asset=cls.asset)
def setUp(self) -> None:
self.client = Client()
self.insert_url = reverse('inventory:insert')
def test_insert_POST_valid(self):
response = self.client.post(self.insert_url,
data = {'rfid_tag':'abcdef012345678900000001',
'asset':'{some assettag #2}',}
)
new_item = Item.objects.get(rfid_tag="abcdef012345678900000001")
self.assertEqual(response.status_code, 302)
self.assertTrue(isinstance(new_item,Item))
self.assertEqual(Item.objects.filter(rfid_tag="abcdef012345678900000001").count(),1)
self.assertEqual(self.asset2.item, new_item)
self.assertFalse(Comments.objects.all())
def test_insert_POST_comment(self):
response = self.client.post(self.insert_url,
data = {'rfid_tag':'abcdef012345678900000001',
'asset':'{some assettag #2}',
'comments':'Test comment.',
}
)
new_item = Item.objects.get(rfid_tag="abcdef012345678900000001")
self.assertEqual(response.status_code, 302)
self.assertTrue(isinstance(new_item,Item))
self.assertEqual(Item.objects.filter(rfid_tag="abcdef012345678900000001").count(),1)
self.assertEqual(self.asset2.item, new_item)
self.assertTrue(isinstance(Comments.objects.get(text='Test comment.'), Comments))
self.assertEqual(Comments.objects.get(text='Test comment.'),
new_item.comments_set.first())
Если я выполню эти тесты, test_insert_POST_valid
не будет выполнен из-за ошибки подтверждения:
AssertionError: ! =
Я вставил несколько операторов печати по всему коду:
# this was placed first in both tests, before the POST request
items = Item.objects.all()
for item in items:
print("Item:",item.id, " - ", item)
# and then again, after each POST request
Я заметил, что при первом запуске печати будет выведен только один Item
объект (это верно для обеих функций):
Item: 1 - abcdef012345678900000009 - {some assettag # 1}
но при втором запуске (после отправки запроса POST и создания нового элемента):
Для test_insert_POST_comment
результат:
Элемент: 1 - abcdef012345678900000009 - {некоторый активаг # 1}
Item: 2 - abcdef012345678900000001 - {некоторый активаг # 2}
В то время как для test_insert_POST_valid
результат равен:
Предмет: 1 - abcdef012345678900000009 - {некоторый активаг # 1}
Предмет: 3 - abcdef012345678900000001 - {Некоторый актив # 2}
Если я сделаю print("Asset Item:", self.asset2.item.id, " - ", self.asset2.item)
в функции test_insert_POST_valid
(по какой-то причине этот тест выполняется вторым) он выдаст:
Элемент актива: 2 - abcdef012345678900000001 - {some assettag # 2}
Я не понимаю, как это происходит. Item.objects.all()
вернет только один объект (abcdef012345678900000000), в то время как объект Asset
все еще поддерживает связь с вымышленным вторым Item
объектом, который не существует.
Я подозревал, что test_insert_POST_valid
фактически не создает никакого объекта, поскольку self.asset2
уже имеет отношение Item
через отношение OneToOne
, но это не так. Если я изменю значение rfid_tag
на abcdef012345678900000002 (или что-то еще), он создаст новый Item
, но self.asset2.item
все равно будет указывать на abcdef012345678900000001.
В чем причина этой проблемы, и может ли это Я это решаю? (Помещение self.asset2.item = None перед отправкой данных не поможет. Новые отношения не будут созданы.)
Редактировать: я мог бы переместить cls.asset2 = Assets.objects.create(assettag="{some assettag #2}")
в методе setUp
и он будет создаваться перед каждым тестом, но у меня есть еще несколько логинов c, в зависимости от этого мне тоже придется его перенести. Предпочтительный способ - оставить его в setUpTestdata
, чтобы он работал только один раз.