TechLife devBlog

#- Django

Dlaczego Django ssie

Kodowanie, Django 2 lipca 2015 22:15

Jeżeli ktoś jeszcze nie widział bardzo fajnej prezentacji Emila (e-mail'a) Stenströma na temat obecnego stanu rozwoju Django to polecam. Konstruktywny hate obecnych braków chyba najpopularniejszego pythonowego frameworka. W pełni się zgadzam.

Django i shortcodes

Kodowanie, Django, Techblog 5 lutego 2012 23:21

Kilka miesięcy temu pytałem #django-wców na blipie o możliwość wstawiania do treści słów kluczowych, które później byłyby zamieniane na różne dziwne rzeczy. Nikt mi wtedy nie odpowiedział, więc pewnie sprawa nie jest jeszcze zbyt popularna. Ostatnio jednak znalazłem rozwiązanie mojego problemu. Słowo klucz - shortcodes.

Shortcodes to znane przede wszystkim z WordPress-a pseudo tagi, za pomocą których możemy wstawiać w dowolne miejsca w tekście różnego rodzaju obiekty. Np. wykorzystując taki kod:

[gallery id="123" size="medium"]

możemy osadzić galerię pomiędzy dwoma paragrafami tekstu. Odpowiednia aplikacja istnieje też dla Django - django-shortcodes. Instalacja jest bardzo prosta i sprowadza się do kilku kroków:


  1. do katalogu z naszym projektem kopiujemy folder shortcodes

  2. w settings.py dodajemy shortcodes do INSTALLED_APPS

  3. w szablonie, w którym chcemy użyć filtra dodajemy {% load shortcodes_filters %}

  4. do zmiennej, która zawiera nasze słowa kluczowe dodajemy filtr shortcodes np. {{ news.tresc|shortcodes|safe }}

Zmienna zawierająca słowa kluczowe nie musi być wyłącznie tekstem. Może być to HTML wyprodukowany przez jakiś edytor np. TinyMCE. Trzeba tylko pamiętać, żeby najpierw dokonać podmiany słów kluczowych a dopiero później wypluć HTML (kolejność filtrów).

Najważniejszą częścią aplikacji django-shortcodes są parsery znajdujące się w katalogu parsers (domyślnie tylko youtube)

parsers

To one zawierają definicje słów kluczowych, oraz ich parametry. Nazwa pliku parsera definiuje też nazwę słowa kluczowego.

Ok, czas na przykłady, np. YouTube (trochę zmodyfikowany):

YouTube - TinyMCE

Rezultat:

YouTube - podgląd

Napisałem też prosty parser do wstawiania mp3 z pomocą Googlowego odtwarzacza:

[mp3 adres=http://www.luzuj.com/pb/mp3/piwo.mp3]

Odtwarzacz mp3

czy parser osadzający galerię zdjęć:

[galeria numer=1]

Galeria zdjęć

Oczywiście łatwo można też modyfikować wstawiane treści za pomocą różnych parametrów np.

[youtube adres=http://youtu.be/d9NF2edxy-M szerokosc=600 wysokosc=400]

lub

[galeria numer=1 tytul=brak]

Naturalnie to tylko banalne przykłady. Jednak najważniejsze jest to, że każdy może je łatwo dostosować do swoich wymagań i wykorzystać do wstawiania ankiet, wykresów, plików, prezentacji, statystyk czy różnych innych treści, które w normalnych przypadkach wymagałyby użycia skomplikowanego (często generowanego) HTML-a, który byłby kłopotliwy dla różnych edytorów typu TinyMCE.

Django: usuwanie cache'u generowanego przez szablon

Kodowanie, Django, Techblog 25 marca 2011 23:58

Sytuacja jest następująca: 1989 rok, Kijów. Piękna ukraińska jesień. Czterech urodzonych morderców, uzbrojonych po zęby, a ja sam, jeden, z moim wiernym kałachem... mamy jakiś kawałek szablonu, którego wygenerowanie kosztuje nas sporo zasobów np. wiele zapytań do bazy. Cache'ujemy więc w szablonie wygenerowany HTML aby ulżyć serwerowi. Np. tak:

{% load cache %}

{% cache 604800 statystyki %}
   jakiś super zasobożerny w generowaniu kod HTML
{% endcache %}

Wszystko działa ładnie. Cache wygasa co tydzień (604800 sekund) i wszystko gra. Problem pojawia się jednak, kiedy wygenerowany kod zależy bezpośrednio od jakiegoś obiektu lub obiektów. Kiedy dany obiekt jest modyfikowany chcielibyśmy usunąć cache generowany przez szablon. Rozwiązanie wydaje się być dość logiczne:

from django.core.cache import cache

cache.delete('statystyki')

Niestety powyższy kod robi nic. Template tag tworzy cache generując klucz:

args = md5_constructor(u':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on]))
cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest())

Jak widzimy schemat jest następujący:
template.cache.KLUCZ-Z-SZABLONU.ZAKODOWANE_ARGUMENTY

Jeżeli nie używaliście argumentów w szablonie przy stworzeniu klucza cache'u (czyli nie zrobiliście np. czegoś takiego: {% cache 604800 statystyki request.user %}) to Wasza lista argumentów będzie pusta. Suma MD5 z pustego stringa zawsze wynosi tyle samo, więc dla cache'u tworzonego bez argumentów klucz jest stały i wygląda tak:

cache.delete('template.cache.statystyki.d41d8cd98f00b204e9800998ecf8427e')

Django i zduplikowane wartości kluczy

Kodowanie, Django 24 stycznia 2011 21:38

Django przy współpracy z Postgresem odstawiło dzisiaj niezłą scenę. Galeria na stabilnie działającej od ponad roku stronie nagle wysypała się przy dodawaniu kolejnych zdjęć, pod pretekstem podwójnych wartości kluczy w tabeli przechowującej pliki galerii.

IntegrityError: duplicate key value violates unique constraint "galeria_plik_pkey"

No więc sprawdzam.

SELECT max(id) FROM galeria_plik;
1573
SELECT nextval('galeria_plik_id_seq'::regclass) FROM galeria_plik;
1526

Great Scott! WTF? Ktoś zakłócił kontinuum! Nie mam pojęcia jak stabilnie działający PostgreSQL mógł nagle tak machnąć się w numeracji... Jeżeli traficie na coś podobnego nie pozostaje nic innego jak zresetowanie indeksu tabeli z właściwym numerem, jaki powinien posiadać kolejny wiersz.

ALTER SEQUENCE galeria_plik_id_seq RESTART with 1574;

I powinno działać. Komuś zdarzyło się coś podobnego?

PS. Python jednak ryje beret. Dzisiaj ułożyłem takie oto zapytanie:

FROM galeria_plik SELECT max(id);

Django – personalizacja settings.py

Kodowanie, Django, Techblog 28 czerwca 2009 11:17

Jak każdy djangowic dobrze wie plik settings.py przechowuje wszelkie ustawienia aplikacji począwszy od nazwy strony a na listach middleware'u czy liście zainstalowanych aplikacji skończywszy. Ponieważ zawiera on wszelkie ustawienia, musi być inny dla developera a inny dla serwera produkcyjnego (np. z powodu tego, że developer ma DEBUG=True).

Pierwszą możliwością rozwiązania tego problemu jest ignorowanie pliku settings.py przez repozytorium kodu, tak aby różne zawartości tego pliku nie powodowały konfliktów. Wadą takiego rozwiązania jest potrzeba ręcznego edytowania tego pliku na serwerze kiedy np. dodajemy nową aplikację do INSTALLED_APPS. Moje repozytorium aktualizuje stronę na serwerze po każdym commicie, więc ręczna edycja settings.py jest męcząca.

Drugą możliwością jest stworzenie osobnych ustawień dla serwera i osobnych dla developera. Django umożliwia uruchomienie serwera z podaniem pliku ustawień jako parametr:

python manage.py runserver --settings=settings-developer.py

Wszystko wydaje się być ok. Jeżeli coś zmieniamy to dokonujemy zmiany w settings.py, settings-developer.py i po commitcie wszystko gra. Czasami jednak przy testowaniu różnych rzeczy w pliku developera można zapomnieć o settings.py i po commicie znowu strona jest rozwalona. Poza tym nie każdy developer korzysta z tych samych ustawień. Jeden używa Django Debug Toolbar inny ColorSQLMiddleware więc trzeba by zrobić tyle settings-xxx ilu developerów. A później przy zmianie czegoś globalnego edytować wszystkie...

Ostatecznym rozwiązaniem okazało się przeciążenie pliku settings.py przez ponowną deklarację zmiennych lub rozszerzenie już istniejących wartości. Jak to zrobić?

  1. Tworzymy plik, który będzie przetrzymywał nasze lokalne ustawienia, np. settings-local.py
  2. Na końcu pliku settings.py dodajemy kod:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
execfile('%s/settings-local.py' % BASE_DIR)

Teraz w pliku settings-local.py możemy ponownie zadeklarować zmienne które mają zostać przeciążone lub rozszerzyć zadeklarowane już w settings.py listy. Np. kiedy jeden z developerów chce użyć Django Debug Toolbar dodaje do settings-local.py taki kod:

MIDDLEWARE_CLASSES += (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)

INSTALLED_APPS += (
    'debug_toolbar',
)

DEBUG_TOOLBAR_PANELS = (
    'debug_toolbar.panels.sql.SQLDebugPanel',
    'debug_toolbar.panels.headers.HeaderDebugPanel',
    'debug_toolbar.panels.cache.CacheDebugPanel',
    'debug_toolbar.panels.profiler.ProfilerDebugPanel',
    'debug_toolbar.panels.request_vars.RequestVarsDebugPanel',
    'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel',
    'debug_toolbar.panels.templates.TemplatesDebugPanel',
    # If you are using the profiler panel you don't need the timer
    # 'debug_toolbar.panels.timer.TimerDebugPanel',
)

Plik settings-local.py wywalamy z repozytorium i w ten sposób otrzymujemy elastyczny mechanizm współdzielenia wartości globalnych przy jednoczesnej personalizacji ustawień lokalnych.