Wielokrotnie zdarzyło mi się (i pewnie nie tylko mi) musieć uzupełnić jakimiś wartościami bazę danych w trakcie zmian w projekcie. Chodziło konkretnie o dane, które powinny się pojawić po wdrożeniu zmian i nie powinny dawać możliwości ich zmiany bądź usunięcia ze względu na potencjalny wpływ na działanie całej aplikacji.
Pierwszy pomysł tego rozwiązania to użycie migracji, jak w poniższym przykładzie:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import migrations | |
from ..models import PREDEFINED_TOASTR_TYPES | |
def preinstall_toastr(apps, schema_editor): | |
ToastrType = apps.get_model('toastr', 'ToastrType') | |
db_alias = schema_editor.connection.alias | |
for title, color in PREDEFINED_TOASTR_TYPES.items(): | |
toastr = ToastrType.objects.create( | |
title=title, | |
color=color | |
) | |
toastr.save() | |
class Migration(migrations.Migration): | |
dependencies = [ | |
('toastr', '0001_initial'), | |
] | |
operations = [ | |
migrations.RunPython( | |
migrations.RunPython.noop, | |
preinstall_toastr, | |
), | |
] |
Po wykonaniu migracji w bazie pojawią nam się wymagane wpisy. Jednak istotnym minusem tego rozwiązania jest to, że nadal można usunąć je bezpośrednio w bazie czy nawet w panelu administracyjnym Django i nie zostaną one odtworzone.
Kolejne podejście do problemu to przeniesienie kodu odpowiedzialnego za dodawanie wpisów do bazy do pliku apps.py
, który uruchamiany jest przy starcie aplikacji – czyli praktycznie zawsze po restarcie serwera. Wrzucenie kodu do metody ready
pozwoli nam obejść w całkiem elegancki sposób wspomniany wcześniej problem:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.apps import AppConfig | |
class ToastrConfig(AppConfig): | |
name = 'toastr' | |
def ready(self): | |
from .models import ToastrType, PREDEFINED_TOASTR_TYPES | |
# Prevent executing before applying migrations | |
if is_database_synchronized(DEFAULT_DB_ALIAS): | |
for title, color in PREDEFINED_TOASTR_TYPES.items(): | |
try: | |
ToastrType.objects.get(title=title) | |
except ToastrType.DoesNotExist: | |
new_toastr_type = ToastrType( | |
title=title, | |
color=color | |
) | |
new_toastr_type.save() |
Na co zwrócić musimy uwagę, w pliku __init__.py naszej aplikacji musimy jawnie wskazać ścieżkę do pliku konfiguracyjnego aplikacji, jako że plik ten jest opcjonalny:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
default_app_config = 'toastr.apps.ToastrConfig' |
Warto także zerknąć na kilka innych kawałków kodu, m. in. uniemożliwienie edytowania kluczowych informacji w panelu administracyjnym Django czy usuwanie tych danych. Zauważyć też można 2 drobne usprawnienia: 1) wizualna reprezentacja zapisanego koloru w panelu administracyjnym dzięki użyciu funkcji format_html
oraz 2) zmiana tytułu kolumny w panelu administracyjnym poprzez właściwość short_description
.
Kawałek kodu związany z synchronizacją modeli z bazą danych został zaczerpnięty ze StackOverflow, a błąd dotyczący braku synchronizacji ujawnił się dopiero przy uruchomieniu testów.
Cały kod do pobrania i przejrzenia znaleźć można na GitHub.