W pliku strona/models.py powinniśmy dodać klasę reprezentującą tabelę w bazie MySQL:
from django.db import modelsTak przygotowana klasa będzie odpowiadać za przechowywanie linków na naszej stronie. Pole ID jest dodawane automatycznie, także jest tutaj pominięte w kodzie. Żeby dodać zmiany do bazy MySQL należy dodać do sekcji INSTALLED_APPS w pliku settings.py naszą aplikację: 'wygryzanko.strona'. Następnie wywołać:
class Link(models.Model):
url = models.CharField(max_length=200)
opis = models.CharField(max_length=255)
kiedy = models.DateTimeField()
#python manage.py syncdbPo czym powinna zostać dodana odpowiednia tabela do bazy - dla nas nie jest potrzebna jej znajomość, ponieważ będziemy operować bezpośrednio na obiektach, a Django zajmie się resztą.
Stwórzmy sobie katalog w projekcie o nazwie templates, a w nim plik strona.html o zawartości:
<html><head><title>{{ title }}</title></head><body><center><h1>{{ title }}</h1><hr>
<br>
<i>{% firstof info 'Przeglądasz wygryzarkę' %}</i>
<table style>
<tr style="background-color:gray">
<td>nr</td>
<td>url</td>
<td>opis</td>
<td>kiedy</td>
</tr>
{% for link in linki %}
<tr style="background-color:{% cycle '#917612' '#417682' %}">
<td>{{ forloop.counter }}</td>
<td>{% if forloop.first %}<a href="{{ link.url }}">{{ link.url }}</a>{% else %}{{ link.url }}{% endif %}</td>
<td>{{ link.opis }}</td>
<td>{{ link.kiedy|date:"d.m.Y H:i" }}</td>
</tr>
{% empty %}
<tr><td>brak wpisów - możesz być pierwszy</td></tr>
{% endfor %}
</table>
<br>
Dodaj własną stronę:
<form action="" method="post">
{% csrf_token %}
URL:<input type="text" name="url"><br>
Opis:<input type="text" name="opis"><br>
<input type="submit" value="Dodaj">
</form>
</center>
</body></html>
Omówię pokrótce tagi, które mogą być niezrozumiałe, jednak większość początkujący programiści powinni się domyśleć - w razie czego pytajcie w komentarzach.
{{ title }} - wyświetla zawartość przekazanej zmiennej 'title'Żeby można było zlokalizować szablon w projekcie, należy dodać odpowiedni wpis do settings.py, w sekcji TEMPLATE_DIRS. Uwaga ścieżka musi być absolutna - u mnie wygląda ona tak:
{% firstof info 'Przeglądasz wygryzarkę' %} - wyświetla zawartość pierwszej zmiennej, która nie jest nullem - jak widać, można podać też stałą wartość
{% cycle '#917612' '#417682' %} - za każdym odwołaniem wyświetla inną następną zmienną, gdy dojdzie do ostatniej - zaczyna od początku, powinno się używać wewnątrz pętli, inaczej będzie tylko jedno odwołanie, co mija się z celem tej funkcjonalności
{{ forloop.counter }} - wyświetla, w którym przebiegu pętli jesteśmy (zaczynamy od 1)
{{ link.kiedy|date:"d.m.Y H:i" }} - po | zaczynają się filtry. Tutaj akurat użyjemy filtru daty, podając jak mamy ją sformatować (d-dzień, m-miesiąc, Y-rok, H-godzina, i-minuta).
{% empty %} - co ma się dziać, gdy pętla for nie ma po czym iterować
{% csrf_token %} - specjalne pole typu hidden z unikalną wartością dla formularzy - jest to konieczne przy wysyłaniu POSTa z formularza osadzonego w szablonie.
"/home/krik/wygryzanko/szablon"Ostatnia czynnością, jaka nam została, by cieszyć się naszą nową stroną, to oprogramowanie całej logiki aplikacji :) Do tego celu edytujemy plik strona/views.py. Na początek dodajmy wszystkie importy, których będziemy używać:
from django.http import *Dlaczego użyjemy RequestContext do uzupełniania treści szablonu, a nie Context? Ponieważ, nasz szablon ma w sobie formularz a klasa Context, nie poradzi sobie z funkcją {% csrf_token %} , wymaganą w formularzach.
from django.template.loader import get_template #ładowanie szablonu z pliku
from django.template.context import RequestContext #Uzupełnianie szablonu
from models import * #możliwość używania naszej klasy Link z models.py
from django.utils.datetime_safe import datetime
import re
Po zaimportowaniu potrzebnych bibliotek, edytujmy pozostałości naszego poprzedniego "Hello World", teraz w funkcji index(req), umieścimy taki kod:
t=get_template('strona.html')Krótkie wyjaśnienie co się stało właściwie stało. Otóż, najpierw ładujemy nasz szablon z pliku strona.html. Dzięki wpisowi w settings.py, Django wie, gdzie szukać plików z szablonami. Następnie stworzyliśmy obiekt RequestContext, z ustawioną zmienną title - jest ona wykorzystywana w szablonie. Teraz trochę wyższa szkoła jazdy- Link.objects.order_by('kiedy').reverse()[0:10].all();.
c=RequestContext(req,{"title":"Django - wygryzanko (krikusio.blogspot.com)"})
linki=Link.objects.order_by('kiedy').reverse()[0:10].all();
c["linki"]=linki
if (req.method=='POST'):
c["info"]=nowy_wpis(req.POST)
return HttpResponse(t.render(c))
Ta linijka pobiera 10 ostatnio dodanych do bazy wpisów związanych z obiektem Link. Rozbiję te linijkę na mniejsze fragmenty:
Link - nasza klasa z models.py powiązana z bazą danychZamiast all() można było użyć get() - jednak get() podnośi wyjątek, gdy nie pobrało z bazy żadnych wpisów.
objects - dobiera się do obiektów z bazy
order_by('kiedy') - sortowanie po dacie dodania (domyślnie rosnąco)
reverse() - ustawiamy sortowanie malejące
[0:10] - pobieramy 10 obiektów, jest to zamieniane w MySQLu na LIMIT 10
all() - zamienia to co pobrało na listę
Po pobraniu danych z bazy, przypisujemy obiekty Link do utworzonego wcześniej RequestContext, by można było uzupełnić szablon. Innymi słowy - wyświetlić ostatnie 10 linków.
Teraz następuje sprawdzenie metody odwołania do strony przez przeglądarkę. Mamy do wyboru dwie POST i GET. Jeżeli jest to metoda POST, to wiemy, że użytkownik wypełnił i przesłał do nas formularz - jeżeli zaś GET, to jest to zwykłe odwołanie do strony. W przypadku metody POST (czyli wysłaniu formularza, co wiąże się z chęcią dodania nowego linku do naszej wygryzarki) powinniśmy przetworzyć wysłane dane z formularza. Tutaj odpowiada za to:
c["info"]=nowy_wpis(req.POST)Przypiszemy informację (o powodzeniu lub niepowodzeniu) do RequestContext, a w celu lepszej przejżystości kodu, dane z POST zostaną przetworzone przez funkcję nowy_wpis, którą za chwilę utworzymy.
Ostatnim krokiem jest już wyrenderowanie z szablonu i danych, kodu HTML do wyświetlenia po stronie przeglądarki - za to odpowiada ostatnia linijka.
Teraz stworzymy funkcję nowy_wpis, odpowiedzialną za dodawanie nowych linków i wszystkich mechanizmów z tym związanych (tj. usuwanie niepotrzebnych wpisów itd.):
def nowy_wpis(post):Pierwsze pytanie jakie się nasuwa to dlaczego używam post.get('url','') zamiast post['url']? Otóż post['url'] wywoła wyjątek, gdy takie pole nie będzie przekazane przez formularz, natomiast post.get(), w przypadku nieprzekazania takiego pola, podstawi wartość z drugiego parametru - bardzo wygodne rozwiązanie. Następnie odbywa się, zwykła weryfikacja - czy pola nie są puste, oraz czy link jest w miarę poprawny. Funkcja zwraca za każdym razem String'a, który jest używany do wyświetlenia komunikatu po stronie przeglądarki.
urlX=post.get('url','')
opisX=post.get('opis','')
if (urlX==''):
return 'Brak podanego linku'
if re.match(r"https?://([^\.]+\.)+[a-z]{2,6}", urlX, re.IGNORECASE)==None:
return urlX+' nie jest poprawnym adres strony'
if (opisX==''):
return 'Brak podanego opisu'
if ( Link.objects.filter(url__istartswith=urlX).count()>0):
return 'Taki wpis w bazie już istnieje - daj szansę innym'
nowy=Link(url=urlX,kiedy=datetime.now(),opis=opisX)
nowy.save()
linki=Link.objects.order_by('kiedy').reverse()[9:10].all();
if (len(linki)==1):
Link.objects.filter(kiedy__lt=linki[0].kiedy).delete();
return 'Wpis został dodany do bazy'
Dokładniej skupię się na opisaniu jedynie weryfikacji, czy podobny link nie widnieje już przypadkiem w naszej bazie:
if ( Link.objects.filter(url__istartswith=urlX).count()>0):Skupmy się na chwilę na url__istartswith. Ogólna konwencja jest taka, że najpierw określamy pole jakie podlega filtrowaniu (tutaj url), a po dwóch znakach podkreślenia, określamy jaki rodzaj filtru zastosować (tutaj istartswith). Zamiast istartswith, moglibyśmy użyć startswith, jednak przedrostek "i", oznacza 'ignorecase' - czyli ignorowanie wielkości liter. Filtry są zamieniane na zapytanie zawierające "WHERE" a url__istartswith na "ILIKE 'napis%'. Natomiast count() to odpowiednik count(*) z SQL'a.
Link.objects - obiekty typu Link z bazy danych
filter - ograniczenia co do obiektów, jakie mają być pobrane z bazy
url__istartswith=urlX - pole url, musi się zaczynać od wartości urlX (tej z formularza).
count() - ile pobrano obiektów z bazy
Jak można wywnioskować z kodu - bardzo prosto dodać nowe wpisy do bazy. Wystarczą dwie linijki:
nowy=Link(url=urlX,kiedy=datetime.now(),opis=opisX)W pierwszej tworzymy nowy obiekt, a w drugiej jest on dodawany do bazy. Co ciekawe po wywolaniu save() ten obiekt będzie posiadał wypełnione pole id, takie same jakie otrzymał w bazie danych.
nowy.save()
Jako, że po wyświetlamy 10 ostatnich wpisów, przy dodawaniu można usunąć pozostałe, czyli:
linki=Link.objects.order_by('kiedy').reverse()[9:10].all();Z wcześniejszych wyjaśnień, wiemy już, że pobieramy ostatni, wyświetlany element (czyli 10). Metoda all(), zamienia nam pobrane dane na listę obiektów, toteż w następnej linijce sprawdzamy, czy coś zostało pobrane z bazy. Ostatnia linijka, może wymagać trochę więcej wyjaśnienia:
if (len(linki)==1):
Link.objects.filter(kiedy__lt=linki[0].kiedy).delete();
filter - użyjemy warunku "WHERE"Prawda, że proste :D Przyznam, że ta koncepcja Django, ukrywania bazy przed programistą, wymaga przyzwyczajenia. Lecz podejście takie, po głębszym zbadaniu jest bardzo przyjazne. Powodzenia!
kiedy__lt - warunkiem będzie sprawdzenie czy pole 'kiedy', jest mniejsze niż linki[0].kiedy, czyli data dodania dziesiątego w kolejności wyświetlanai wpisu.
delete(); - oznacza, że obiekty te zostaną usunięte z bazy.
Brak komentarzy:
Prześlij komentarz