2015-06-11 1 views
7

Я запускаю проблему с базой данных в своих модульных тестах. Я думаю, что это имеет какое-то отношение к тому, как я использую TestCase и setUpData.Django Tests: setUpTestData on Postgres бросает: «Дублирующее значение ключа нарушает уникальное ограничение»

Когда я пытаюсь установить мои тестовые данные с определенными значениями, тесты бросить следующее сообщение об ошибке:

django.db.utils.IntegrityError: duplicate key value violates unique constraint 

... 

psycopg2.IntegrityError: duplicate key value violates unique constraint "InventoryLogs_productgroup_product_name_48ec6f8d_uniq" 
DETAIL: Key (product_name)=(Almonds) already exists. 

Я изменил все мои первичные ключи и это, кажется, работает нормально. Кажется, что это не повлияло ни на один из тестов.

Однако я обеспокоен тем, что делаю что-то неправильно. Когда это произошло впервые, я отменил около часа работы над моим приложением (не так много кода для нуба), что исправило проблему.

Затем, когда я снова написал изменения, эта же проблема снова появилась. TestCase вставлен ниже. Проблема возникает, когда я добавляю элементы sortrecord, но соответствует элементам, расположенным над ним.

Я не хочу продолжать и менять первичные ключи и URL-адреса в своих тестах, поэтому, если кто-то видит что-то не так, как я использую это, пожалуйста, помогите мне. Благодаря!

TestCase

class DetailsPageTest(TestCase): 


@classmethod 
def setUpTestData(cls): 

    cls.product1 = ProductGroup.objects.create(
         product_name="Almonds" 
         ) 
    cls.variety1 = Variety.objects.create(
         product_group = cls.product1, 
         variety_name = "non pareil", 
         husked = False, 
         finished = False, 
         ) 

    cls.supplier1 = Supplier.objects.create(
         company_name = "Acme", 
         company_location = "Acme Acres", 
         contact_info = "Call me!" 
         ) 

    cls.shipment1 = Purchase.objects.create(
         tag=9, 
         shipment_id=9999, 
         supplier_id = cls.supplier1, 
         purchase_date='2015-01-09', 
         purchase_price=9.99, 
         product_name=cls.variety1, 
         pieces=99, 
         kgs=999, 
         crackout_estimate=99.9 
         ) 
    cls.shipment2 = Purchase.objects.create(
         tag=8, 
         shipment_id=8888, 
         supplier_id=cls.supplier1, 
         purchase_date='2015-01-08', 
         purchase_price=8.88, 
         product_name=cls.variety1, 
         pieces=88, 
         kgs=888, 
         crackout_estimate=88.8 
         ) 
    cls.shipment3 = Purchase.objects.create(
         tag=7, 
         shipment_id=7777, 
         supplier_id=cls.supplier1, 
         purchase_date='2014-01-07', 
         purchase_price=7.77, 
         product_name=cls.variety1, 
         pieces=77, 
         kgs=777, 
         crackout_estimate=77.7 
         ) 

    cls.sortrecord1 = SortingRecords.objects.create(
         tag=cls.shipment1, 
         date="2015-02-05", 
         bags_sorted=20, 
         turnout=199, 
         ) 

    cls.sortrecord2 = SortingRecords.objects.create(
         tag=cls.shipment1, 
         date="2015-02-07", 
         bags_sorted=40, 
         turnout=399, 
         ) 
    cls.sortrecord3 = SortingRecords.objects.create(
         tag=cls.shipment1, 
         date='2015-02-09', 
         bags_sorted=30, 
         turnout=299, 
         ) 

Модели

from datetime import datetime 

from django.db import models 
from django.db.models import Q 


class ProductGroup(models.Model): 
    product_name = models.CharField(max_length=140, primary_key=True) 

    def __str__(self): 
     return self.product_name 

    class Meta: 
     verbose_name = "Product" 

class Supplier(models.Model): 
    company_name = models.CharField(max_length=45) 
    company_location = models.CharField(max_length=45) 
    contact_info = models.CharField(max_length=256) 

    class Meta: 
     ordering = ["company_name"] 

    def __str__(self): 
     return self.company_name 

class Variety(models.Model): 
    product_group = models.ForeignKey(ProductGroup) 
    variety_name = models.CharField(max_length=140) 
    husked = models.BooleanField() 
    finished = models.BooleanField() 
    description = models.CharField(max_length=500, blank=True) 

    class Meta: 
     ordering = ["product_group_id"] 
     verbose_name_plural = "Varieties" 

    def __str__(self): 
     return self.variety_name 


class PurchaseYears(models.Manager): 

    def purchase_years_list(self): 
     unique_years = Purchase.objects.dates('purchase_date', 'year') 
     results_list = [] 
     for p in unique_years: 
      results_list.append(p.year) 
     return results_list 


class Purchase(models.Model): 
    tag = models.IntegerField(primary_key=True) 
    product_name = models.ForeignKey(Variety, related_name='purchases') 
    shipment_id = models.CharField(max_length=24) 
    supplier_id = models.ForeignKey(Supplier) 
    purchase_date = models.DateField() 
    estimated_delivery = models.DateField(null=True, blank=True) 
    purchase_price = models.DecimalField(max_digits=6, decimal_places=3) 
    pieces = models.IntegerField() 
    kgs = models.IntegerField() 
    crackout_estimate = models.DecimalField(max_digits=6,decimal_places=3, null=True) 
    crackout_actual = models.DecimalField(max_digits=6,decimal_places=3, null=True) 
    objects = models.Manager() 
    purchase_years = PurchaseYears() 
    # Keep manager as "objects" in case admin, etc. needs it. Filter can be called like so: 
    # Purchase.objects.purchase_years_list() 
    # Managers in docs: https://docs.djangoproject.com/en/1.8/intro/tutorial01/ 

    class Meta: 
     ordering = ["purchase_date"] 

    def __str__(self): 
     return self.shipment_id 

    def _weight_conversion(self): 
     return round(self.kgs * 2.20462) 
    lbs = property(_weight_conversion) 

class SortingModelsBagsCalulator(models.Manager): 

    def total_sorted(self, record_date, current_set): 
     sorted = [SortingRecords['bags_sorted'] for SortingRecords in current_set if 
        SortingRecords['date'] <= record_date] 
     return sum(sorted) 


class SortingRecords(models.Model): 
    tag = models.ForeignKey(Purchase, related_name='sorting_record') 
    date = models.DateField() 
    bags_sorted = models.IntegerField() 
    turnout = models.IntegerField() 
    objects = models.Manager() 

    def __str__(self): 
     return "%s [%s]" % (self.date, self.tag.tag) 

    class Meta: 
     ordering = ["date"] 
     verbose_name_plural = "Sorting Records" 

    def _calculate_kgs_sorted(self): 
     kg_per_bag = self.tag.kgs/self.tag.pieces 
     kgs_sorted = kg_per_bag * self.bags_sorted 
     return (round(kgs_sorted, 2)) 
    kgs_sorted = property(_calculate_kgs_sorted) 

    def _byproduct(self): 
     waste = self.kgs_sorted - self.turnout 
     return (round(waste, 2)) 
    byproduct = property(_byproduct) 

    def _bags_remaining(self): 
     current_set = SortingRecords.objects.values().filter(~Q(id=self.id), tag=self.tag) 
     sorted = [SortingRecords['bags_sorted'] for SortingRecords in current_set if 
        SortingRecords['date'] <= self.date] 
     remaining = self.tag.pieces - sum(sorted) - self.bags_sorted 
     return remaining 
    bags_remaining = property(_bags_remaining) 

EDIT

Он также не с целыми числами, например, так.

django.db.utils.IntegrityError: duplicate key value violates unique constraint "InventoryLogs_purchase_pkey" 
DETAIL: Key (tag)=(9) already exists. 

UDPATE

Так я говорил об этом раньше, но я совершенно забыл. У меня есть два тестовых файла, которые используют одни и те же данные. Просто для ударов я сопоставил первичный ключ в обоих случаях setUpTestData() с другим значением и, конечно же, получил ту же ошибку.

Эти две установки работали отлично бок о бок, прежде чем я добавил больше данных к одному из них. Теперь кажется, что им нужны разные значения. Наверное, вы можете уйти с помощью повторных данных так долго.

+1

почему вы не позволить дб выбрал идентификаторы? –

+0

вы должны поместить свои модели в свой пост. Возможно ли, что 'SortingRecords.tag' должно быть уникальным? –

+0

Hah. Вероятно, это было бы разумно. Я думаю, что я настроил это так изначально, потому что я хотел предотвратить появление нескольких тегов, продуктов и т. Д. Это был самый очевидный способ в то время, но я уверен, что есть более умный способ сделать это. –

ответ

0

я обнаружил проблему, как отмечено в нижней части вопроса.

Из того, что я могу сказать, база данных не понравилась мне, используя дубликаты данных в методах setUpTestData() двух разных тестов. Изменение значений первичного ключа во втором тесте устранило проблему.

0

Введенный логин: DETAIL: Key (product_name)=(Almonds) already exists. Вы подтвердили в своем db?

Чтобы избежать подобных ошибок в будущем, вы должны префикс все тестовую строку данных по test_

+0

Фактически, миндаль существует как название продукта и на некоторое время. Однако это никогда не было проблемой. В любом случае тесты всегда могли успешно работать в TestCase. Кроме того, он дает мне ту же ошибку для тегов, которые не дублируются в базе данных, но (предположительно) создаются с каждым тестом, а затем удаляются. Я должен повторить итерацию, эти таинственные данные фактически не представлены во время тестов. –

+0

Недавно вы использовали 'manage.py migrate'? Я думаю, что он создает моментальный снимок db, который используется в качестве отправной точки для тестов. –

+0

Да, я на самом деле сильно испортил миграцию, поэтому я отменил кучу работы. По-видимому, это усилие не решило проблему. Во всяком случае, я пытался заново создать ошибку для вас, но проблема, похоже, исчезла. Мне не хватает репутации, но спасибо за вашу помощь. –

0

Я думаю, проблема в том, что у вас был метод tearDownClass в TestCase без вызова метода super. Таким образом, тест-центр django потерял функциональные возможности транзакций позади setUpTestData, поэтому он не очищает тестовый db после завершения TestCase.

Проверить предупреждение в Джанго документы здесь: https://docs.djangoproject.com/en/1.10/topics/testing/tools/#django.test.SimpleTestCase.allow_database_queries

 Смежные вопросы

  • Нет связанных вопросов^_^