milo noir

Hogyan készült? 4. rész

Egy blogban keresni több módon is lehet. Az egyik legegyszerűbb, ha témakör alapján kategóriákat hozunk létre, majd ezekkel felcímkézzük a bejegyzéseinket. Ezután a kategóriákat vagy címkéket felsoroljuk mondjuk egy oldalsávban és ezekre kattintva egy szűrést végzünk a posztok listáján. Először nézzük meg hogyan lehet megcsinálni a címkézést, aztán pedig, hogy hogyan tudjuk velük a szűrést intézni!

Címkézés

A legegyszerűbb megoldás, ha egy egyszerű CharField mezőt hozzácsapunk a meglévő modellünkhöz:

1
2
3
4
5
6
7
8
from django.db import models

class Story(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    category = models.CharField(max_length=50)
    content = models.TextField(blank=True)
    created = models.DateTimeField(default=datetime.datetime.now)

Ezzel csak egy probléma van: maximum 1 db kategóriát tudunk hozzárendelni egy bejegyzéshez. Szerencsére létezik már több kész megoldás a problémára, ezekből az egyik a django-taggit, amit én is használok. Sajnos a django-taggit önmagában nem tartalmaz template tageket, azonban később jól jöhetnek, így én javaslom a django-taggit-templatetags telepítését is. Nincs különösebb varázslat, a Taggit elintéz mindent. Először is hozzá kell adni az appokhoz a settings.py-ban:

1
2
3
4
5
6
INSTALLED_APPS = (
    ...
    'taggit',
    'taggit_templatetags',
    ...
)

A modellünkbe importáljuk be a TaggableManager osztályt és cseréljük le vele az egyszerű category mezőt (nevezzük inkább tags-nek mostantól):

1
2
3
4
5
6
7
from taggit.managers import TaggableManager
from django.db import models


class Story(models.Model):

tags = TaggableManager()

Ha ez megvan rögtön futtassunk is egy migrálást, hogy az adatbázisunk sémája frissüljön:

1
python manage.py migrate

Innentől kezdve az admin oldalon vesszővel elválasztva bármennyi címkét hozzárendelhetünk a postjainkhoz. A tagek egyszerűen megjeleníthetőek az oldalon az alábbi template részlet segítségével:

1
2
3
{% for tag in story.tags.all %}
    <a href="{% url 'blog-tagged' tag.slug %}">{{ tag.name }}</a>
{% endfor %}

A fenti példában a story template object egy darab posztra mutat, a blog-tagged URL-ről a szűrésnél lesz szó. Minden tag-nek van egy name mezője, ami a konkrét megjelenítendő címke neve lesz és egy slug mezője, ami ugyanazt a szerepet játsza, mint a saját Story modellünkben. A story.tags.all lekérdezi az összes, adott story-hoz tartozó taget és egy listában adja vissza.

Szűrés

Szűrésnél az utolsó lekérdezést fordítjuk meg: az adott taghez milyen story-k tartoznak? Ehhez már saját view-t és URL patternt kell írnunk. A view:

1
2
3
4
5
6
7
8
9
from django.shortcuts import render_to_response, get_object_or_404
from django.template.context import RequestContext
from taggit.models import Tag
from models import Story

def tagged(request, slug):
    tag = get_object_or_404(Tag, slug=slug)
    story_list = Story.objects.filter(tags__name__in=[tag.name])
    return render_to_response('story_list.html', locals(), context_instance=RequestContext(request))

Először egy get_object_or_404 hívással a címke egyedi slug attribútuma alapján kiválasztjuk a megfelelő taget, majd az összes Story közül kiszűrjük azokat, amelyekben ez megtalálható. Végül az egészet egy erre alkalmas template-tel a kész HTML válasszá rendereljük.

A context is a “variable name” -> “variable value” mapping that is passed to a template.

A template renders a context by replacing the variable “holes” with values from the context and executing all block tags.

A URL pattern:

1
url(r'^tags/(?P<slug>[-\w]+)/$', 'tagged', name='blog-tagged')

A fenti URL pattern három részből áll:

  1. Egy reguláris kifejezés, amelyre egy beérkező URL request illeszkedhet. Ebben a (P<slug>[-\w]+) rész egy nevesített csoport, amely - és alfanumerikus karakterek kombinációjára illeszkedik. A csoport a slug nevet kapja.
  2. A view neve (lásd a view kódjában a függvény neve is tagged): ezt a view-t kell hívni ha a regexpre van illeszkedő találat.
  3. A name argumentum pedig egy alias, amelyet a template-ben használtunk (lásd fent a template kódját). További értelmezés itt.

Az alábbi template részlettel felsorolható az összes témába vágó poszt:

1
2
3
4
5
6
7
8
9
{% if tag %}
    <p>{{ tag.name }}</p>
{% endif %}
...
{% for story in story_list %}
    <p><a href="{{ story.get_absolute_url }}">{{ story.title }}</a></p>
{% empty %}
    <p>No posts.</p>
{% endfor %}

Útdíjért a Tescoba!

Az Úrnak 2015. évében ha véletlenül autópályára merészkedsz Magyarországon, akkor ajjaj. Ezt megelőzendő ún. megyei matricát szeretnél vásárolni, hogy legalább a nyüves Tescoig eljuthass. Amikor megpróbálod január 2-án, akkor ezt az újévi köszöntést látod a NÚSZ-tól:

Köszi!

Kiváló minőségű nemzeti termék. (Felhívnám a figyelmet a hosszú ékezetes betűket nem tartalmazó karakterkészletre, valamint az elbaszott keretezésre.)

Hogy ezt SMS-ben hogyan kell intézni, na arról meg semmi info. Lendületes hátramenetben az ország!

A tökéletes palacsintatöltelék

A héten Csu csajos palacsintapartit tartott nálunk, ám egy kicsit elmérte a mennyiséget. Ennek következtében egy fazék bekevert tészta maradt a nyakunkon, amit én tegnap kisütöttem. Többféle töltelékkel várta a vendégeket, és ezekből is maradt, így volt lehetőségem kísérletezni. Nekem alapvetően kedvencem a túrós, de ezt mindenhol szarul csinálják - mazsolásan meg kaprosan... pfej! Én régebben sem féltem összeengedni más töltelékekkel (lekvár, kakaó, nutella, pudingok), most viszont megtaláltam a killer kombót: túrókrém (túró, tejföl, vaníliás cukor, kristálycukor, citromhéj reszelve) + Tesco almapüré + őrölt fahéj. Egyszerűen o-da-basz!

Hogyan készült? 3. rész

Frissiben találtam megoldást egy problémára, amely pár hete bosszant, ezért erről írok röviden: biztonsági mentés.

Adott ugye a Raspberry Pi, rajta pedig a weboldal beélesítve. Maga a Django kód persze verziókövető rendszerben van, annak nem eshet bántódása. De mi a helyzet az adatbázissal és a statikus tartalmakkal (pl. képek)? Amióta szeptember végén elindítottam az oldalt aggaszt, hogy nincsen backup stratégiám.

Mostanáig! Úgy képzeltem, hogy a nem verziókövetett fájljaimat felsyncelem valamilyen felhő tárhelyre minden nap és akkor nyugodtan alhatok. Ma délután meg is találtam a megoldást ezen az oldalon. Csak annyira kötnék bele, hogy az első wget lépés nálam nem működött, úgyhogy helyette inkább clone-oztam a repot innen, ahogyan az a Getting started részben írva vagyon.

Ha jól csináltam, akkor minden reggel 6 órakor egy újabb backup mentésem lesz a Dropboxomban.

Hogyan készült? 2. rész

Sokat gondolkoztam az első rész óta a folytatás mikéntjén. Temérdek információ fellelhető tutorial és könyv formában arról, hogy hogyan kell egy nagyon alap Django blogot megcsinálni, úgyhogy én nem szeretnék beállni ebbe a sorba. Írásaimban feltételezem, hogy az olvasó már egy ilyen kalauzon végigment, így az alapokkal nem foglalkozom, sokkal inkább olyan feature leírásokat szeretnék közölni, amelyekkel egy blog továbbléphet erről a szintről.

Egy tipikus nagyon alap blog models.py állománya nagyjából így szokott kinézni:

1
2
3
4
5
6
7
from django.db import models

class Story(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    content = models.TextField(blank=True)
    created = models.DateTimeField(default=datetime.datetime.now)

Mint látható egy ez nem egy túlkomplikált dolog. Amire szükség van az egy cím, egy slug mező permalink generáláshoz, maga a bejegyzés tartalma és egy időbélyeg.

A created mezőt az admin felület automatikusan kitölti majd nekünk amikor új bejegyzést kreálunk. A slug-ot a title-ből generáltatjuk - ez a beállítás admin.py-ból látszik:

1
2
3
4
5
6
7
8
from django.contrib import admin
from models import Story

class StoryAdmin(admin.ModelAdmin):
    ...
    prepopulated_fields = {'slug': ('title')}

admin.site.register(Story, StoryAdmin)

Ami a tartalmat illeti, a történet itt már érdekesebbé válik. Ebben a formában a content csak egyszerű szöveget fog visszaadni, ami azért valljuk be, elég kopár. Mondjuk azt, hogy szeretnénk látni ilyesmiket is: címsorokat, félkövér és dőlt betűket, képeket, beágyazott videókat és programkódokat lehetőleg szintaxis kiemeléssel és a sorok megszámozásával. Hívjuk ezt most egyszerűen formázott tartalomnak.

Figyelem! Most érkeztünk el oda, amit a mai poszt mondanivalójának szántam.

Jelen esetben egy WYSIWYG tartalomszerkesztő nem áll rendelkezésre, ígyhát ahhoz, hogy formázott tartalmat kapjunk a szöveget tele kell pakolnunk HTML tagekkel és CSS kódokkal. Ettől egyel jobb, ha csak HTML tageljük a szöveget és a CSS-t valahonnan source-oljuk. A legjobb azonban, ha egy text-to-HTML fordítót használunk, pl. a Markdownt. A Markdown szintaxisa egyszerűen elsajátítható, hasonló a Wikiéhez, de attól sokkal átgondoltabb és felhasználóbarátabb (szerintem).

Felmerül a kérdés, hogy mikor történik meg a text-HTML konverzió? Erre két megoldás kínálkozik:

  • A content mező tartalmazza a mindenkori Markdown formátumú szöveget, amiből minden lekéréskor generáltatjuk a HTML kódot; vagy
  • Fenntartunk egy másik mezőt a HTML kódnak is, amit minden szerkesztés után frissítünk.

Az első megoldással az a probléma, hogy számítási feladattal terheljük a szerverünket, a másodikkal pedig az, hogy duplikálva tároljuk a kontentot, tehát az adatbázisunk nagyobbra hízik. Az R-Pi esetében tegyük fel, hogy az utóbbi a kisebbik rossz. Ezek után a models.py úgy módosul, hogy felveszünk egy újabb nem szerkeszthető content mezőt (az eredeti content nevét is megváltoztatjuk a readability szellemében)...:

1
2
3
4
5
6
...
class Story(models.Model):
    ...
    markdown_content = models.TextField(blank=True)
    html_content = models.TextField(editable=False)
    ...

... majd ezt úgy töltetjük ki az admin felülettel, hogy a szerkesztés végeztével (mentéskor) ráhívjuk a Markdownt a markdown_content tartalmára - szintén a models.py-ban (természetesen a Markdownt nem felejtjük el importálni). Ehhez a Model class save metódusát írjuk felül:

1
2
3
4
5
6
7
from markdown import markdown
...
class Story(models.Model):
    ...
    def save(self, *args):  # yes, we override it
        self.html_content = markdown(text=self.markdown_content, extensions=['codehilite(linenums = True)'])
        super(Story, self).save()  # don’t forget to call super()

A codehilite(linenums = True) rész lesz felelős a kontentben megjelenő programkódok szintaxisának színezéséért és sorszámozásáért. Ahhoz, hogy ez működjön két dolgot kell megtennünk:

  1. Telepítsük a Pygments csomagot.
  2. Készítsünk vagy szerezzünk be egy CSS fájlt a codehilite számára, amit linkelünk a template-ünkben. Pl.: <link rel="stylesheet" type="text/css" href="./codehilite.css">

Már csak egy dolog van hátra: default felállásban a Django semmilyen változóból származó HTML kódot nem hajlandó értelmezni biztonsági okokból, ezért külön szólnunk kell neki, hogy mi megbízunk a html_content tartalmában és legyen szíves azt HTML kódnak látni. Ezt valamelyik vonatkozó template fájlban tehetjük meg a safe flag segítségével. Például:

1
2
3
<p id="story-body">
    {{ story.html_content|safe }}
</p>

Ezzel kész is vagyunk, most már mindenféle formázási lehetőség birtokában vagyunk, amit a Markdown támogat.

Az urls.py és views.py, valamint a template-ek tartalma a fenti feladat megoldása szempontjából irreleváns, bármelyik tutorialban választ kaphatunk rá. Éppen ezért itt nem is szerepeltetem.

Video Games Live

Évek óta szeretnék elmenni egy szimfonikus koncertre. Mióta megtudtam, hogy egy évben egy, esetleg két alkalommal filmzenei koncertet is szoktak rendezni Pesten, jobban rápörögtem a témára. Még októberben utánanéztem és kiderült, hogy már sajnos lecsúsztam az ideiről. De helyette lesz más.

Ahogy a neve is árulkodik róla, a Video Games Live egy olyan rendezvény, ahol videojátékok zenéit adja elő egy szimfonikus zenekar, kórussal (!) kiegészülve. Na hát nekem sem kellett több, azonnal rohantam jegyet vásárolni a legközelebbi honlapig. Bizony nem olcsó mulatság, a legpóriasabb jegy is 4990 HUF-ba fájt a leghátsó sorba, de ez egy igen exkluzív valami, menjünk!

A helyszín a Syma csarnok C terme volt. Mondanom sem kell, hogy ez egy hangár. Inkább kiállítási helyszín, mintsem koncertre alkalmas tér. Ezen aggályaim nem is voltak alaptalanok. Habár elsősorban az akusztikától féltem, azzal végül nem volt különösebb probléma. Annál inkább a berendezéssel. Egy ücsörgős koncertről van szó, viszont a székek annyira bénák voltak, hogy fél óra után már egyáltalán nem volt kényelmes. Csináltam is róla fényképet:

Video Games Live

Az előadás több, mint 2 órán át tartott, egy 15-20 perces szünettel a közepén. Maga a show eléggé rendben volt fényekkel, mindennel. A szervező csávó, bizonyos Tommy Tallarico villanygitáron be-beszállt némelyik dalba, valamint a konferanszié szerepét is eljátszotta. A dolog további érdekessége, hogy a mostani turnén a zenekar és a kórus tagjai mind magyarok voltak és csak most először játszottak idehaza. Fellépett még továbbá 2 dal erejéig egy Riva Taylor nevű csaj. Túl messze voltam, hogy jobban megnézzem, de elég szűk ruhában volt.

Az összes zenéből (kb. 20) talán 5-6 volt mindössze ismerős. Az első felvonásban szinte semmi olyan nem szólt, amit valaha hallottam volna. Többnyire japán Segás és PS-es játékok muzsikái mentek. Ezek nekem nem annyira jöttek be. A második félidő erősen indult, itt már több tetszetős dallam is felcsendült: Silent Hill, Uncharted (bár ez lehet, hogy még az elején volt), World of Warcraft. Ezek után viszont újra a japán játékok következtek.

Megmondom őszintén a katarzisélmény elmaradt. Kiderült, hogy nem vagyok annyira kocka, hogy túlságosan élvezzem. Tetszettek ugyan a felismert darabok, viszont sokkal több ilyet vártam. Szerettem volna hallani pl. a 3 Diabloból valamit, de ez sajnos nem teljesült. Mindenesetre nem bánom, hogy ott voltunk. Legközelebb inkább egy filmes koncertre megyünk egy arra megfelelő helyszínre (a Symát ebből a szempontból kerülni fogom).

Megnéztem az Interstellart

Jó volt.

A sztori különösebben nem nyűgözött le, viszont tetszett a pusztuló Föld itt megjelenített ötlete. De például az, hogy Coopert 1 óra alatt meggyőzték, hogy hagyja el két tizenéves gyerekét egy “kis” űrkalandért, nekem eléggé WTF élmény volt.

A látvány kifejezetten jól sikerült. Nem tolja az ember pofájába a sok felesleges CGI-t, csak ahol szükséges. Az ütött-kopott űrfelszerelés is sokkal befogadhatóbbá tette számomra az egészet. A hiperkocka megjelenítése nagyon pofás lett.

Ami a legjobban tetszett, az a film zenéje. Hans Zimmer egyébként is az egyik legnagyszerűbb kortárs filmzeneszerző, de ahogy a sci-fi világát párosította az orgonával, az valami zseniális:

Külön köszönöm, hogy nincsen hang az űrben!

Következő projekt

Van még egy dolog, amit már régóta meg kellett volna csinálnom:

aquarium

Véged lesz, mint a Twin Peaksnek!

A releváns rész 1:23-tól:

Nos, úgy tűnik Jock tévedett és nincs vége a Twin Peaksnek. A történet szerint 25 év - és a rágó, amit szerettem újra divatba jön. Innen folytatódik tehát a sorozat 2016-ban.

Bevallom, mindig félek a relaunch-októl és remake-ektől: valami, ami jó volt ott és akkor, az nem lesz már ugyanolyan jó itt és most. Némileg ugyan reménykeltő, hogy mind a 9 beharangozott epizódot David Lynch jegyzi majd rendezőként, akinek elborultsága a hosszú évek alatt sem kopott meg. A sorozatért a Showtime csenget és nekik általában sikeres sorozataik vannak (Dexter, Californication, Weeds, L). Nem hiszem, hogy pusztán csak a Twin Peaks cím belengetéséért kinyílt a pénztárca. Másrészt Lynch körül is nagy a csend mostanában - leszámítva a rövidfilmjeit, a 2006-os INLAND EMPIRE (így kell írni, nem a caps lock ragadt be) óta nem villantott semmit.

Black Lodge

A második évad végén nagy szerepet kapott a Black Lodge és Bob, így kíváncsian várom, hogy a harmadik hol és hogyan veszi fel a fonalat. A Black Lodge gyerekkori személyes kedvencem, remélem visszatérhetünk oda még egyszer. Viszont a Bobot megformáló “színész” sajnos már elhunyt, őt mindenképpen valaki más alakítja - ha felbukkan egyáltalán.

Hogyan készült? 1. rész

Ahogy arról már korábban megemlékeztem, nagyjából 2 évvel ezelőtt jött az ihlet, hogy én bizony itthon beüzemelek egy nagyon kicsi PC-t és azon lakik majd az én weblapom. Mielőtt ezt kitaláltam volna, mindenféle ingyenes és olcsó hosting szolgáltatásokat böngésztem. Teljesen fogalom nélkül voltam, de azt tudtam, hogy nem akarok sokat rákölteni és a lehető legkevesebb hirdetést szeretném látni az oldalamon. Ekkoriban hallottam először a bankkártya méretű, alacsony fogyasztású számítógépekről. Rövidesen kiderült, hogy mindent meg tudok valósítani saját magam.

Hardver

Választásom az akkoriban nagyon népszerű Raspberry Pi-re esett, azon belül is a combosabb B variánsra. A gépen egy 700 MHz-es ARM proci dolgozik, 512 MB RAM-mal megtámogatva. Található rajta 2 db USB 2.0 port, egy Ethernet csati, egy kompozit video kimenet, egy 3.5-es jack audio out, egy SDHC memóriakártya-foglalat és egy micro USB bemenet a delejnek. Itt jegyezném meg, hogy egy közönséges mobiltelefon töltőről üzemeltethető, amely képes 700 mA-t produkálni 5 V-on. Ha ezekkel az adatokkal számolunk, akkor folyamatos üzem mellett ez évi kb. 30 kWh fogyasztást jelent. A gépet kitben vásároltam az eBay-ről, ezért átlátszó műanyag tokozást is kaptam hozzá. Mivel Kínából jött, szép piros a NYÁK színe. Ezen kívül vettem még egy Class 10-es SanDisk SDHC kártyát 16 GB tárhellyel (azt hiszem szintén eBay-ről) és egy noname micro USB töltőt, ami a Pi-t táplálja. Az egész konstrukciót így megúsztam 16-17 ezer JMF-ből.

Raspberry Pi

Op. rendszer

Az üres SD kártyára fel kellett varázsolnom valamilyen operációs rendszert. Ma már rendelhető Málnáéktól előre installált kártya is, de azért ez nem agysebészet. Végigkövettem a leírást, majd a kártyáról bootoltam be a Pi-t. Első indításkor összedugtam a TV-mmel, csatlakoztattam bele billentyűzetet és egeret. A NOOBS (New Out Of the Box Software) felkínál néhány lehetőséget, csak rá kell bökni melyik oprendszert szeretnénk telepíteni. Mivel Ubuntu-féle Linuxokkal érzem magam a legjobban, a nagyon ajánlott Raspbian OS-t installáltam, ami végülis egy ARM-ra optimalizált Debian. Miután a Raspbian működőképessé vált felraktam rá egy SSH-t (meg VNC-t is, bár azt nem szoktam használni) és osztottam neki statikus IP-t (mert ugye közben rácsatlakoztattam az otthoni routerünkre). Innentől a TV-re, billentyűzetre és egérre többé már nem volt szükség.

Szoftver

Megszámlálhatatlanul sok fórumot és blogot áttúrtam, hogy megtaláljam az optimális software stacket. Két kiindulási pontom volt: Raspberry Pi és Django (Python 2.7 alapon). Ezek mellé kellett találnom gyors és lightweight adatbázist és webszervert. SQL? NoSQL? A Django teszt célokra és kisebb projektekhez az SQLite3-at ajánlja. Az igazság az, hogy egy blog kiszolgálásához bőven elég ez is. MySQL-t vagy Postgret telepíteni emiatt teljesen felesleges. Ha később mégsem jönne be, akkor MongoDB-vel fogok próbálkozni.

Ha webszerver, akkor a legtöbb embernek az Apache ugrik be legelőször. Kicsit talán ágyúval verébre, de nem lehetetlen. Én azért ettől lájtosabbat szerettem volna. Elolvastam néhány leírást, így lett aztán Nginx-Gunicorn kombó. Ebben a felállásban az Nginx HTTP proxyként a beérkező requesteket továbbítja egy statikus könyvtár vagy valamilyen applikáció (Gunicorn) felé. A Gunicorn egy socketen keresztül összeköti az Nginx-et a blogmotorral és ezen keresztül szolgálja ki a dinamikus tartalmakat.

A blogmotor maga pedig egy Django applikáció egy virtualenv-ben. A virtuális környezet különválasztja a blogot a rendszer Pythontól; alapja egy 2.7-es Python és egy Django 1.6-os installáció, melyekhez minden szükséges egyéb library is telepítve van.

Nagy vonalakban így áll össze az egész rendszer.

  • 1 / 2