Wczoraj byłem zmuszony szybko dodać dodatkową funkcjonalność do jednej z rozwijanych przez nas aplikacji. Widok, o który chodziło opiera się na automatycznym, scaffoldowanym widoku administratora. Brzmi prosto?

Problem polegał na tym, że dostosowywanie logiki administratora jest dopiero w fazie planów (gałąź admin-newforms). W związku z tym, rozbudowanie go o własną logikę jest teoretycznie na tym etapie niewykonalne.

Przekopałem się więc przez większość kodu modułu django.contrib.admin i okazało się, że można Django nieco oszukać. Widoki są funkcjami, z czego korzysta się często przy generic views. Nie można po nich dziedziczyć, ale można wołać jeden z drugiego.

Brzydki hack

Do pliku urls.py na samym początku (przed sekcją admin) dopisujemy:

(r'^admin/aplikacja/model/add/', 'serwis.aplikacja.admin_views.add_stage'),

Następnie zakładamy plik serwis/aplikacja/admin_views.py:

from django.contrib.admin.views import main

def add_stage(request, *args, **kwargs):
	post = request.POST.copy()
	if request.method == 'POST':
		post['user'] = request.user.id
	request.POST = post
	return main.add_stage(request, 'aplikacja', 'Model', *args, **kwargs)

Voila!

Parę słów wytłumaczenia

Pierwsza część jest chyba oczywista, w tym przypadku nadpisujemy widok dodawania dla konkretnego modelu. Ponieważ Django przetwarza mapę rutingu w takiej kolejności, w jakiej są podane regułki, nasza linijka przesłoni znajdujące się niżej automatyczne reguły wbudowanego panelu administratora.

Druga część to utworzenie osobnego pliku dla widoków administracyjnych (nie musi to być osobny plik, ale to dobra praktyka). W pliku tym umieszczamy definicję własnego widoku add_stage.

W powyższym przykładzie dodawany jest automatycznie właściciel dla tworzonej instancji modelu. Nie bardzo można zrobić to w samym modelu, bo model jest bezstanowy i nie ma dostępu do stanu aplikacji. Poza tym, chcemy, żeby pole to było ustawiane tylko w tym przypadku.

Obiekt request.POST jest domyślnie immutable, więc tworzymy jego kopię i właśnie tę kopię modyfikujemy, a następnie podmieniamy oryginalny obiekt.

Ostatnia linijka powoduje wywołanie oryginalnego, magicznego widoku administratora, dostarczonego przez Django. Z tą różnicą, że przekazujemy do niego nazwę aplikacji i modelu. W przypadku wywołania bezpośredniego, pochodzą one z wyrażenia regularnego w konfiguracji urls.py, tutaj musimy podać je jawnie.