Частни проблеми при използването на django-evolution
Днес за мен бе белязан от епична битка с django-evolution. С различните неудобства на това приложение, ние сме в разработването първо Където отиват всички, а след това Плакатите са изправени от доста време. Днешният инцидент най-накрая ме накара да преодолея мързела си и да представя тези проблеми на обществеността.
Подреждане на Syncdb
Едно от избраните решения за django-evolution е хендсфри синхронизация. Тоест, django-evolution никога не се опитва да изпълни SQL за създаване на таблици.
Първото неудобство в това за нас е по-сложният процес на компилиране на SQL за миграция. Факт е, че нашите администратори не обичат да стартират промени в базите данни с всякакви неконтролирани инструменти, но искат да им изпращат SQL скриптове. Django-evolution е в състояние да генерира SQL на своите еволюции, а SQL за създаване на таблици трябва да бъде изрязан от базата данни на ръка поотделно.
Но нещо друго е много по-лошо. Представете си, че в процеса на мигриране на база данни от доста стара версия има както нови модели, така и еволюции, приложени към тези модели. Тъй като трябва да създадете таблици в отделна команда, трябва да запомните, че този SQL трябва да премине преди еволюцията. Донякъде тъжно. Сега си представете, че сред еволюциите има преименуване на една таблица (съответстващо на преименуването на модела). От гледна точка на syncdb няма таблица за името на новия модел и тя ще го създаде. От това ще се обхване развитието на преименуването, защото таблицата вече съществува.
Пристигна - има еволюции, които трябва да се изпълнят преди syncdb, а има и такива след. Съответно процесът се превръща в напълно ръчно бране на череши.
Имаше някои други специални случаи, когато редът на използване на syncdb и evolve е важен и в резултат, за да се отървем от тези проблеми, започнахме да въвеждаме създаването на таблици в еволюция ръчно.
Симулации на писане
Кой не знае, има интересна функция в django-evolution: той знае как да не прилага еволюции директно към базата данни, а първо да ги изгони („симулира“ по отношение на приложението). Това се прави всъщност не в базата данни, а чрез изпълнение на специално написани функции, които променят подписа на проекта - представянето на текущия тип модели в паметта. Това е нещо като този речник:
Идеята е, че django-evolution взема запазения подпис от базата данни, който съответства на предишното й състояние, и го изтегля през симулационните функции на всички неприложени еволюции. Те променят подписа в паметта и ако в края той съвпада с това, което в момента е описан вашият модел на Python, django-evolution съобщава, че наличните еволюции са достатъчни за надграждане на база.
Това работи добре на теория, ако приложението за миграция прави цялата тази симулация вместо вас. На практика обаче се оказва, че освен обичайните еволюции като добавяне на поле/премахване на поле, имаме и много персонализирани еволюции. Създаване на таблици, преименуване на таблици, разделяне на таблици на няколко и т.н. Оказва се, че за всяко подобно кихане по поръчка трябва да напишете два кода: истински SQL и симулация.
Още по-лошо, много симулации са необходими само за да накарат еволюцията да спре да се оплаква от несъответствие при симулацията, защото тогава тя отказва да направи каквото и да било. Нито прилагайте еволюции, нито извеждайте SQL за тях. Той няма ключа --force .
Като тъжен пример мога да цитирам еволюцията на "DeleteFieldWithoutSQL", която е необходима за симулацията, за да покаже, че полето е изтрито от модела, но в същото време, за да не бъде физически изтрито, защото по-късно влачим данните от него в друга таблица и едва след това изтрийте.
В резултат на това се оказва, че все още не сме имали нито едно издание, когато не сме отделили много време за добавяне на код, само за да може еволюцията да върви поне върху доста стара версия на базата данни. Тоест, от своя страна, по време на разработката всичко изглеждаше приложено, но на едро - не.
Намеквани и спасени еволюции
Еволюцията може да се приложи по два начина. Можете да поискате автоматично да определите текущата разлика между кода и основата (--hint) и незабавно да я приложите. Или можете да запазите еволюцията във файл и да я регистрирате в последователността на приложението, така че да се приложи при надграждане на друга база данни.
Сега си представете напълно нормален работен процес:
- разработчикът прави промени в модела
- чрез evolve --hint -x го отразява в DB
- тестове, отстраняване на грешки
Сега, за да се извърши това, е наложително да се запишат еволюциите, извършени под формата на файл, за да се надстроят автоматично други бази данни. Откъде получавате тези еволюции от сега? Всичко вече е приложено.
Изглежда, че можете да въведете правило в процеса на разработка, че никога не трябва да използвате подсказки, а да ги правите само под формата на файлове. Но първо, твърде много от тези "хигиенни" правила поставят много стрес върху главата, и второ, това просто не работи. Тъй като разработчикът все още не е роден, който ще напише правилния код за първи път, включително при промяна на схемата на базата данни. Винаги трябва да повторите нещо въз основа на резултатите за отстраняване на грешки.
В резултат на това това също е вечна война: или ровете в червата на таблиците на django-evolution, изчиствайки ръчно информация за приложените еволюции, или ръчно премахвайте промените в базата данни и правите нова еволюция.
Наличието на два вида еволюция ми се струва сега, може би, основният архитектурен недостатък на django-еволюцията. Въпреки че може би просто не разбрахме как да го използваме.
Ключът "всичко е наред"
Често се случва и обратната ситуация: имате определена база, в която нещо е преработено ръчно, нещо е изхвърлено/копирано от някъде. Програмистът обаче е сигурен, че съвпада с това, което е в моделите. Еволюциониращият екип не е така. Тъй като базата данни не записва, че запазените еволюции са приложени, тя се опитва да ги симулира и открива грешки при многократна употреба.