Zarządzanie użytkownikami Rezerwowanie noclegów Źródła opisywanego programu
def admin_page(request):
template = get_template("admin.html")
apartments = Apartment.objects.all()
types = TypCeny.objects.all()
users = User.objects.order_by('username')
if request.method == 'POST' and request.POST.has_key('first'):
first = int(request.POST['first'])
if request.POST.has_key('back'):
first = first - 10
if request.POST.has_key('forward'):
first = first + 10
else:
first = 0
variables = RequestContext(request,{'apartments':apartments,'types':types,'users':users[first:first+10],'number_of_users':len(users),'first':first,'end':(first+10>=len(users))})
output = template.render(variables)
return HttpResponse(output)Metoda User.objects.order_by('username') zwraca listę wszystkich użytkowników uporządkowana wg wartości pola
username, do funkcji renderującej przekazywany jest tylko 10-elementowy fragment tej listy ('users':users[first:first+10]), przekazywana jest również
informacja czy jest to początkowy fragment (klucz 'first' w słowniku) i czy jest to końcowy fragment (klucz 'end'). Wartość klucza 'first'
jest numeryczna, wartośc klucza klucz 'end' jest logiczna. Ta różnica nie ma to znaczenia ze względu na niejawne konwersje int => bool.
Fragment pliku admin.html odpowiedzialny za wyświetlanie użytkowników i (warunkowe) umieszczenie przycisków Wcześniejsi i Następni:
<h2>Zarejestrowani użytkownicy ({{number_of_users}})</h2>
<ul>
{% if first %}<form style="display:inline;" method="post" action="/admin/">{% csrf_token %}<button type="submit" value="">Wcześniejsi<br><img src="/media/back.gif" width="16px"></button>
<input type="hidden" name="back"><input type="hidden" name="first" value="{{first}}"></form>{% endif %}
<hr width="50%" align="left">
{% for user in users %}
<li style="list-style-type:none;width:50%;">
<form style="display:inline;"><input type="checkbox" disabled {% if user.is_superuser %}checked="checked"{% endif %}></form>
{{user.username}} {{user.email}} <div id="nav"><a href="/save_user/{{user.id}}">Edycja</a>
{% if not user.is_superuser %} <a href="/delete_user/{{user.id}}">Usunięcie</a>{% endif %}
{% if user.email %} <a href="/send_mail/?id={{user.id}}">Wysłanie maila</a>{% endif %}</div>
</li>
{% endfor %}
<hr width="50%" align="left">
{% if not end %}<form style="display:inline;" method="post" action=".">{% csrf_token %}<button type="submit" value="">Następni<br><img src="/media/forward.gif" width="16px"></button>
<input type="hidden" name="forward"><input type="hidden" name="first" value="{{first}}"></form>{% endif %}
</ul>Niemożliwe jest usunięcie administratora, wysłanie maila jest możliwe tylko wtedy gdy użytkownik podał adres pocztowy.
@permission_required('user.is_superuser',"/login/")
def delete_user_page(request,args):
if request.method == 'POST':
...
else:
id = args
user = User.objects.get(id=id)
day = date.today()
number_of_bookings = Booking.objects.filter(who=user,date__gte=day).count()
template = get_template("edition/user_delete.html")
variables = RequestContext(request,{'user':user,'bookings':number_of_bookings})
output = template.render(variables)
return HttpResponse(output)Pogrubiony wiersz odpowiada takiemu zapytaniu SQL:SELECT COUNT(*) FROM Booking WHERE who = user AND date >= dayPrzy budowaniu filtru możemy sie posługiwać zmienionymi nazwami kolumn, zmiana polega na dopisaniu podwójnego znaku podkreślenia ('__') oraz pewnego słowa kluczowego. Pełną liste słów kluczowych można znaleźć tutaj (gte = greater than or equal to).
Trochę dokładniej opiszemy wysyłanie maila.
(r'^send_mail/$',send_mail_page)
class FormularzPocztowy(forms.Form):
subject = forms.CharField(label="Temat (min. 10 znaków):",min_length=10,max_length=100,widget=forms.TextInput({'size':100}))
body = forms.CharField(label="Treść:",widget=forms.Textarea({'rows':10,'cols':60}))
{% extends "base.html"%}
{% block title %} - {{user.username}}{% endblock %}
{% block header %}<h2><center>Administrator: {{sender.username}} - wysyłanie maila do: {{recipient.username}}</center></h2>{% endblock %}
{% block content%}
<center>
<div style="margin-left:25%;margin-right:25%;">
<fieldset>
<form method="post" action=".">{% csrf_token %}
<div style="text-align:left";>{{ form.as_p }}</div>
<input type="hidden" name="recipient" value="{{ recipient.id }}" />
<input type="hidden" name="sender" value="{{ sender.id }}" />
<input type="submit" value="Wysłanie"/> <input type="reset" value="Wartości domyślne"/>
</form>
</fieldset>
</div>
</center>
{% endblock %}
{% block footer%}
<p>
<div style="text-align:center;"><a href="/admin">Strona administratora</a></div>
</p>
{% endblock %}from django.core.mail import send_mail,BadHeaderErrorPiszemy funkcję send_mail_page:
@permission_required('user.is_superuser',"/login/")
def send_mail_page(request):
template = get_template("send_mail.html")
if request.method == 'POST':
id = request.POST['sender']
sender = User.objects.get(id=id)
id = request.POST['recipient']
recipient = User.objects.get(id=id)
form = FormularzPocztowy(request.POST)
if form.is_valid():
try:
send_mail(form.cleaned_data['subject'],form.cleaned_data['body'],sender.email,[recipient.email])
return HttpResponseRedirect("/admin/")
except BadHeaderError:
pass
else:
id = request.GET['id']
user = User.objects.get(id=id)
form = FormularzPocztowy()
variables = RequestContext(request,{'form':form,'recipient':user})
output = template.render(variables)
return HttpResponse(output)Wysłanie maila, to jeden wiersz w kodzie (plus wymagana obsługa błędów). Kolejne argumenty funkcji send_mail, to
temat maila, jego treść, adres nadawcy, lista adresów odbiorców. W powyższym przykładzie, lista odbiorców jest jednoelementowa.
Linki Cennik i Aktualne rezerwacje wyświetlają to samo co na stronie adminstratora, z tą różnicą, że zwykły użytkownik (zalogowany lub anonimowy) nie
może edytować cennika.
Zaczniemy od utworzenia nowej tabeli Booking. Wpierw opisujemy tabelę w pliku models.py:
class Booking(models.Model):
apartment = models.ForeignKey(Apartment)
who = models.ForeignKey(User)
date = models.DateField(db_index=True)
def __str__(self):
return "Data: "+self.date.strftime('%d.%m.%y')+" Id rezerwującego: "+str(self.who_id)+" Id apartamentu: "+str(self.apartment_id)a potem otwieramy konsolę
i wpisujemy polecenia:python manage.py syncdb #utworzona zostanie nowa tabela python manage.py sqlindexes bookings #w tabeli zostaną utworzone indeksyParametr db_index=True powoduje, że zostanie utworzony indeks na pole date. Pozostałe czynności są dosyć standardowe.
(r'^booking/(\w+)$',booking_page),
class FormularzRezerwacji(forms.Form):
from_day = forms.DateField(label="Od dnia (format dd.mm.rr):",input_formats=['%d.%m.%y'],widget=forms.DateInput(format='%d.%m.%y'))
to_day = forms.DateField(label="Do dnia (format dd.mm.rr):",input_formats=['%d.%m.%y'],widget=forms.DateInput(format='%d.%m.%y'))
def clean_to_day(self):
from_day = self.cleaned_data['from_day']
to_day = self.cleaned_data['to_day']
if from_day<=to_day:
return to_day
raise forms.ValidationError("Koniec jest wcześniejszy niż początek")
{% extends "base.html" %}
{% block title %} - rezerwowanie noclegów {% endblock %}
{% block content%}
<center>
<h2>Apartament: {{ apartment.name }} - rezerwowanie noclegów</h2>
<p>
<div style="margin-left:30%;margin-right:30%;">
<fieldset>
<form method="post" action="/booking/{{ apartment.id }}?id={{id}}">{% csrf_token %}
<div style="text-align:left";>{{ form.as_p }}</div>
<input type="hidden" name="id" value="{{ apartment.id }}" />
<input type="submit" value="Rezerwacja"/>
</form>
</fieldset>
</div>
{% endblock %}
{% block footer %}
<div style="text-align:center;"><a href="/">Główna strona</a>
</div>
{% endblock %}
def free_room(apartment,first,last):
day = first
while day<=last:
try:
Booking.objects.get(apartment=apartment,date=day)
return False
except ObjectDoesNotExist:
day+=timedelta(days=1)
return TruePiszemy funkcję save_new_booking zapisującą nową rezerwację. Każda rezerwacja tworzy tyle zapisów w tabeli Booking ile jest w dni
we wskazanym okresie.
def save_new_booking(apartment,first,last,user):
day = first
while day<=last:
ap = Apartment.objects.get(id=apartment)
us = User.objects.get(id=user)
booking = Booking.objects.create(apartment=ap,date=day,who=us)
booking.save()
day+=timedelta(days=1)
return TrueObecna wersja tej funkcji jest mocno niedoskonała, nie ma żadnego sprawdzania czy zapis sie powiódł.
@login_required
def booking_page(request,args):
id = int(args)
id_user = int(request.GET['id'])
if request.method == 'POST':
form = FormularzRezerwacji(request.POST)
if form.is_valid():
first_day = datetime.strptime(request.POST['from_day'],'%d.%m.%y')
last_day = datetime.strptime(request.POST['to_day'],'%d.%m.%y')
if free_room(id,first_day,last_day):
if save_new_booking(id,first_day,last_day,id_user):
template = get_template("edition/booking_success.html")
variables = RequestContext(request)
output = template.render(variables)
return HttpResponse(output)
else:
apartment = Apartment.objects.get(id=id)
template = get_template("edition/booking_failure.html")
variables = RequestContext(request,{'apartment':apartment,'id':id_user,'from':request.POST['from_day'],'to':request.POST['to_day']})
output = template.render(variables)
return HttpResponse(output)
else:
form = FormularzRezerwacji()
apartment = Apartment.objects.get(id=id)
template = get_template("edition/booking.html")
variables = RequestContext(request,{'apartment':apartment,'form':form,'id':id_user})
output = template.render(variables)
return HttpResponse(output)
W przeciwnym razie kierowany jest na stronę booking_correction.html
z informacją o wolnych apartamentach w pobliżu wskazanego wykresu. Użytkownik może teraz zmienić apartament lub okres. Szczegółów tego przekierowania nie będę juz omawiał.
python manage.py createsuperuser python manage.py runserverMerytorycznie kolejność tych poleceń nie ma znaczenia, podana wyżej jest wygodniejsza - nie wymaga dwukrotnego otwierania konsoli. Pierwsze z tych poleceń dodaje do tabeli User administratora, bez dodania administratora program też będzie działał.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'pensjonat',
'USER': 'postgres',
'PORT': 5432,
'PASSWORD': '***',
'HOST':'',
}
}Jeżeli podczas instalacji programu Postgre zostaną zaakceptowane paramtery domyślne, to wystarczy zmienić hasło.python manage.py syncdbZostaną utworzone wymagane tabele, na końcu padnie pytanie czy utworzyć administratora (superuser w terminologii Django). Odpowiadamy twierdząco. Wpisujemy polecenie:
python manage.py runserver