Миграция необязательного поля в обязательное
В этой статье будем разбирать сложные случаи при миграциях. Если вы не знаете, что это, вот статья о миграциях.
В процессе создания миграций вы можете увидеть сообщение ниже:
$ python3 manage.py makemigrations You are trying to add a non-nullable field `title` to post without a default; we can t do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option:
В этой статье мы разберёмся, что это значит и что с этим делать. Рассматривать ситуацию будем на примере сайта-блога. На сайте есть модель Post с единственным полем для текста статьи:
from django.db import models class Post(models.Model): text = models.TextField()
Теперь добавляем поле для заголовка статьи ( title ):
from django.db import models class Post(models.Model): text = models.TextField() title = models.CharField()
После миграций база данных будет выглядеть как в таблице снизу:
При запуске makemigrations возникнет ошибка You are trying to add a non-nullable field without a default — вы пытаетесь добавить обязательное к заполнению поле title , но не указали для него значение по умолчанию.
Для новых статей база данных будет требовать заполнения обоих полей: title и text , но не понятно что делать с теми записями, которые уже есть в базе. Им нужно добавить title , но тогда это поле окажется пустым. База данных такого не разрешит.
Способ первый: Задать значение по умолчанию
Чтобы база данных сама заполнила пустые ячейки можно задать значение по умолчанию. Это можно сделать двумя способами.
Одноразовое значение по умолчанию
Это значение проставится всем незаполненным полям во время миграции. makemigrations предложит вам выбор из двух пунктов, выберите первый. Вам выдадут поле для ввода и там вы можете указать значение, которое проставится во все незаполненные ячейки таблицы. Мы указали строку «default title» :
Теперь создастся файл миграции. В нём будет зашита эта строка: «default title» . После запуска команды migrate база данных будет выглядеть как таблица:
Многоразовое значение по умолчанию
Это значение задаёт title не только для старых постов, которые уже лежат в БД, но и для новых с пустым заголовком. Оно задаётся в models.py с помощью аргумента default :
from django.db import models class Post(models.Model): text = models.TextField() title = models.CharField(default="default title")
Теперь запуск makemigrations пройдёт успешно и без лишних вопросов. Более того, если при добавлении в базу нового поста вы не укажете title , то он автоматически примет значение по умолчанию:
>>> post = Post.objects.create(text="Текст статьи") >>> post.text Текст статьи >>> post.title default title
Способ второй: Разрешить полю быть пустым
Поле не заполнено, но, может, и фиг с ним? Некоторым полям и не нужно всегда быть заполненными. В такой ситуации вам поможет null=True и blank=True . Как это работает мы увидим, если добавим ещё одно поле published_at , в котором будет храниться время и дата публикации поста:
from django.db import models class Post(models.Model): text = models.TextField() title = models.CharField(blank=True) published_at = models.DateTimeField(null=True)
Что такое null=True
Это свойство позволяет базе данных оставлять поле published_at пустым. Это пригодится, если мы добавили пост в базу данных, но ещё не опубликовали. Чтобы опубликовать пост, достаточно добавить ему текущее время в поле published_at :
>>> some_post = Post.objects.create(text="Текст этого поста") >>> some_post.published_at None # Сейчас не опубликован, в поле лежит None >>> some_post.published_at = timezone.now() >>> some_post.save() # Теперь опубликован, в поле лежит текущее время
Крайне не рекомендуется применять это свойство к строковым полям (CharField и TextField), так как вместо None в них лучше класть пустую строку. Если указать для строкового поля null=True , то оно может содержать два возможных “пустых” значения: None и пустую строку. Два варианта “пустых” значений создадут путаницу, поэтому в Django разрешают только пустую строку. В этом вам поможет blank=True .
Что такое blank=True
Если null=True управляет поведением базы данных, то blank=True настраивает поведение админки и Django-форм. Если для поля published_at не указан blank=True , то админка запретит нам сохранить пост, и потребует заполнить поле published_at . Да, админка будет сопротивляться даже если у published_at уже есть null=True .
Способ третий: Если не хочется ставить default или null
Иногда значение по умолчанию или null=True ставить не хочется. Например, в ситуации, когда у вас есть модель поста и модель комментария, и вы решили их связать. Изначально комментария есть только сам его текст:
from django.db import models class Post(models.Model): text = models.TextField() title = models.CharField(blank=True) class Comment(models.Model): text = models.TextField()
Теперь вы захотели добавить комментарию ссылку на пост, чтобы знать, к какому посту писался этот комментарий:
class Comment(models.Model): text = models.TextField() post = models.ForeignKey(Post, on_delete=models.CASCADE)
При запуске makemigrations возникнет ошибка из начала статьи: You are trying to add a non-nullable field without a default. Проблему можно обойти через добавление атрибутов null=True или default , но это неправильно, ведь на самом деле у комментария поле post не должно пустовать ни при каких условиях.
Эта проблема возникла из-за того что связь не была продумана на этапе проектирования моделей. Обычно все связи модели с другими стараются продумать сразу, до первой миграции.
В такой ситуации есть 2 пути: простой, но варварский и хороший, но сложный.
Простой, но варварский способ решения проблемы
Если вы только проектируете БД и в ней нет никаких важных данных, то самое простое — это удалить все миграции и саму БД. После этого смело создавайте миграции и мигрируйтесь. Получится так, будто вы с самого начала создали модель с ForeignKey , а значит и пустым это поле никогда не было.
Если вы работаете вместе с другими программистами, никогда так не делайте, потому что это вызовет у них большие проблемы: им тоже придётся удалять свои базы данных. Но если вы в проекте один, а в базе данных нет ничего важного — это хороший способ быстро всё исправить.
Сложный, но правильный способ решения проблемы
Если вы работаете с другими программистами и/или в БД уже есть важные данные, то удалять базу — не вариант. Тогда делается следующее:
1. Создайте новую модель PostComment рядом с существующей. В новой модели укажите связь — ForeignKey :
class Comment(models.Model): text = models.TextField() class PostComment(models.Model): text = models.TextField() post = models.ForeignKey(Post, on_delete=models.CASCADE)
2. Сделайте и примените миграцию с помощью makemigrations и migrate .
3. Скопируйте данные из Comment в PostComment с помощью дата-миграции. Это специальная миграция, которая просто запустит тот код Python, который вы сами напишете. Вам придётся придумать способ как перенести старые данные на новую модель.
4. Запустите дата-миграцию, чтобы скопировать данные из Comment в PostComment .
5. Теперь, когда все важные данные спасены, можно удалить модель Comment . После миграции с удалением старой модели можно переименовать PostComment к старому названию Comment и провести ещё одну миграцию.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Как сделать поле необязательным django
Свойство label позволяет установить текстовую метку, которая отображается рядом с полей. По умолчанию она отображает название самого поля с большой буквы. Например:
from django import forms class UserForm(forms.Form): name = forms.CharField(label="Имя") age = forms.IntegerField(label="Возраст")
Эта форма сформирует следующие поля ввода:

widget
Параметр widget позволяет задать виджет, который будет использоваться для генерации разметки html:
from django import forms class UserForm(forms.Form): name = forms.CharField(label="Имя") comment = forms.CharField(label="Комментарий", widget=forms.Textarea)
По умолчанию поле CharField использует виджет forms.widgets.TextInput , который создает однострочное текстовое поле. Однако если нам надо создать многострочное текстовое поле, то необходимо воспользоваться виджетом forms.Textarea :

Значения по умолчанию
С помощью параметра initial можно установить значения по умолчанию.
from django import forms class UserForm(forms.Form): name = forms.CharField(initial="undefined") age = forms.IntegerField(initial=18)
Порядок полей
Поля ввода отображаются на веб-странице в том порядке, в котором они определены в классе формы. С помощью свойства field_order можно переопределить порядок, как в классе формы:
class UserForm(forms.Form): name = forms.CharField() age = forms.IntegerField() field_order = ["age", "name"]
Так и при определении объекта формы в представлении:
def index(request): userform = UserForm(field_order = ["age", "name"]) return render(request, "index.html", )
help_text
Параметр help_text устанавливает подсказку рядом с полем ввода:
from django import forms class UserForm(forms.Form): name = forms.CharField(help_text="Введите свое имя") age = forms.IntegerField(help_text="Введите свой возраст")

Настройка вида формы
С помощью специальных методов можно настроить общее отображение формы:
- as_table() : отображение в виде таблицы
- as_ul() : отображение в виде списка
- as_p() : каждое поле формы отобажается в отдельном параграфе
- as_div() : каждое поле формы отобажается в отдельном блоке div
Как сделать сделать поле ForeignKey (связь) не обязательным?
как сделать чтобы при добавлении записи можно было как выбирать что-то из связывающей таблицы, так и оставить его пустым если не нужно?
- Вопрос задан более трёх лет назад
- 521 просмотр
Комментировать
Решения вопроса 2
Че-то там программирую
sone_field = models.ForeignKey(SomeModel,blank = True, null = True)
Ответ написан более трёх лет назад
zkweb @zkweb Автор вопроса

Владислав Скляр @VladSkliar
subject = models.ForeignKey(subjects, blank=True, null=True)
Ответ написан более трёх лет назад
Django Book: изменение полей на необязательные
После того как вы немного поработаете с панелью управления вы, возможно, заметите некоторые ограничения, например – форма редактирования записи требует, что бы все поля были заполнены, хотя в некоторых случаях вы хотели бы оставить их пустыми. Например, вы хотите что бы поле email модели Authors было не обязательным для заполнения (опциональным).
Что бы указать, что поле email является опциональным – отредактируйте модель Author (которая, как вы должны помнить из предыдущей главы, описана в файле mysite/books/models.py ) и добавьте blank=True к полю email , примерно так:
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True)
Это укажет Django, что пустое значение допустимо для поля email у авторов. По умолчанию – у всех полей установлено значение blank=False .
Тут происходит кое-что интересное, на чём стоит остановиться. До сих пор, за исключением метода __unicode__() , наши модели использовались как описания таблиц базы данных в виде Python-выражений для операторов SQL CREATE TABLE . Добавив blank=True мы расширили возможности нашего метода. Теперь класс модели представляет собой более полную коллекцию описаний того, что такое объекты Author и что они могут выполнять.
После того как вы добавили blank=True – перезагрузите страницу “Add author” и вы увидите, что поле Email больше не выделено жирным шрифтом. Теперь вы можете добавить нового автора без указания адреса почты – сообщений “This field is required” больше не будет.
Изменение даты и числовых полей
Описанный пример с blank=True подойдёт и для полей даты и чисел, но тут требуется дополнительное пояснение и действие.
В SQL есть специальный способ для определения пустых значений – это NULL . NULL может означать “неизвестно” или “неверное значение”, или другое, в зависимости от приложения.
В SQL значение NULL отличается от пустой строки, так как же специальный объект None в Python отличается от пустой строки (“”). Это значит, что некоторые символьные поля (такие как VARCHAR ) могут содержать значения и NULL и пустые строки.
Это может вызвать нежелательную двусмысленность и путаницу: “Почему эта запись имеет значение NULL, а другая – пустую строку?” и “Как мне получить все записи с пустыми значениями – должен ли я искать и NULL и пустые строки, или только пустые строки?”
Что бы избежать такой путаницы – сгенерированный Django запрос CREATE TABLE (который мы рассматривали в предыдущей главе) добавляет явное указание NOT NULL для описания каждой колонки. Например, вот запрос для нашей модели Author :
CREATE TABLE `books_author` ( `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `first_name` varchar(30) NOT NULL, `last_name` varchar(40) NOT NULL, `email` varchar(75) NOT NULL ) ;
Как правило это оптимальное решение для ваших приложений, которое избавит вас от головной боли с несогласованностью данных в базе данных, и оно работает со всеми частями Django, такими как панель управления, которая вставляет пустые строки (а не значение NULL ), когда вы оставляете пустое поле при добавлении или редактировании записи.
Однако, имеются исключения, которые касаются полей типа даты, времени и чисел в вашей базе данных – они не принимают пустые строки в качестве корректных значений. Если вы попробуете добавить пустую строку в колонку с датой или цифрами – вы скорее всего получите ошибку базы данных, в зависимости от типа сервера баз данных (PostgreSQL вернёт ошибку, MySQL – может вернуть – а может и нет, в зависимости от используемой версии, времени для и фазы луны). В таком случае – NULL единственный вариант что бы задать пустое значение. В моделях Django вы можете указать, что использование NULL разрешено, добавив строку null=True к нужному полю.
Так вот, если вы хотите разрешить использовать пустые поля для даты (т.е. DateField , TimeField , DateTimeField ) или полей с числами (т.е. IntegerField , DecimalField , FloatField ) – вам требуется указать и null=True и blank=True .
Например, давайте изменить нашу модель Book , что бы разрешить оставлять поле publication_date незаполненным:
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField(blank=True, null=True) def __unicode__(self): return self.title
Добавление null=True – более сложный процесс, чем blank=True так как требует изменение синтаксиса базы данных – то есть, оно изменят запрос CREATE TABLE , убирая NOT NULL из описания поля publication_date . Что бы применить это изменение – вам необходимо обновить базу данных.
По многим причинам Django не выполняет изменения в базе автоматически, поэтому вам необходимо выполнить ALTER TABLE самостоятельно, когда вы решите внести такие изменения. Вспомните, что вы можете использовать manage.py dbshell , что бы подключиться к вашей базе из консоли сервера баз данных. Вот как вы можете выполнить ALTER TABLE в данном примере (для MySQL/MariaDB):
MariaDB [django_example1]> ALTER TABLE books_book MODIFY publication_date date; Query OK, 0 rows affected (0.36 sec)
Или для PostgreSQL:
ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL;
Мы рассмотрим изменения схемы баз данных подробнее далее в нашей книге.
Возвращаясь к панели управления Django – теперь в форме редактирования “Add book” поле даты публикации можно оставлять пустым.