Administrator strony (superuser) będzie miał możliwość modyfikowania informacji o apartamentach i cenach. Będzie mogł także przeglądać listę zarejestrowanych użytkowników, usuwać użytkowników i wysyłac do nich maile.
Wejście na stronę adminstratora Wyświetlanie i edycja cenników Edycja opisu apartamentów Podgląd rezerwacji
urlpatterns = patterns('', (r'^$',main_page), (r'^register/$',register_page), (r'^media/(?P.*)$','django.views.static.serve',{'document_root': media_dir}), (r'^admin/$',admin_page), ...
# _*_ coding: utf-8 _*_ from django.http import HttpResponse,HttpResponseRedirect ... from bookings.models import Apartment,User,Cena,TypCeny from bookings.forms import * ... @permission_required('user.is_superuser',"/login/") def admin_page(request): template = get_template("admin.html") apartments = Apartment.objects.all() types = TypCeny.objects.all() variables = RequestContext(request,{'apartments':apartments,'types':types}) output = template.render(variables) return HttpResponse(output)Powyższy kod zawiera dwie nowe tabele Cena i TypCeny, zostaną one opisane w następnym rozdziale.
{% extends "base.html"%} {% block title %} - strona administratora{% endblock %} {% block content%} <p> <div style="text-align:center;"><a href="/logout">Wylogowanie</a></div> </p> <p> <h2>Lista apartamentów</h2> <ul> <hr width="50%" align="left"> {% for apartment in apartments %} <li style="list-style-type:none;width:50%;">{{apartment.name}}<div id="nav"> <a href="/save_apartment/?id={{apartment.id}}">Edycja</a> <a href="/all_prices/{{apartment.id}}">Cennik</a></div> <br>Sypialnie: {{apartment.bedrooms}} <br>Łóżka: {{apartment.beds}} <br>{% if apartment.kitchenette %} Posiada aneks kuchenny {% else %} Nie posiada aneksu {% endif %} <br>{% if apartment.pathToPicture %} <img src="/media/{{apartment.pathToPicture}}" alt="Brak zdjęcia" width="100px"/> {% else %} Brak informacji o zdjęciu {% endif %} <hr width="100%" align="left"> </li> {% endfor %} <li style="list-style-type:none;"><a href="/save_apartment/">Dopisanie</a></li> </ul> </p> <hr> <p> <h2>Rodzaje cen</h2> <ul> <hr width="50%" align="left"> {% for type in types %} <li style="list-style-type:none;width:50%;"> <form style="display:inline;"><input type="checkbox" disabled {% if type.active %}checked="checked"{% endif %}></form> {{type.description}}<div id="nav"><a href="/save_type/?id={{type.id}}">Edycja</a> <a href="/delete_type/?id={{type.id}}">Usunięcie</a></div> </li> {% endfor %} <hr width="50%" align="left"> <li style="list-style-type:none;"><a href="/save_type/">Dopisanie</a></li> </ul></p> <hr> <p> <h2>Zarejestrowani użytkownicy</h2> </p> {% endblock %} {% block footer %} <p> <div style="text-align:center;"><a href="/logout">Wylogowanie</a></div> </p> {% endblock %}
from django.db import models class Apartment(models.Model): ... class TypCeny(models.Model): description = models.CharField(max_length=60,unique=True) active = models.BooleanField() class Cena(models.Model): price = models.SmallIntegerField() type = models.ForeignKey(TypCeny) apartment = models.ForeignKey(Apartment)Klasa TypCeny zawiera teksty typu Cena w weekend majowy, Cena w sezonie, z wyżywieniem,... oraz pole logiczne active pozwalające na tymczasowe ukrywanie przed użytkownikiem pewnych cen. W klasie Cena definiujemy dwa klucze obce wiążące cenę z apartamentem i typem (opisem) ceny. Kod SQL tworzący tabelę Cena wygląda tak:
CREATE TABLE "bookings_cena" ( "id" integer NOT NULL PRIMARY KEY, "price" smallint NOT NULL, "type_id" integer NOT NULL REFERENCES "bookings_typceny" ("id"), "apartment_id" integer NOT NULL REFERENCES "bookings_apartment" ("id") ) ; COMMIT;Domyślnie klasa ForeignKey wiąże rekordy z dwóch tabel używając pola id, a dokładniej, używa klucza głównego. Można to zachowanie zmienić: type = models.ForeignKey(TypCeny,to_field=nazwa_pola). Inne ważne domyślne zachowanie, to kaskadowe usuwanie. Jeżeli usuniemy rekord z tabeli TypCeny, to automatycznie usunięte zostaną wszystkie rekordy z tabeli Cena powiązane z usuniętym rekordem. To zachowanie też można zmienić: type = models.ForeignKey(TypCeny,on_delete=pożądane_zachowanie).
{% extends "base.html"%} {% block header %} ... {% endblock %} {% block content %} <p> <div style="text-align:center;"> <h2>Strona domowa pensjonatu "Pod kwiatami"</h2> E-mail: <b><tt>podkwiatami@gmail.com</tt></b> <br>Telefon: <b><tt>123-456-789</tt></b> ... <h2>Lista apartamentów</h2> <table border="0px" width="70%"> {% for apartment in apartments %} <tr style="text-align:center;"> <td style="text-align:left;font-size:14pt;"> ... <p class="br"><a href="/prices/{{apartment.id}}">Cennik</a> ... </td> ... </tr> {% endfor %} </table>Mapowanie w pliku urls.py:
(r'^prices/(\w+)/$',prices_page),Fragment adresu \w+ oznacza dowolny ciąg złożony ze znaków alfanumerycznych i znaku podkreślenia. Umieszczenie go w nawiasach powoduje przekazanie do funkcji prices_page drugiego argumentu (nadamy mu nazwę id_app).
Możliwe jest też inne rozwiązanie:
<a href="/prices/?id={{apartment.id}}">Cennik</a>
(r'^prices/$',prices_page),
def prices_page(request): id_app = request.GET['id'] template = get_template("prices.html") apartment = Apartment.objects.get(id=id_app) prices = Cena.objects.select_related().filter(apartment=id_app) variables = RequestContext(request,{'apartment':apartment,'prices':prices}) output = template.render(variables) return HttpResponse(output)
def prices_page(request,id_app): template = get_template("prices.html") apartment = Apartment.objects.get(id=id_app) prices = Cena.objects.select_related().filter(apartment=id_app) variables = RequestContext(request,{'apartment':apartment,'prices':prices}) output = template.render(variables) return HttpResponse(output)Funkcja Cena.objects.select_related().filter(apartment=id_app) zwraca listę wszystkich cen powiązanych z apartamentem, którego klucz główny jest równy id_app. Funkcja filter może mieć więcej argumentów. filter(apartment=id_app,type=2) zwróci listę cen powiązanych z apartamentem którego klucz główny jest równy id_app oraz z typem (opisem) ceny, którego klucz główny jest równy 2.
{% extends "base.html"%} {% block title %} - apartament {{apartment.name}}{% endblock %} {% block header %}<h2><center>Apartament {{apartment.name}} - cennik</center></h2>{% endblock %} {% block content%} <p> <div style="text-align:center;">{% if apartment.pathToPicture %} <img src="/media/{{apartment.pathToPicture}}" />{% endif %}</div> </p> <center> <div style="margin-left:30%;margin-right:30%;"> <fieldset> <table border="0px"> <tr> <th>Rodzaj ceny</th> <th>Cena za apartament (zł.)</th> </tr> {% for price in prices %} <tr> <td style="text-align:left;">{{price.type.description}}</td> <td style="text-align:right;">{{price.price}}</td> </tr> {% endfor %} </table> </fieldset> </div> </center> {% endblock %} {% block footer %} <p> <div style="text-align:center;"><a href="/">Główna strona</a></div> </p> {% endblock %}price to obiekt typu Cena, price.type to powiązany z nim obiekt typu TypCeny.
Administrator ma możliwość edytowania tabeli TypCeny.
Pliki html używane podczas edycji można umieścić gdziekolwiek w katalogu templates, dla zachowania porządku będziemy je umieszczać w podkatalogu
templates/edition.
Fragment pliku admin.html:
<h2>Rodzaje cen</h2> <ul> <hr width="50%" align="left"> {% for type in types %} <li style="list-style-type:none;width:50%;"> <form style="display:inline;"><input type="checkbox" disabled {% if type.active %}checked="checked"{% endif %}></form> {{type.description}}<div id="nav"><a href="/save_type/?id={{type.id}}">Edycja</a> <a href="/delete_type/?id={{type.id}}">Usunięcie</a></div> </li> {% endfor %} <hr width="50%" align="left"> <li style="list-style-type:none;"><a href="/save_type/">Dopisanie</a></li> </ul></p>Dopisujemy w pliku urls.py mapowania:
(r'^save_type/$',save_type_page), (r'^delete_type/$',delete_type_page),Tworzymy pliki templates/type_delete.html:
{% extends "base.html"%} {% block title %} - usunięcie opisu ceny{% endblock %} {% block content%} <center> <h2>Usunięcie opisu ceny</h2> <p> <div stylemargin-left:35%;margin-right:35%;"> <fieldset> <form method="post" action=".">{% csrf_token %} Opis: <b>{{type.description}}</b> <input type="hidden" name="id" value="{{ type.id }}" /> <p><input type="submit" value="Usunięcie"/></p> </form> </fieldset> </div> </center> {% endblock %}oraz templates/type_save.html:
{% extends "base.html"%} {% block title %} - edycja opisu ceny{% endblock %} {% block content%} <center> <h2>Edycja opisu ceny</h2> <p> <div style="margin-left:35%;margin-right:35%;"> <fieldset> <form method="post" action=".">{% csrf_token %} <div style="text-align:left";>{{ form.as_p }}</div> <input type="hidden" name="id" value="{{ type.id }}" /> <input type="submit" value="Zapisanie"/> <input type="reset" value="Wartości dotychczasowe"/> </form> </fieldset> </div> </center> {% endblock %}Oba pliki zawierają formularze, w obu przypadkach zatwierdzenie formularza przekierowuje na tę samą stronę (action="."). Funkcje obsługujące te strony rozpoznają sposób wejścia na stronę poprzez metodę żądania POST lub GET. Definiujemy w pliku views.py funkcje save_type_page i delete_type_page:
@permission_required('user.is_superuser',"/login/") def delete_type_page(request): if request.method == 'POST': id = request.POST['id'] type = TypCeny.objects.get(id=id) type.delete() template = get_template("admin.html") apartments = Apartment.objects.all() types = TypCeny.objects.all() variables = RequestContext(request,{'apartments':apartments,'types':types}) output = template.render(variables) return HttpResponse(output) else: id = request.GET['id'] type = TypCeny.objects.get(id=id) template = get_template("edition/type_delete.html") variables = RequestContext(request,{'type':type}) output = template.render(variables) return HttpResponse(output)Metoda TypCeny.objects.get(id=id) zwraca obiekt - rekord w tabeli TypCeny spełniający podany warunek lub generuje wyjątek ObjectDoesNotExist. W naszym programie może się tak zdarzyć, jeżeli jednocześnie jest zalogowanych dwóch administratorów, i obaj usuwają ten sam rekord z tabeli TypCeny. Metoda type.delete() usuwa obiekt - rekord z tabeli.
@permission_required('user.is_superuser',"/login/") def save_type_page(request): if request.method == 'POST': form = FormularzEdycjiTypu(request.POST,True) if form.is_valid(): id = request.POST['id'] if len(id) == 0: type = TypCeny() else: type = TypCeny.objects.get(id=int(id)) type.description = form.cleaned_data['description'] type.active = form.cleaned_data['active'] type.save() template = get_template("admin.html") apartments = Apartment.objects.all() types = TypCeny.objects.all() variables = RequestContext(request,{'apartments':apartments,'types':types}) output = template.render(variables) return HttpResponse(output) elif request.GET.has_key('id'): id = request.GET['id'] type = TypCeny.objects.get(id=id) form = FormularzEdycjiTypu({'description':type.description,'active':type.active},False) template = get_template("edition/type_edition.html") variables = RequestContext(request,{'form':form,'type':type}) output = template.render(variables) return HttpResponse(output) else: form = FormularzEdycjiTypu({'active':True},False) template = get_template("edition/type_edition.html") variables = RequestContext(request,{'form':form}) output = template.render(variables) return HttpResponse(output)Jeżeli żądanie jest typu GET, to możliwe są dwa przypadki:
class FormularzEdycjiTypu(forms.Form): def __init__(self,dictionary,control): forms.Form.__init__(self,dictionary) self.control = control id = forms.IntegerField(required=False,widget=forms.HiddenInput()) description = forms.CharField(label="Opis:",max_length=60,widget=forms.TextInput({'size':60})) active = forms.BooleanField(label="Aktywna:",required=False) def clean_description(self): if not self.control: return description description = self.cleaned_data['description'] id = self.cleaned_data['id'] if len(description) == 0: raise forms.ValidationError("To pole musi być wypełnione") try: type = TypCeny.objects.get(description=description) if type.id == id: return description except ObjectDoesNotExist: return description raise forms.ValidationError("Taki opis już występuje")Kilka fragmentów tego formularza jest nietypowych.
def __init__(self,dictionary,control): forms.Form.__init__(self,dictionary) self.control = controlKażdy fomularz dziedziczący po forms.Form ma konstruktor jednoargumentowy. Tym argumentem jest słownik. Powyższy konstruktor ma dodatkowy argument logiczny control decydujący czy funkcja clean_description wykonuje jakiekolwiek sprawdzanie.
Opiszemy teraz edycję samych cen. Nie będziemy korzystać z formularzy oferowanych przez Django. Formularze te są dostosowane do edytowania obiektu -
rekordu w tabeli, a my będziemy na jednym formularzu edytowali wszystkie ceny jednego apartamentu.
Opis formularza umieścimy w pliku edition/prices_edition.html:
{% extends "base.html"%} {% block title %} - apartament {{apartment.name}}{% endblock %} {% block header %}<h3><center style="white-space:pre;">Apartament: {{apartment.name}} - cennik</center></h3>{% endblock %} {% block content%} <center> <div style="margin-left:20%;margin-right:20%;"> <fieldset> <legend> {{ legend }} </legend> <form method="post" action=".">{% csrf_token %} <table border="0px"> <tr> <th width="50%">Rodzaj ceny</th> <th width="50%">Cena za apartament (zł.)</th> </tr> {% for type in types %} <tr> <td style="text-align:left;"> <input type="checkbox" disabled {% if type.active %}checked="checked"{% endif %}/> {{type.description}} </td> <td style="text-align:right;"><input type="text" name="{{type.id}}" value="{{type.price}}"/></td> </tr> {% endfor %} </table> <input type="submit" value="Zapisanie"/> <input type="reset" value="Wartości dotychczasowe"/> </fieldset> </form> </div> </center> {% endblock %} {% block footer%} <p> <div style="text-align:center;"><a href="/admin">Strona administratora</a></div> </p> {% endblock %}Informacja o aktywności rodzaju cen jest tylko wyświetlana, edytować można wyłącznie wartość ceny.
@permission_required('user.is_superuser',"/login/") def all_prices_page(request,id_app): legend = '' if request.method == 'POST': for key in request.POST.keys(): ok = True if key.isdigit(): id_type = int(key) try: price_value = int(request.POST[key]) save = (price_value>0) ok = (price_value>=0) except ValueError: ok = False else: ok = False if ok: legend = 'Nowe ceny zostały zapisane' if save: try: cena = Cena.objects.get(type=id_type,apartment=id_app) cena.price = price_value except ObjectDoesNotExist: cena = Cena(price=price_value,type_id=id_type,apartment_id=id_app) cena = Cena.objects.create(price=price_value,type=TypCeny.objects.get(id=id_type),apartment=Apartment.objects.get(id=id_app)) cena.save() else: try: cena = Cena.objects.get(type=id_type,apartment=id_app) cena.delete() except ObjectDoesNotExist: pass template = get_template("edition/prices_edition.html") apartment = Apartment.objects.get(id=id_app) types = TypCeny.objects.all() for typ in types: try: typ.price = Cena.objects.select_related().get(apartment=id_app,type=typ.id).price except ObjectDoesNotExist: typ.price = 0 variables = RequestContext(request,{'apartment':apartment,'types':types,'legend':legend}) output = template.render(variables) return HttpResponse(output)Nasz formularz nie dziedziczy po klasie django.forms.Form, musimy zatem samodzielnie przeprowadzić walidację wpisanych cen. Walidacja odbywa sie w pętli po wszystkich cenach, każda poprawna cena jest zapisywana - również wtedy gdy na formularzu znajdują się niepoprawne ceny. Cena jest poprawna jeżeli:
except ObjectDoesNotExist: cena = Cena(price=price_value,type_id=id_type,apartment_id=id_app) cena = Cena.objects.create(price=price_value,type=TypCeny.objects.get(id=id_type),apartment=Apartment.objects.get(id=id_app))Oczywiście, potrzebny jest tylko jeden z powyższych wierszy.
class Cena(models.Model): price = models.SmallIntegerField() type = models.ForeignKey(TypCeny) apartment = models.ForeignKey(Apartment)musimy jakoś zdobyć informacje, że powstały pola o nazwach type_id oraz apartment_id,
for typ in types: try: typ.price = Cena.objects.select_related().get(apartment=id_app,type=typ.id).price except ObjectDoesNotExist: typ.price = 0wykorzystuje możliwość dynamicznej zmiany struktury obiektu. Zmienna typ jest typu TypCeny, a w tej klasie nie ma pola price.
<h2>Lista apartamentów</h2> <ul> <hr width="50%" align="left"> {% for apartment in apartments %} <li style="list-style-type:none;width:50%;">{{apartment.name}}<div id="nav"> <a href="/save_apartment/?id={{apartment.id}}">Edycja</a> <a href="/all_prices/{{apartment.id}}">Cennik</a></div> <br>Sypialnie: {{apartment.bedrooms}} <br>Łóżka: {{apartment.beds}} <br>{% if apartment.kitchenette %} Posiada aneks kuchenny {% else %} Nie posiada aneksu {% endif %} <br>{% if apartment.pathToPicture %} <img src="/media/{{apartment.pathToPicture}}" alt="Brak zdjęcia" width="100px"/> {% else %} Brak informacji o zdjęciu {% endif %} <hr width="100%" align="left"> </li> {% endfor %} <li style="list-style-type:none;"><a href="/save_apartment/">Dopisanie</a></li> </ul>W pliku urls.py dopisujemy mapowanie:
(r'^save_apartment/$',save_apartment_page)W pliku forms.py opisujemy formularz:
class FormularzEdycjiApartamentu(forms.Form): name = forms.CharField(label="Opis:",max_length=80,widget=forms.TextInput({'size':80})) bedrooms = forms.IntegerField(label="Sypialnie:",min_value=1,max_value=4) beds = forms.IntegerField(label="Miejsca:",min_value=1,max_value=12) kitchenette = forms.BooleanField(label="Aneks kuchenny:",required=False) file = forms.FileField(label="Zdjęcie:",required=False)Nowością jest pole typu FileField pozwalające uruchamić standardowe okno dialogowe wyboru pliku.
{% extends "base.html"%} {% block title %} - edycja opisu apartamentu{% endblock %} {% block content%} <center> <h2>Edycja opisu apartamentu</h2> <p> <div style="margin-left:35%;margin-right:35%;"> <fieldset> <form enctype="multipart/form-data" method="post" action=".">{% csrf_token %} <div style="text-align:left";>{{ form.as_ul }}</div> <input type="hidden" name="id" value="{{ apartment.id }}" /> <input type="submit" value="Zapisanie"/> <input type="reset" value="Wartości dotychczasowe"/> </form> </fieldset> </div> </center> {% endblock %}Nowością jest fragment enctype="multipart/form-data", który zapewnia, że żądanie request zawiera słownik request.FILES z informacją o wybranym pliku. Fragment {{ form.as_ul }} powoduje, że formularz jest wyświetlany w postaci listy nienumerowanej.
@permission_required('user.is_superuser',"/login/") def save_apartment_page(request): if request.method == 'POST': form = FormularzEdycjiApartamentu(request.POST,request.FILES) if form.is_valid(): id = request.POST['id'] if len(id) == 0: apartment = Apartment() else: apartment = Apartment.objects.get(id=int(id)) apartment.name = form.cleaned_data['name'] apartment.kitchenette = form.cleaned_data['kitchenette'] apartment.bedrooms = form.cleaned_data['bedrooms'] apartment.beds = form.cleaned_data['beds'] if request.FILES: f = request.FILES['file'] destination = open("media/"+str(f),'wb+') for chunk in f.chunks(): destination.write(chunk) destination.close() apartment.pathToPicture = str(f) apartment.save() template = get_template("admin.html") apartments = Apartment.objects.all() types = TypCeny.objects.all() variables = RequestContext(request,{'apartments':apartments,'types':types}) output = template.render(variables) return HttpResponse(output) elif request.GET.has_key('id'): ap_type = int(request.GET['id']) apartment = Apartment.objects.get(id=ap_type) form = FormularzEdycjiApartamentu({'name':apartment.name,'bedrooms':apartment.bedrooms,'beds':apartment.beds,'kitchenette':apartment.kitchenette}) template = get_template("edition/apartment_edition.html") variables = RequestContext(request,{'form':form,'apartment':apartment}) output = template.render(variables) return HttpResponse(output) else: form = FormularzEdycjiApartamentu({'kitchenette':True}) template = get_template("edition/apartment_edition.html") variables = RequestContext(request,{'form':form}) output = template.render(variables) return HttpResponse(output)Pogrubiony fragment sprawdza czy użytkownik wskazał plik, jeśli tak, to wskazany plik jest kopiowany na serwer, a informacja o nazwie pliku jest zapamiętywana w tabeli Apartment.
<h2>Lista apartamentów</h2> <ul> <hr width="50%" align="left"> {% for apartment in apartments %} <li style="list-style-type:none;width:50%;">{{apartment.name}}<div id="nav"> <a href="/save_apartment/?id={{apartment.id}}">Edycja</a> <a href="/all_prices/{{apartment.id}}">Cennik</a> <a href="/one_room/1?id={{apartment.id}}">Rezerwacje</a></div> <br>Sypialnie: {{apartment.bedrooms}} ... {% endfor %} <li style="list-style-type:none;width:50%;"> <div id="nav"><a href="/all_rooms/1">Rezerwacje zbiorczo</a></div> <br> <hr width="100%" align="left"> </li>
(r'^one_room/(\w+)$',one_room_page), (r'^all_rooms/(\w+)$',all_rooms_page),użycie wyrażenia (\w+) powoduje, że do funkcji one_room_page oraz all_rooms_page przekazane zostaną dwa parametry: request i drugi o dowolnej nazwie oraz wartości "1".
{% block title %} - rezerwacje {% endblock %} {% block content%} <center> <h2>Wszystkie apartamenty - rezerwacje w miesiącu {{ month }}.{{ year }}</h2> <hr> <table width="100%" border="1px"> <tr> <th width="14%" align="center">Poniedziałek</th> <th width="14%" align="center">Wtorek</th> <th width="14%" align="center">Środa</th> <th width="14%" align="center">Czwartek</th> <th width="14%" align="center">Piątek</th> <th width="14%" align="center">Sobota</th> <th width="14%" align="center">Niedziela</th> </tr> {% for week in weeks %} <tr> {% for day in week %} <td style="text-align:center;" bgcolor="{{ day.color }}">{{ day.header}}<br><br>{{day.content}}<br><br></td> {% endfor %} </tr> {% endfor %} </table> </center> <hr> <table border="1px" width="50%"> <caption>Znaczenie kolorów i liczb</caption> <tr> <td bgcolor="#fdf5e5" width="33%" align="center">Wszystkie wolne<br>Ilość wolnych</td> <td bgcolor="#7ae68e" width="33%" align="center">Niektóre wolne<br>Ilość wolnych</td> <td bgcolor="#ff0000" width="33%" align="center">Wszystkie zarezerwowane</td> </tr> </table> {% endblock %} {% block footer %} <div style="text-align:center;">{% if admin %}<a href="/admin/">Strona administratora</a>{% else %}<a href="/">Główna strona</a>{% endif %} <a href="/all_rooms/{% if admin %}1{%else%}0{%endif%}?&month={{next_month}}&year={{next_year}}">Następny miesiąc</a> <a href="/all_rooms/{% if admin %}1{%else%}0{%endif%}?&month={{prev_month}}&year={{prev_year}}">Poprzedni miesiąc</a> </div> {% endblock %}Funkcja renderująca ten plik musi otrzymać mnóstwo informacji, część jest prosta (aktualny miesiąc i rok, następny miesiąc i rok, poprzedni miesiąc i rok, czy wejście nastąpiło ze strony administratora), część dosyć skomplikowna. Lista weeks jest listą list, wewnętrzne listy zawierają po siedem obiektów typu Day. Prosta klasa Day nie należy do bibliotek Pythona i Django, została napisana na potrzeby tego programu.
Poniższy kod musi się znaleźć w pliku views.py:
Ze względu na operacje na datach (dodawanie, formatowanie, parsowanie napisów) konieczny jest import:
from datetime import *Potrzebne będą cztery funkcje pomocnicze: dwie które dla pary liczb (miesiąc i rok) zwracają dwuelementową krotkę: miesiąc i rok następny lub miesiąc i rok poprzedni, jedna, która dla tych samych argumentów zwraca obiekt typu date reprezentujący ostatni poprzedzający poniedziałek i jedna, która zwraca datę pierwszej niedzieli po:
def next_month_year(month,year): if month == 12: return (1,year+1) else: return (month+1,year) def prev_month_year(month,year): if month == 1: return (12,year-1) else: return (month-1,year) def last_monday(month,year): d = date(year,month,1) while d.weekday()>0: d-=timedelta(days=1) return d def first_sunday(month,year): next = next_month_year(month,year) d = date(next[1],next[0],1) while d.weekday()<6: d+=timedelta(days=1) return dDwie ostatnie funkcje korzystają ze standardowej klasy date z modułu datetime oraz z metody timedelta z tego samego modułu pozwalającej dodawać liczby całkowite do daty.
class Day: def __init__(self,date,color="#fdf5e5",content=""): self.date = date self.content = content self.color = color self.header = date.strftime('%d.%m.%y')Służy tylko do zebrania informacji o konkretnym dniu, funkcja date.strftime z modułu datetime formatuje datę do postaci '27.07.12'.
def all_rooms_page(request,args): year = date.today().year month = date.today().month if request.GET.has_key('year'): year = int(request.GET['year']) if request.GET.has_key('month'): month = int(request.GET['month']) admin = (args == '1') next = next_month_year(month,year) prev = prev_month_year(month,year) weeks = [] week =[] day = last_monday(month,year) last_day = first_sunday(month,year) number_of_rooms = Apartment.objects.count() while day<=last_day: bookings = Booking.objects.filter(date=day) if len(bookings) == 0: week.append(Day(day,content=str(number_of_rooms))) elif len(bookings) == number_of_rooms: week.append(Day(day,color="#ff0000")) else: week.append(Day(day,color="#7ae68e",content=str(number_of_rooms-len(bookings)))) if len(week) == 7: weeks.append(week) week = [] day+=timedelta(days=1) template = get_template("show_all_rooms.html") variables = RequestContext(request,{'month':month,'year':year,'next_month':str(next[0]),'next_year':str(next[1]),'prev_month':str(prev[0]),'prev_year':str(prev[1]),'weeks':weeks,'admin':admin}) output = template.render(variables) return HttpResponse(output)Metoda date.today() zwraca aktualna datę. Zatem jeżeli administrator wchodzi na tę stronę bezpośrednio ze "swojej" strony to słownik request.GET jest pusty, zatem wyświetlane są rezerwacje z bieżącego miesiąca. W przeciwnym przypadku, informacja o miesiącu (i roku) jest zawarta w słowniku request.GET.
SELECT * FROM Booking WHERE date = day. Długość tej listy, to ilość apartamentów zarezerowanych w dniu day. Struktura tabeli Booking zostanie omówiona na następnym wykładzie.