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

Документация 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

Документация South — Перевод. Что такое миграция?

Для непосвящённых: миграция (migrations), так же известная как 'schema evolution' или 'mutations' — это способ изменения схемы вашей БД с одной версии на другую. Django сам по себе может лишь добавлять новые модели, но почти во всех проектах часто происходят изменения самой модели — добавляются поля, изменятся свойства полей, и т.д.
South, и другие аналогичные решения, предоставляет способ обойти эту проблему, давая Вам инструмент для лёгкого и предсказуемого изменения схемы вашей БД. Вы пишете миграцию, которая говорит South как перейти с одной версии на другую, и, связывая эти миграции в цепочку, Вы можете двигаться вперёд или назад по истории изменений схемы вашей БД. 
В South, миграция это так же способ создания БД — первая миграция на самом деле миграция из пустой схемы к вашей схеме. Таким образом Вы можете как воссоздать самую последнюю версию схемы БД, так и проапгрейдить любую из устаревших, просто запустив миграцию с определённой точки. 
В процессе этого руководства мы покажем Вам как миграция работает и как она может быть полезна на разных примерах.

Автор: Ishayahu Lastov

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

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

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

Хотя South начинался как малоизвестный проект, он медленно набирал популярность и теперь стал одним из самых популярных инструментов для изменения схемы приложений Django.

Основные возможности

  • Создание автоматической миграции: South может отслеживать изменения в файле models.py file и автоматически создавать миграцию, которая отражает эти изменения.
  • Не зависит от конкретной БД: Насколько это вообще возможно South не зависит от используемой Вами БД, поддерживая 5 типов БД.
  • Подкован в приложениях: South знает что такое приложения и умеет с ними работать, позволяя Вам проводить миграцию для некоторых ваших приложений и оставляя остальные на совести syncdb.
  • VCS-proof: South оповестит Вас если кто-то ещё делает миграцию для выбранного приложения и ваши миграции вступают в конфликт.

Краткая история

Изначально South был разработан Torchbox в 2008, когда ещё не было другой системы, которая могла предоставить нам необходимый функционал. В короткие сроки код системы был открыт и она получила популярность после представления на панели Schema Evolution на DjangoCon 2008.

Где-то в 2009, она стала самой популярной системой среди всех альтернативных вариантов и, похоже, её популярность будет только расти. Хотя есть множество запросов по интеграции South или чего-то похожего напрямую в Django, такой интеграции ещё не было сделано, в основном по причине некоторой незрелости систем миграции.

Автор: Ishayahu Lastov

О сборке мусора, деструкторах и разных питонах

В этом посте я писал почему работа с файлами и другими объектами, требующими гарантированного закрытия должна должна производиться через with. Однако кроме минуса в виде добавления в код лишнего уровеня вложенности with еще и решает только часть проблемы — если код обработки файла не локален (нужно возвращать дескриптор в вызывающий код или хранить неопределенное время) with не может помочь. И собственно никто вообще не может помочь — суровая реальность состоит в том, что python не гарантирует вызов деструктора объекта. Т.е. если вы работаете на CPython, и не создаете циклических ссылок, то за крайне редкими исключениями деструктор будет вызываться вовремя. Но если вы используете ironpython/jython/pypy то ситуация становится совсем печальна.

Читать

Создаём QR коды на Python (Перевод)

Как-то раз я задумался: было бы забавно создать программу при помощи wxPython, которая генерирует QR коды и затем отображает их на экране. Поскольку, как понятно, я хотел написать её на Python, то после недолгих поисков я обнаружил трёх кандидатов на это дело:
Я воспользовался python-qrcode и pyqrnative, так как они прекрасно работают как на Windows, так и на Mac и Linux. Кроме того, они не требуют ничего больше, только Python Imaging Library. Проект pyqrcode требует дополнительных модулей и не работает на Windows, так что я даже не стал с ним заморачиваться. В итоге я взял старый код моего  Photo Viewer и немного его переиначил, чтобы получить генератор QR кодов. Если интересно — давайте поподробнее!

Приступаем

Как я уже упомянул в начале, Вам понадобится Python Imaging Library. Мы будем использовать wxPython для создания GUI, так что без него тоже не обойтись. И кроме того Вам понадобится загрузить python-qrcode и pyqrnative. Основное отличие, которое я обнаружил между ними — python-qrcode гораздо быстрее создаёт изображение и изображение именно привычного нам вида. По каким-то причинам, pyqrnative требует больше времени и создаёт более плотно выглядящие QR коды. Возможно, у каждого из проектов есть опции, меняющие вид кодов, но у обоих проектов отвратительная документация. В итоге пришлось копаться в исходниках больше, чем когда-либо.

Создаём QR коды

Итак, как только мы установили всё, что нам нужно, мы можем запустить следующий код и посмотреть, на что же способен Python:
import os
import wx
 
try:
import qrcode
except ImportError:
qrcode = None
 
try:
import PyQRNative
except ImportError:
PyQRNative = None
 
########################################################################
class QRPanel(wx.Panel):
""""""
 
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent=parent)
self.photo_max_size = 240
sp = wx.StandardPaths.Get()
self.defaultLocation = sp.GetDocumentsDir()
 
img = wx.EmptyImage(240,240)
self.imageCtrl = wx.StaticBitmap(self, wx.ID_ANY,
wx.BitmapFromImage(img))
 
qrDataLbl = wx.StaticText(self, label="Текст для перевода в QR код:")
self.qrDataTxt = wx.TextCtrl(self, value="http://www.mousevspython.com", size=(200,-1))
instructions = "Имя файла с QR кодом"
instructLbl = wx.StaticText(self, label=instructions)
self.qrPhotoTxt = wx.TextCtrl(self, size=(200,-1))
browseBtn = wx.Button(self, label='Изменить место для сохранения')
browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
defLbl = "Сохраняется по умолчанию в: " + self.defaultLocation
self.defaultLocationLbl = wx.StaticText(self, label=defLbl)
 
qrcodeBtn = wx.Button(self, label="Создать QR при помощи qrcode")
qrcodeBtn.Bind(wx.EVT_BUTTON, self.onUseQrcode)
pyQRNativeBtn = wx.Button(self, label="Создать QR при помощи PyQRNative")
pyQRNativeBtn.Bind(wx.EVT_BUTTON, self.onUsePyQR)
 
# компоновщик
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
qrDataSizer = wx.BoxSizer(wx.HORIZONTAL)
locationSizer = wx.BoxSizer(wx.HORIZONTAL)
qrBtnSizer = wx.BoxSizer(wx.VERTICAL)
 
qrDataSizer.Add(qrDataLbl, 0, wx.ALL, 5)
qrDataSizer.Add(self.qrDataTxt, 1, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(wx.StaticLine(self, wx.ID_ANY),
0, wx.ALL|wx.EXPAND, 5)
self.mainSizer.Add(qrDataSizer, 0, wx.EXPAND)
self.mainSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
locationSizer.Add(instructLbl, 0, wx.ALL, 5)
locationSizer.Add(self.qrPhotoTxt, 0, wx.ALL, 5)
locationSizer.Add(browseBtn, 0, wx.ALL, 5)
self.mainSizer.Add(locationSizer, 0, wx.ALL, 5)
self.mainSizer.Add(self.defaultLocationLbl, 0, wx.ALL, 5)
 
qrBtnSizer.Add(qrcodeBtn, 0, wx.ALL, 5)
qrBtnSizer.Add(pyQRNativeBtn, 0, wx.ALL, 5)
self.mainSizer.Add(qrBtnSizer, 0, wx.ALL|wx.CENTER, 10)
 
self.SetSizer(self.mainSizer)
self.Layout()
 
#----------------------------------------------------------------------
def onBrowse(self, event):
""""""
dlg = wx.DirDialog(self, "Выберите папку:",
style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
self.defaultLocation = path
self.defaultLocationLbl.SetLabel("Сохраняем в: %s" % path)
dlg.Destroy()
 
#----------------------------------------------------------------------
def onUseQrcode(self, event):
"""

https://github.com/lincolnloop/python-qrcode

"
""
qr = qrcode.QRCode(version=1, box_size=10, border=4)
qr.add_data(self.qrDataTxt.GetValue())
qr.make(fit=True)
x = qr.make_image()
 
qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
img_file = open(qr_file, 'wb')
x.save(img_file, 'JPEG')
img_file.close()
self.showQRCode(qr_file)
 
#----------------------------------------------------------------------
def onUsePyQR(self, event):
"""

http://code.google.com/p/pyqrnative/

"
""
qr = PyQRNative.QRCode(20, PyQRNative.QRErrorCorrectLevel.L)
qr.addData(self.qrDataTxt.GetValue())
qr.make()
im = qr.makeImage()
 
qr_file = os.path.join(self.defaultLocation, self.qrPhotoTxt.GetValue() + ".jpg")
img_file = open(qr_file, 'wb')
im.save(img_file, 'JPEG')
img_file.close()
self.showQRCode(qr_file)
 
#----------------------------------------------------------------------
def showQRCode(self, filepath):
""""""
img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
# масштабируем изображение
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.photo_max_size
NewH = self.photo_max_size * H / W
else:
NewH = self.photo_max_size
NewW = self.photo_max_size * W / H
img = img.Scale(NewW,NewH)
 
self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
self.Refresh()
 
 
########################################################################
class QRFrame(wx.Frame):
""""""
 
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="QR Code Viewer", size=(550,500))
panel = QRPanel(self)
 
if __name__ == "__main__":
app = wx.App(False)
frame = QRFrame()
frame.Show()
app.MainLoop()
Код для изменения и отображения картинки разъяснён в одной из предыдущих статей (не переведена), так что единственное, что может вызвать у Вас непонимание — два метода создания кодов: onUseQrcode и onUsePyQR. Я всего лишь взял примеры с их сайтов и немного поменял их, чтобы сгенерировать изображение с кодом. Они достаточно самоочевидны, но почти не документированны, так что я не смогу сказать Вам, что же тут происходит. И, хотя оба проекта страдают на момент написания от отсутствия документации, код, тем не менее работает:
Как можно видеть — вполне стандартный код. А этот, созданный PyQRNative, выглядит гораздо более «плотным»:
Оба кода были корректно распознаны моим телефоном на  Android. Так что если Вам надо создать для вашего проекта QR код — я надеюсь, эта статья Вам поможет!

Обновление от 21/05/2012

Один из моих читателей (Mike Farmer) сообщил мне недавно, что в PyQRNative “первый аргумент — размер контейнера, а второй — избыточность / коррекция ошибок”. У меня были такие подозрения по поводу второго аргумента, но я не знаю, что делают эти уровни коррекции ошибок. К счастью, Mr. Farmer объяснил это мне: Если уровень коррекции ошибок низкий, то смазывание кода или его намокание приведёт его в нечитабельное состоянии. Если же Вы будете использовать более высокий уровень коррекции, то за счёт добавления / дублирования данных внутри кода, он сможет пережить даже потоки слёз. Более того, при высоком уровне коррекции ошибок Вы можете даже наложить на код дополнительное изображение, и он  это переживёт. В любом случае, если Вы изменяете первый параметр, Вы можете изменить размер кода. Зачем? Ну, чем больше информации Вы хотите засунуть в код, тем больше он должен быть. Mr. Farmer предложил забавный тестовый код, который позволяет вычислить минимальный возможный для вашей информации размер кода:

import PyQRNative
 
def makeQR(data_string,path,level=1):
quality={1: PyQRNative.QRErrorCorrectLevel.L,
2: PyQRNative.QRErrorCorrectLevel.M,
3: PyQRNative.QRErrorCorrectLevel.Q,
4: PyQRNative.QRErrorCorrectLevel.H}
size=3
while 1:
try:
q = PyQRNative.QRCode(size,quality[level])
q.addData(data_string)
q.make()
im=q.makeImage()
im.save(path,format="png")
break
except TypeError:
size+=1

Исходник

Автор: Ishayahu Lastov

Релиз Demakein 0.2: Шалмей (Перевод)

 

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

 

Автор: Ishayahu Lastov