Набор тестов Django выбрасывает ошибку на поле, которое не загружено: "не удалось преобразовать строку в float".
Следующий простой тест выбрасывает ошибки
ValueError: could not convert string to float: '' Приведенное выше исключение стало непосредственной причиной следующего исключения: ValueError: Поле 'amount' ожидалось число, а получено ''.
from django.test import TestCase from .models import Customer
class TestSetup(TestCase):
def setUp(self):
self.testCustomer = Customer(firstname='Ron', street_name='Somestreet', street_suffix='Dr', street_number='1', phone='111-111-1111', city='Somecity', zipcode='87000')
Поле 'amount' существует, но в модели, которая не загружается ни для одного теста:
class Appointment(models.Model):
amount = models.FloatField(blank=True, default=0.0)
Но на всякий случай, если она инстанцирует эту модель в процессе создания тестовой базы данных, я изменил "default=0.0" на "default='0.0' " (одинарные кавычки вокруг значения). Та же ошибка.
Я начинаю тест с
python manage.py test
из каталога проекта.
Обратное прослеживание показывает, что это происходит при вызове alter_field.
> line 235, in database_forwards
> schema_editor.alter_field(from_model, from_field, to_field) File "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/backends/base/schema.py",
> line 830, in alter_field
> self._alter_field( File "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/backends/postgresql/schema.py",
> line 287, in _alter_field
> super()._alter_field( File "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/backends/base/schema.py",
> line 1025, in _alter_field
> new_default = self.effective_default(new_field) File "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/backends/base/schema.py",
> line 429, in effective_default
> return field.get_db_prep_save(self._effective_default(field), self.connection) File
> "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/models/fields/__init__.py",
> line 954, in get_db_prep_save
> return self.get_db_prep_value(value, connection=connection, prepared=False) File
> "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/models/fields/__init__.py",
> line 947, in get_db_prep_value
> value = self.get_prep_value(value) File "/var/www/ptech/venv/lib/python3.8/site-packages/django/db/models/fields/__init__.py",
> line 1959, in get_prep_value
> raise e.__class__( ValueError: Field 'amount' expected a number but got ''.
Это часть инициализации объекта AlterField
. Поэтому ошибка не в вашем models.py
. Это происходит, когда вы пытаетесь создать объект migrations.AlterField
.
Посмотрите на последнюю строку, чтобы понять, что именно пошло не так.
ValueError: Field 'amount' expected a number but got ''
Поэтому при создании базы данных SQL необходимо, чтобы тип поля соответствовал типу данных, который вы пытаетесь использовать. При создании объекта AlterField
вы присвоили полю 'amount' значение по умолчанию в базе данных в виде строки. Но это FloatField
, поэтому нам нужно иметь значение float.
Вот проблемный код:
# Generated by Django 4.1.3 on 2023-01-10 22:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('giraffe', '0014_appointment_notes'),
]
operations = [
migrations.AlterField(
model_name='appointment',
name='amount',
field=models.FloatField(blank=True, default=''),
),
]
Поле 'amount' по умолчанию получает пустую строку '', как и написано в ошибке. Нам нужен float.
# Generated by Django 4.1.3 on 2023-01-10 22:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('giraffe', '0014_appointment_notes'),
]
operations = [
migrations.AlterField(
model_name='appointment',
name='amount',
field=models.FloatField(blank=True, default=0.0),
),
]
Аналогично, все ваши объекты FloatField
должны иметь значение float по умолчанию при их создании. В следующем переходе после 0014_appointment_notes
, 0015_alter_appointment_amount
, у FloatField
нет значения по умолчанию. Измените его так, чтобы по умолчанию он имел значение float.
Измененный код:
class Migration(migrations.Migration):
dependencies = [
('giraffe', '0015_alter_appointment_amount'),
]
operations = [
migrations.AlterField(
model_name='appointment',
name='amount',
field=models.FloatField(blank=True, default=0.0),
),
]
Возможно, вы уже пытались изменить это, но ничего не вышло.
Как сказал Рон в разделе комментариев, решение состоит в том, чтобы переустановить миграции. Это может оказаться сложным для вас, если вы не знакомы с тем, как работают миграции в Django. В каждой папке, где находится Django-приложение, есть скрытая папка, то есть в каждой папке есть свой models.py
. Это файл кэша, который создается после вызова makemigrations
, чтобы ускорить выполнение команды migrate
, потому что обычно сами модели не изменяются. Добавление новой модели обновляет этот кэш, а изменение модели - нет. Убедитесь, что вы удалили каждую скрытую папку .migrations
, а также все файлы и папки в ней с помощью команды Unix.
rm -r .migrations
При выполнении команды migrate
теперь автоматически выполняется команда makemigrations
, так как папка .migrations
не существует и теперь должна быть создана для переноса базы данных.
Предыдущие модели, которые вы пытались удалить, больше не будут присутствовать при миграции базы данных, если вы убедитесь, что мигрируете в свежую базу данных, в которой еще не записана предыдущая модель.
Первоначальный вопрос, который был опубликован, не о том, как сбросить миграции, и НЕ является дубликатом других вопросов SO о том, как сбросить миграции. Речь идет о более глубоком понимании любых глубинных механизмов, которые могут позволить оригинальным миграциям выполняться безупречно, несмотря на ошибки в коде, проиллюстрированные в вопросе, а затем провалиться в юнит-тесте. Все 76+ миграций в истории отображаются как успешно выполненные в списке 'showmigrations', но теперь (должным образом) терпят неудачу при создании базы данных для модульного тестирования.
Надеемся, что вопрос можно решить, не делая сброс миграции.
Это как если бы данные не применялись к моделям, определенным в исходных миграциях - как если бы слова определений полей модели принимались на веру - но тогда проблемы все равно могут возникнуть при реальной инсталляции в тесте. Однако я не могу представить, чтобы такая дыра существовала в Django. В любом случае, сценарий, описанный в вопросе, является хорошим аргументом в пользу TDD.
Попутно хотелось, чтобы нюансы, которые могут возникнуть в связи с последовательной, взаимозависимой природой миграций, стали более понятными для новых пользователей Django, которые могут прочитать это в будущем, то есть это немного сложнее, чем "просто исправить одну миграцию".
Но в итоге независимо от того, какие механизмы или действия привели к проблеме, необходимо выполнить сброс миграций и сгенерировать новую начальную миграцию. Рекомендуемый метод для этого приведен в Как сбросить миграции .