Итак, как обстоят дела: после первого рывка я скурпулезено заполнял базу через существующие странички. Иногда когда скрипели зубы - добавлял функциональность. А так могу сказать - сейчас меня более-менее устраивает, все работает как и должно, есть куда развивать систему, следующие шаги ясны. Собственно, вот продолжение хроник, информация восстановлена по change-ам. Все измения фискируются в source-control'е в качестве которого я успешно использую bazaar.
- Добавляем просмотр отдельного дня. То есть это выборка из базы по конкретному дню, ссылки на редактирование отдельных записей (покупок), собственно ценники, и сумма за день. Как - добавляем новый метод в контроллер, добавляем отдельную вьюху для html-я, добавляем правило для урлов. all right, let's get started
- Чтобы было понятно - начинаем с правила для урлов. хотим чтобы запрос для странички дня выглядел типа blah-blah-blah/view/day/2010/04/30 ну типа никаких идиотских GET параметров. В urls.py добавляем следующее правило:
(r'^purchases/view/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', 'day_view') - Новый метод контроллера (в файле purchases/views.py), логика такая - добавленное в предыдущем пункте правило добавит необходимые параметры день/месяц/год, мы из них делаем нормальный объект datetime (если не получается - типа 68 января - то 404), потом забираем из базы все записи Purchase с этой датой, берем сумму по ценам и выводим все во вьюху.
def day_view(request, year, month, day): try: dt = datetime.date(year=int(year), month=int(month), day=int(day)) except: raise Http404 plist = Purchase.objects.filter(purchase_date = dt) price_sum = Decimal('0.0') for it in plist: price_sum += it.price_total return render_to_response('purchases/day_view.html', { 'date': dt, 'list': plist, 'price_sum': price_sum, }) - Создаем новый темплейт вьюхи templates/purchases/day_view.html с примерно следующим содержимым. Логика такая - пробегаем по списку значений как на главной странице, выводим ссылки на изменение и тд, выводим отдельно заранее посчитанный тотал.
<p>Date: {{date}} </p> {% if list %} <ul> {% for p in list %} <li> {{p.name}} <b>{{p.price_total}}</b> <a href='/purchases/edit/{{p.pk}}/'>Edit</a> </li> {% endfor %} </ul> {% else %} <p>No purchases yet</p> {% endif %} <p> Total: <b>{{price_sum}}</b> </p> <a href='/purchases/add/'>Add</a> - Проверяем на работоспособность. Должно работать :)
- Теперь делаем тоже самое но для месяца. Тупо по аналогии.
- Добавляем аналогично просмотру дня правило для просмотра месяца (доступ по ссылке типа blah-blah-blah/view/day/2010/04/):
(r'^purchases/view/(?P<year>\d{4})/(?P<month>\d{1,2})/$', 'month_view') - В методе контроллера имеем сразу же несколько проблем. Первая - нужно сделать запрос на совпадение и года, и месяца одновременно (в случае с просмотром дня - все было просто - тупо совпадение даты). Лечим следующим методом:
from django.db.models import Q plist = Purchase.objects.filter(Q(purchase_date__year=dt.year) & Q(purchase_date__month=dt.month))
- Вторая проблема - нет в стандартной библиотеке пайтона функций типа добавить месяц. Можно есть - добавления отрезка времени, но так как в месяцах непостоянное кол-во дней такой способ выглядит очень корявым. Какой по-проще способ нашел - по дате составить новую с месяцем+1.
# dt - дата y, m, d = dt.timetuple()[:3] dt_next = datetime.date(year=y+((m+1)/12), month=(m+1)%12, day=d)
- Третья проблема - как передать данные во вьюху. Я выбрал способ через словарь (dictionary) - в котором date => purchase_item.
- Вот получившийся код. Логика такая - получаем год/месяц - строим дату как первое число месяца. Выбираем из базы все записи purchase за этот месяц. Потом прибавляя по дню пока не прийдем к первому числу следующего месяца, попутно собирая суммы за каждый день и сумму за весь месяц.
def month_view(request, year, month): try: dt = datetime.date(year=int(year), month=int(month), day=1) except: raise Http404 from django.db.models import Q plist = Purchase.objects.filter(Q(purchase_date__year=dt.year) & Q(purchase_date__month=dt.month)).order_by('purchase_date') y, m, d = dt.timetuple()[:3] dt_next = datetime.date(year=y+((m+1)/12), month=(m+1)%12, day=d) day_price = {} month_total = Decimal('0.0') while (dt < dt_next): purchases_day = plist.filter(purchase_date__day=dt.day) day_total = Decimal('0.0') for it in purchases_day: day_total += it.price_total month_total += day_total day_price[dt] = day_total dt += datetime.timedelta(days=1) return render_to_response('purchases/month_view.html', { 'date': datetime.date(year=int(year),month=int(month),day=1), 'dict': day_price, 'total': month_total, }) - Создаем новый темплейт templates/purchases/month_view.html
<p>Date: {{date}} </p> {% if dict %} <ul> {% for key,value in dict.items %} <li> {{key}} <b>{{value}}</b> <a href='/purchases/view/{{key.year}}/{{key.month}}/{{key.day}}/'>Edit</a> </li> {% endfor %} </ul> {% else %} <p>No purchases yet</p> {% endif %} <p> Total: <b>{{total}}</b> </p> <a href='/purchases/add/'>Add</a> - Проверяем. Опа, не работает - все дни вперемешку, хотя и выставлено для запроса order_by. А тут начинаются темные, не хорошо документированные особенности джанги по сортировке словаря (я об этот момент чуть голову не сломал). Есть миллион решений - типа сделать буферный список из отсортированных ключей и его передавать с моделью, и тп. Но вот чтобы применить к for-у для словаря сортировку прямо в темплейте - нашел только один:
for key,value in dict.items|dictsort:"0"
- Сделаем так чтобы после Edit-а или Add-а происходил редирект на просмотр использованного дня.
- Метод Add-а:
def purchase_add(request): if request.method == 'POST': form = PurchaseItemForm(request.POST) if form.is_valid(): form.save() purchase_date = form.cleaned_data["purchase_date"] return HttpResponseRedirect('/purchases/view/%s/%s/%s/' % purchase_date.timetuple()[:3]) else: form = PurchaseItemForm() return render_to_response('purchases/purchase_add.html', { 'form': form }) - Метод edit-а:
def purchase_edit(request, purchase_id): try: item = Purchase.objects.get(pk=purchase_id) except Purchase.DoesNotExist: raise Http404 if request.method == 'POST': form = PurchaseItemForm(request.POST, instance=item) form.save() purchase_date = form.cleaned_data["purchase_date"] return HttpResponseRedirect('/purchases/view/%s/%s/%s/' % purchase_date.timetuple()[:3]) form = PurchaseItemForm(instance=item) return render_to_response('purchases/purchase_edit.html', { 'form': form, 'id' : purchase_id }) - Делаем добавление покупки на конкретный день (а не только на сегодня) - те если хотим добавить покупку за вчера со страницы вчерашнего дня - то поле дата должно быть корректно подставлено на вчера. Другими словами blah-blah-blah/purchases/add/ - должно добавлять покупку на сегодня, а blah-blah-blah/purchases/add/2010/04/30 - должно подставлять 2010.04.30
-
В urls.py добавляем следующий словарь для определения сегодня:
import datetime year, month, day = datetime.datetime.today().timetuple()[:3] today_dict = {'year':year, 'month':month, 'day':day}И следующее правила взамен старого add-а:(r'^purchases/add/$', 'purchase_add', today_dict), (r'^purchases/add/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', 'purchase_add'), - Метод add должен теперь выглядеть так:
def purchase_add(request, year, month, day): if request.method == 'POST': form = PurchaseItemForm(request.POST) if form.is_valid(): form.save() purchase_date = form.cleaned_data["purchase_date"] return HttpResponseRedirect('/purchases/view/%s/%s/%s/' % purchase_date.timetuple()[:3]) else: try: dt = datetime.date(year=int(year), month=int(month), day=int(day)) except: dt = datetime.today() purchaseitem = Purchase() purchaseitem.purchase_date = dt form = PurchaseItemForm(instance=purchaseitem) return render_to_response('purchases/purchase_add.html', { 'form': form }) - Ну и собственно линка внутри templates/purchases/day_view.html
<a href='/purchases/add/{{date.year}}/{{date.month}}/{{date.day}}/'>Add</a>
Посты по теме:

Комментариев нет:
Отправить комментарий