Архив рубрики: Python

Исключения в Питоне

Поговорим об исключениях.

Всё нижеизложенное относится к Python 3.3, хотя отчасти справедливо и для более ранних версий.

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

Рассмотрим простейший пример: открытие файла. Если всё нормально — open(filename, 'r') возвращает объект этого самого файла, с которым можно делать всякие полезные вещи: читать из него данные и т.д.

Если файл не может быть открыт — выбрасывается исключение:

try:
f = open(filename, 'r')
try:
print(f.read())
finally:
f.close()
except OSError as ex:
print("Cannot process file", filename, ": Error is", ex)

Открываем файл и печатаем его содержимое.

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

Если файл открыт — читаем его через f.read(). Этот вызов тоже может выбросить исключение, но файл закрывать всё равно нужно. Поэтому необходим блок finally: f.close() должен быть вызван даже если f.read() сломался. В этом месте удобней было бы воспользоваться конструкцией with но мы же сейчас говорим об исключениях а не о контекстных менеджерах, верно?

Исключения из обоих мест попадут в except OSError, где можно будет что-то сделать с ошибкой.

Питон делает явный выбор в пользу исключений перед возвратом кода ошибки в своём ядре и стандартной библиотеке. Прикладному программисту стоит придерживаться этих же правил.

Введение закончено. Теперь сконцентрируемся на том что происходит в except.

Типы исключений

Все исключения составляют иерархию с простым наследованием. Вот простой небольшой кусочек от довольно обширного набора исключений ядра Питона:

BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- AssertionError
+-- AttributeError
+-- BufferError

Самый базовый класс — BaseException. Он и его простые потомки (SystemExit, KeyboardInterrupt, GeneratorExit) не предназначены для перехвата обыкновенным программистом — только Питон и редкие библиотеки должны работать с этими типами. Нарушение правила ведет, например, к тому что программу невозможно корректно завершить — что совсем не хорошо.

Также не нужно перехватывать все исключения:

try:
...
except:
...

работает как

try:
...
except BaseException:
...

Всё, что может быть нужно программисту — это Exception и унаследованные от него классы.

Вообще-то лучше ловить как можно более конкретные классы исключений. Например, в случае с файлом это OSError или даже может быть FileNotFoundError. Таким образом мы не перехватим AttributeErrorили ValueError, которые в этом примере означали бы ошибку или опечатку программиста.

Кстати, обратите внимание: StopIteration порожден от Exception а GeneratorExit от BaseException. Подробности, почему сделано именно так, можно найти в PEP 342.

Цепочки исключений

Прочитав предыдущую главку все прониклись необходимостью указывать правильный класс исключений и пообещали никогда не использовать BaseException.

Идем дальше. Следующий пример:

try:
user = get_user_from_db(login)
except DBError as ex:
raise UserNotFoundError(login) from ex

Получаем пользователя из базы данных чтобы что-то потом с ним сделать. get_user_from_db может выбросить ошибку базы данных. Для нас это скорее всего означает что такого пользователя нет. Но для логики приложения полезней наш собственный тип UserNotFoundError с указанием логина проблемного пользователя, а не обезличенная ошибка БД — что мы и выбрасываем в обработчике исключения.

Проблема в том, что программисту часто хотелось бы знать, а почему это пользователь не найден. Например, чтобы сохранить в логах для дальнейшего разбирательства.

Для таких целей служит конструкция raise ... from ....

По PEP 3134 у объекта исключения имеется несколько обязательных атрибутов.

В первую очередь это __traceback__, содержащий кусочек стека от места возникновения исключения до места его обработки.

Затем — __context__. Если исключение было создано в ходе обработки другого исключения (выброшено из except блока) — __context__будет содержать то самое породившее исключение. Которое, в свою очередь тоже может иметь установленный __context__. Этот атрибут равен None если наше исключение — самое первое и н
е имеет предшественников.

__context__ устанавливается автоматически.

В отличие от контекста __cause__ устанавливается только если исключение было выброшено конструкцией raise ... from ... и равно значению from.

Если исключение выбрасывалось простым raise ... то __cause__ будет равно None в то время как __context__ всегда будет содержать породившее исключение если оно существует.

Для вывода исключения со всей информацией служит набор функций из модуля traceback, например traceback.print_exc().

И тут тоже есть проблема: печатается либо явная цепочка если есть установленный __cause__ или неявная, тогда используется __context__.

Иногда программисту может быть нужно отбросить породившие исключения как не имеющие смысла при выводе traceback. Для этого появилась форма записи

raise exc from None

PEP 409 и PEP 415 рассказывают как это работает:

У исключения всегда есть атрибут __supress_context__. По умолчанию он равен False.

Конструкция raise ... from ... записывает fromв __cause__ и устанавливает __supress_context__ в True.

Тогда семейство функций traceback.print_exc() печатают цепочку если явно указан (не равен None) __cause__ или есть __context__ и при этом __supress_context__ равен False.

Изложение получилось несколько длинным, но сократить текст без потери смысла у меня не вышло.

Семейство OSError

Последняя проблема о которой хотелось бы рассказать — это типы исключений порожденные вызовами операционной системы.

До Python 3.3 существовало много разных типов таких исключений: os.error, socket.error, IOError, WindowsError, select.errorи т.д.

Это приводило к тому, что приходилось указывать несколько типов обрабатываемых исключений одновременно:

try:
do_something()
except (os.error, IOError) as ex:
pass

Ситуация на самом деле была еще хуже: очень легко забыть указать еще одно нужное исключение, которое может внезапно прилететь. Дело в том что исключения операционной системы часто никак не проявляют себя при разработке. На машине программиста всё работает отлично и он не подозревает о возможных проблемах. Как только программа выходит в production пользователь вдруг ловит что-то неожиданное и программа аварийно завершается. Все опечалены.

Проблема решена в PEP 3151: весь этот зоопарк теперь является псевдонимами для OSError. Т.е. пишите OSError и не ошибетесь (прочие имена оставлены для обратной совместимости и облегчения портирования кода на новую версию).

Давайте рассмотрим ещё один аспект исключений, порожденных операционной системой.

У OSError есть атрибут errno, который содержит код ошибки (список всех возможных символьных констант для ошибок можно посмотреть в модуле errno).

Открываем файл, получаем OSError в ответ. Раньше мы должны были анализировать ex.errno чтобы понять, отчего произошла ошибка: может файла нет на диске, а может нет прав на запись — это разные коды ошибок (ENOENT если файла нет и EACCES или EPERM если нет прав).

Приходилось строить конструкцию вроде следующей:

try:
f = open(filename)
except OSError as ex:
if ex.errno == errno.ENOENT:
handle_file_not_found(filename)
elif ex.errno in (errno.EACCES, errno.EPERM):
handle_no_perm(filename)
else:
raise # обязательно выбрасывать не обработанные коды ошибки

Теперь иерархия расширилась. Привожу полный список наследников OSError:

OSError
+-- BlockingIOError
+-- ChildProcessError
+-- ConnectionError
| +-- BrokenPipeError
| +-- ConnectionAbortedError
| +-- ConnectionRefusedError
| +-- ConnectionResetError
+-- FileExistsError
+-- FileNotFoundError
+-- InterruptedError
+-- IsADirectoryError
+-- NotADirectoryError
+-- PermissionError
+-- ProcessLookupError
+-- TimeoutError

Наш пример можем переписать как:

try:
f = open(filename)
except FileNotFound as ex:
handle_file_not_found(filename)
except PermissionError as ex:
handle_no_perm(filename)

Гораздо проще и понятней, правда? И меньше мест, где программист может ошибиться.

Заключение

Переходите на Python 3.3, если можете. Он хороший и облегчает жизнь.

Новые плюшки в вопросе, касающемся исключений, я показал.

Если использовать новый питон не позволяют обстоятельства — пишите на чём есть, но помните как правильно это делать.

Автор: Andrew Svetlov

PyBooklet: создание PDF с двумя страницами на листе для печати (Перевод)

Интересная идея.
PyBooklet может быть полезным дополнением к моей утилите PDFBook (части xtopdf), которое может создавать книжки в PDF из набора текстовых файлов, используя при этом меньшее количество бумаги, что хорошо отразится на окружающей среде.

Автор: Ishayahu Lastov

Документация South — Перевод. Часть 3: Дополнительные команды и миграция данных

Последовательная работа с миграцией

Иногда Вы можете обнаружить, что изменения в модели требуют некоторых улучшений. Предположим, Вы определили модель:
 class Group(models.Model):
name = models.TextField(verbose_name="Name")
facebook_page__id = models.CharField(max_length=255)

и вы создали и применили миграцию:
 ./manage.py schemamigration southtut --auto
./manage.py migrate southtut

После чего Вы обнаружили, что: name на самом деле должно быть CharField, а не TextField, а facebook_page__id содержит двойное подчёркивание, а не одинарное, как Вы хотели. Тогда можно исправить эти проблемы и выполнить:
  ./manage.py schemamigration southtut --auto --update
+ Added model southtut.Group
Migration to be updated, 0026_auto__add_group, is already applied, rolling it back now...
previous_migration: 0025_auto__foo (applied: 2012-05-25 21:20:47)
Running migrations for southtut:
- Migrating backwards to just after 0025_auto__foo.
< partner:0026_auto__add_group
Updated 0026_auto__add_group.py. You can now apply this migration with: ./manage.py migrate southtut

Что произошло? South удалил последнюю миграцию, которая создала модель, но в которой были ошибки, и заменил её новой миграцией, которая уже не содержит этих ошибок.
Стоит так же обратить внимание на то, что та миграция, которая была уже применена, была автоматически откачена назад. Вы можете теперь применить последнюю версию миграции чтобы получить правильный вариант модели:
 ./manage.py migrate southtut

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

Просмотр текущих миграций

Часто бывает полезно посмотреть, какие миграции были применены на данный момент и какие доступны для использования. Для этой причины есть команда ./manage.py migrate —list. Вот результат выполнения этой команды для нашего проекта:
 $ ./manage.py migrate --list

southtut
(*) 0001_initial
(*) 0002_auto__add_field_knight_dances_whenever_able
(*) 0003_auto__add_field_knight_shrubberies
(*) 0004_auto__add_unique_knight_name
Наличие звёздочки (*) говорит о том, что миграция была применена, а отсутствие — что ещё нет. Соответственно, чтобы увидеть только те миграции, которые были применены, воспользуйтесь командой ./manage.py migrate —list | grep -v «*»
Если у Вас есть несколько приложений, работающих при помощи миграций, то можно задать и имя приложения для просмотра миграций только для него.

Перенос данных

До сих пор мы говорили только о «миграции схемы», то есть об изменении колонок и индексов. Но есть и другой тип миграции — «миграция данных».
Миграция данных используется для изменения данных, сохранённых в вашей БД для приведения их в соответствие с новой схемой или предоставления новых возможностей. Например, если Вы сохраняете пароль в виде обычного текста (если это и правда так, то немедленно исправьте это. НЕМЕДЛЕННО!!!) и теперь хотите хранить его в виде солёного хеша, то Вам могут потребоваться эти три шага (причём каждый шаг — это одна миграция):
  • Создаём две новые колонки: password_salt и password_hash (миграция схемы)
  • Используя содержимое колонки password вычисляем соль и хеш для каждого пользователя (миграция данных)
  • Удаляем старую колонку password (миграция схемы)
Как провести первую и последнюю миграцию Вы уже и сами знаете: изменяете models.py и запускаете ./manage.py schemamigration —auto myapp. Главное не удаляйте сразу колонку password, так как нам понадобятся данные из неё для заполнения двух новых колонок (всегда, всегда делайте бакуп вашей БД перед тем, как сделать любое изменение, которое может попортить данные. Потому что однажды именно так и будет).
Давайте возьмём реальный пример. Создадим новое приложение под именем southtut2. Добавим его в INSTALLED_APS и создадим его модель:
 from django.db import models

class User(models.Model):

username = models.CharField(max_length=255)
password = models.CharField(max_length=60)
name = models.TextField()
Сде

Документация South — Перевод. Часть 2. Более сложные изменения схемы

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

Значение по умолчанию

Во-первых, давайте займёмся более хитрыми типами колонок. В прошлой части мы добавили BooleanField в таблицу — такую операцию БД легко обработать, так как у него есть значение по умолчанию (False). Это значение и будет установлено для всех уже имеющихся записей в БД.
Однако для некоторых колонок нет предопределённого значения по умолчанию. Если в колонке может присутствовать значение null, то есть null = True, тогда уже существующие записи получат значение null. В противном же случае, если колонка NOT NULL (null = False, свойство колонки по умолчанию), то в таком случае нет значения, которое БД могла бы присвоить уже имеющимся записям, так что Вы не сможете просто добавить колонку (однако, некоторые БД могут позволить Вам добавить колонку в пустую таблицу).
Если South обнаружит такую ситуацию, он тут же спросит у Вас что делать. Давайте посмотрим на это.
Для начала изменим нашу модель добавив поле, которое не имеет значения по умолчанию и не может быть равно null:
 from django.db import models

class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
Теперь давайте попробуем сделать миграцию на эту модель:
 ./manage.py schemamigration southtut --auto
? The field 'Knight.shrubberies' does not have a default specified, yet is NOT NULL.
? Since you are adding or removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice:
South предлагает Вам два варианта: если вы выбираете вариант 1, не будет сделано ничего и Вам надо будет отредактировать models.py  и добавить значение по умолчанию для поля.
Если же Вы выбираете вариант 2, то Вы должны будете ввести в приглашение интерпретатора Python значение по умолчанию для этой миграции. Это значение будет использоваться только для тех записей, которые присутствуют в таблице на данный момент, так что это хороший вариант, если Вы не хотите, чтобы это поле имело значение по умолчанию.
Мы выберем вариант 2 и в качестве значения по умолчанию используем 0 (так как это, в конце концов, числовое поле):
  ? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 0
+ Added field shrubberies on southtut.Knight
Created 0003_auto__add_field_knight_shrubberies.py. You can now apply this migration with: ./manage.py migrate southtut
Если Вы посмотрите на сгенерированную миграцию, то Вы увидите, что для нового поля установлено значение по умолчанию, так что жаловаться БД не будет. Завершим нашу миграцию:
$ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0003_auto__add_field_knight_shrubberies.
> southtut:0003_auto__add_field_knight_shrubberies
- Loading initial data for southtut.

Уникальные значения

Кроме обнаружения новых полей (или удаления старых) South так же может обнаружить большее количество изменений в самих полях, в том числе изменение их атрибута unique.
Во-первых, давайте изменим схему нашего Knigts, чтобы он имел уникальные поля:
 from django.db import models

class Knight(models.Model):
name = models.CharField(max_length=100, unique=True)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()
shrubberies = models.IntegerField(null=False)
Теперь запустим создание автоматической миграции:
 $ ./manage.py schemamigration --auto southtut
+ Added unique constraint for ['name'] on southtut.Knight
Created 0004_auto__add_unique_knight_name.py. You can now apply this migration with: ./manage.py migrate southtut
Как Вы можете видеть, он определил появление нового ограничения на поле name. Вы можете теперь применить его:
 $ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0004_auto__add_unique_knight_name.
> southtut:0004_auto__add_unique_knight_name
- Loading initial data for southtut.
Кроме того, South может обнаружить появление или изменение unique_together в подклассе Meta вашей модели.

Поля ManyToMany

South должен автоматически определять поля ManyToMany. Когда Вы добавляете такое поле, South создаёт таблицу, представляющую это отношение, а когда удаляете — South её удаляет.
Единственное исключение — through model, т. е. если Вы используете опцию through. Так как таблица для этой модели уже создана, South не будет делать ничего с этими полями ManyToMany.

Пользовательские поля

Если Вы внимательнее посмотрите на файлы миграции, Вы увидите, что South хранит определения полей сохраняя их класс и аргументы, необходимые для передачи в конструктор поля.
Так как в Python нет способа получить аргументы, используемые в классе конструктора, напрямую, South использует нечто под названием «model introspector» для того, чтобы выяснить, какие аргументы были переданы в конструктор поля. Мы узнаем, что хранится в переменных, которым присваивается значение аргументов и таким образом можем реконструировать сами переданные значения.
Поскольку пользовательские поля (написанные Вами или полученные от сторонних приложений) совсем другие, South не может получить значения для их аргументов без дополнительной помощи. Так что если Вы хотите добавить / изменить / удалить пользовательское поле, South понадобится ваша помощь и он попросит Вас задать правила для этих полей. Эта тема будет затронута в разделе «пользовательские поля».

Ещё?

South поддерживает большинство операций, с которыми Вы будете встречаться каждый день. Если Вам интересно — то есть полный список автоопределяемых изменений.
Теперь, скорее всего, Вам захочется прочитать третью часть руководства.

Автор: Ishayahu Lastov

Документация South — Перевод. Установка

Текущий релиз South — 0.7.6. Но документация написана для 0.7.5, так что имейте это ввиду.
Есть несколько способов установить South:

  • Используя easy_install или pip, что рекомендуется, если Вы хотите получить стабильный релиз
  • Используя Mercurial, что рекомендуется если Вы хотите получить самый последний релиз
  • Скачав архив, если у Вас нет ни easy_install, ни Mercurial
Некоторые дистрибутивы Linux (и UNIX, например, FreeBSD — прим. переводчика) уже содержат South в их репозитории, например, если Вы используете не стабильный Debian, то Вы можете использовать команду apt-get install python-django-south, а на Fedora — yum install Django-south. Обратите внимание, что таким образом Вы можете получить старую версию, так что проверьте сначала номер версии в репозитории.
South <=0.7.3 должен работать с версиями Django от 0.97-pre до 1.1
South >=0.7.4 должен работать с версиями Django начиная от версии 1.2
Пожалуйста, проверьте совместимость с вашей версией в заметках о релизе, чтобы быть в курсе последних изменений.

Установка при помощи easy_install

Если у Вас есть easy_install, то просто выполните команду easy_install South. Если у Вас уже установлена предыдущая версия и Вы просто хотите её обновить — используйте команду easy_install -U South. Вот и всё, что Вам нужно для установки пакета. Теперь Вам следует настроить Django (см. последний раздел).

Установка при помощи Mercurial

Вы можете так же воспользоваться возможностью установки напрямую из нашего репозитория Mercurial, что позволит Вам получить все обновления и исправления ошибок сразу по их выходу. Для этого Вам нужен установленный Mercurial. На большинстве систем соответствующий пакет называется mercurial; для Windows и OSX пакет можно скачать с http://mercurial.berkwood.com.
Убедитесь, что Вы находитесь в той папке, куда и хотите получить south. После этого выполните команду hg clone http://bitbucket.org/andrewgodwin/south/ Для обновления имеющейся версии выполните две команды:
hg pull
hg up -C tip
(Вместо использования tip Вы так же можете использовать тег stableish, который в ручную устанавливается на стабильные коммиты, или же выбрать тег с номером версии)
После этого переходите к разделу «установка из папки»

Использование скачиваемого архива

Если Вы питаете отвращение к использованию Mercurial и не имеете под рукой easy_install, тогда Вы можете установить South из нашего .tar.gz файла.
Во-первых, скачайте архив со страницы релизов и извлеките его в папку south. После этого следуйте инструкциям из раздела «установка из папки».

Установка из папки

Если Вы получили копию South при помощи Mercurial или скачав и распаковав архив, то теперь Вам надо установить South на вашу систему. Попробуйте выполнить эту команду:
python setup.py develop
Если она не сработает, значит на вашей системе не доступны setuptools. Тогда либо установите их, либо выполните команду
python setup.py install
Обратите внимание, что при использовании develop Вы будете запускать установленную версию из той папки, которую Вы создали, тогда как при использовании install Вы скопируете все файлы в папку site-packages вашей версии Python, а значит при обновлении Вам снова понадобится использовать команду install.
Вы так же можете установить South локально, только для одного проекта, либо включив его в этот проект и изменив sys.path в файле настрое, либо (предпочтительнее) используя virtualenv, pip и requirements.txt. Рассказ о том, как это сделать выходит за рамки этой документации, то его не тяжело найти в сети.
После установки Вам надо будет настроить вашу версию Django.

Настройка Django

После того, как Вы установили South на вашей системе, Вам осталось только настроить Django, чтобы он его использовал. Сделать это просто — достаточно отредактировать setti
ngs.py, добавив 'south' в конец раздела INSATALLED_APPS.
Если Django не торопится подхватывать South, то проверьте что Вы нигде не переопределили INSTALLED_APS, и что Вы можете выполнить import south в ./manage.py shell без ошибок.
После того, как South добавлен, Вам надо запустить ./manage.py syncdb чтобы South создал таблицу для слежения за изменениями (South не использует миграции для своих моделей по некоторым причинам).
Теперь South загружен в ваш проект и готов к использованию. Так что перейдём к более интересным занятиям.

Автор: Ishayahu Lastov

Документация South — Перевод. Часть 1: Основы

Добро пожаловать в руководство. Тут мы постараемся рассказать об основах использования South и дать несколько общих советов как ещё его можно использовать.
Если Вы никогда не слышали о библиотеках для миграции, тогда сперва прочитайте раздел «что такое миграция«. Это поможет Вам лучше понять для чего предназначены South и его альтернативы, такие как django-evolution.
В этом руководстве мы предполагаем, что South у Вас уже установлен и настроен, иначе смотрите раздел «установка«.

Приступаем

В этом руководстве мы рассмотрим процесс миграции на новое приложение. Вопросы изменения схемы существующего приложения мы рассмотрим позже.
Во-первых, надо отметить, что South работает с каждым приложением по отдельности. Миграции сохраняются в коде приложения (хотя их можно хранить там, где Вы захотите — см «SOUTH_MIGRATION_MODULES»). Если для приложения не определена никакая миграция, то South будет его просто игнорировать и оно будет использовать syncdb.
Так что возьмём проект для работы (или сделаем новое, определив БД и прочие настройки) и создадим новое приложение:
./manage.py startapp southtut
Как и предполагается, эта команда создаст новую папку southtut. Во-первых, добавьте это приложение в INTALLED_APPD, после чего откройте созданный southtut/models.py и создайте новую модель:

 from django.db import models

class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()

Просто, да? Но теперь, вместо того, чтобы запустить syncdb для создания таблицы для модели в нашей БД, мы создадим для этого миграцию.

Первая миграция

В South есть несколько способов создать миграцию. Некоторые автоматические, некоторые делаются ручками. Как стандартный пользователь, Вы скорее всего будете использовать два автоматических способа — —auto и —initial.
—auto смотрит на предыдущую миграцию, смотрит, что изменилось и создаёт миграцию, которая применяет эти изменения. Например, если Вы добавляете поле в модель, то —auto это заметит и сделает миграцию, которая создаст в таблице новую колонку, соответствующую этому полю в модели.
Но, как Вы наверняка заметили, —auto требуется предыдущая миграция, а в нашем новом приложении нет ещё ни одной миграции. Вместо этого мы воспользуемся —initial, который создаёт таблицы и индексы для всех моделей в приложении. Это то, что Вам нужно в самом начале, аналог syncdb, тогда как —auto Вам понадобится уже позже, для обслуживания изменений.
Так что давайте создадим нашу первую миграцию:

 $ ./manage.py schemamigration southtut --initial
Creating migrations directory at '/home/andrew/Programs/litret/southtut/migrations'...
Creating __init__.py in '/home/andrew/Programs/litret/southtut/migrations'...
+ Added model southtut.Knight
Created 0001_initial.py. You can now apply this migration with: ./manage.py migrate southtut

(Если на этом этапе Вы получите ошибку, что south_migrationhistory не существует, значит Вы забыли запустить syncdb после установки South)
Как Вы можете видеть, эта команда создала для нас папку миграции и создала внутри неё новую миграцию. Всё, что нам осталось — лишь применить её к БД:

 $ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0001_initial.
> southtut:0001_initial
- Loading initial data for southtut.

Теперь South создал новую таблицу в нашей модели — можете проверить если хотите, и добавить нескольких Knight при помощи ./manage.py shell.

Изменение модели

До сих пор мы не делали ничего, с чем бы не смог справиться syncdb. Теперь пришло время изменить нашу модель. Давайте добавим к ней ещё одно поле:
 from django.db import models

class Knight(models.Model):
name = models.CharField(max_length=100)
of_the_round_table = models.BooleanField()
dances_whenever_able = models.BooleanField()

Теперь, если мы не будем использовать миграции, так просто добавить новую колонку к таблице southtut_knight уже не получится. Но при помощи South мы сможем сделать это всего в два шага: создать миграцию для отражения изменения и затем применить её:
Во-первых, создадим миграцию при помощи —auto:
 $ ./manage.py schemamigration southtut --auto
+ Added field dances_whenever_able on southtut.Knight
Created 0002_auto__add_field_knight_dances_whenever_able.py. You can now apply this migration with: ./manage.py migrate southtut


братите внимание, что South автоматически подбирает имя для миграции. Вы можете задать для миграции своё имя, указав его в качестве другого аргумента).
Теперь давайте её применим:
 $ ./manage.py migrate southtut
Running migrations for southtut:
- Migrating forwards to 0002_auto__add_field_knight_dances_whenever_able.
> southtut:0002_auto__add_field_knight_dances_whenever_able
- Loading initial data for southtut.

После этого наша новая колонка создана и опять же это можно проверить.

Конвертация существующего приложения

Иногда, особенно когда Вы добавляете South в проект, Вы хотите использовать его для уже существующего приложения — там, где уже есть созданные таблицы.
В этом и состоит отличие работы с существующим приложением от работы с новым приложением. И о том, как справиться с такой задачей смотрите страницу «конвертация приложения (пока не переведено)».
Теперь, когда Вы знаете как использовать South, можно перейти ко второй части руководства.

Автор: Ishayahu Lastov