4.04.2010

хроники django или homebudget part 1

В общем решил вплотную изучить django фреймворк. Есть опыт работы с asp.net mvc фреймворком, есть знания питона. Почему бы и нет?) Как изучить новую технологию максимально эффективно? считаю что нужно сразу же делать какое-нить реальное приложение. Типа учебная тревога.

Здесь буду публиковать хроники моих изысканий, не хочется чтобы они пропали зря. Не в коем случае не претендую на самое оптимальное решение и описание. Вообще происходящее будет много дублировать офф tutorial и примеры из документации с http://docs.djangoproject.com/, в этом нет ничего странного - я по этому и учусь ;)

Что буду писать - менеджер домашнего бюджета. Требования - весьма размытые - хочу контролировать свои расходы. Впрочем таким требованием никого не удивишь. Ладно, скучно стало. Начинаем.

  1. инсталируем django (надеюсь питон уже есть)
    sudo apt-get install python-django
    
  2. инсталируем psycopg2 модуль к питону для работы с бд
    sudo apt-get install python-psycopg2
    
  3. инсталируем postgre
    sudo apt-get install postgresql
    
  4. проверям что все установилось и демон запусчен:
    ps -A | grep postgres
    
  5. запускаем консоль постгреса
    sudo -u postgres psql postgres
    
  6. меняем пользователю postgres пароль
    \password postgres
    
  7. выходим из консоли
    \q
    
  8. создаем базу
    sudo -u postgres createdb homebudget
    
  9. создаем новый django проект
    django-admin.py startproject homebudget
    
  10. проверям что все работает
    cd homebudget
    ./manage.py runserver
    
  11. в браузере переходим на линку
    http://127.0.0.1:8000/
    
  12. редактируем settings.py
    DATABASE_ENGINE = 'postgresql_psycopg2'
    DATABASE_NAME = 'homebudget'
    DATABASE_USER = 'postgres'
    DATABASE_PASSWORD = 'наш пароль'
    DATABASE_HOST = 'localhost'
    DATABASE_PORT = '5432'
    
  13. запускаем синхронизацию проекта с базой (нужно чтобы работали installed_applications из настроек проекта)
    ./manage.py syncdb
    
  14. на вопрос You just installed Django's auth system, which means you don't have any superusers defined. отвечаем да и вводим данные
  15. стартуем новое приложение в проекте
    ./manage.py startapp purchases
    
  16. редактируем models.py
    from django.db import models
     
    class Purchase(models.Model):
        name = models.CharField('identifier of item', max_length=200)
        quantity = models.FloatField('quantity of purchase')
        price_for_one = models.DecimalField('price for one item', max_digits=10, decimal_places=2)
        price_total = models.DecimalField('price for all', max_digits=10, decimal_places=2)
    
  17. добавляем приложение в installed_apps, в файле settings.py добавляем в список
    'homebudget.purchases'
    
  18. синхронизируем базу с моделями
    ./manage.py syncdb
    
  19. забиваем базу тестовыми данными (можно через pgadmin), заходим в консоль джанги
    ./manage.py shell
    
  20. там вбиваем
    from homebudget.purchases.models import Purchase
    p = Purchase(name='beer',quantity=3, price_for_one=48, price_total=144)
    p.save()
    quit()
    
  21. добавляем новое правило для url rewriter-а в файле urls.py
    (r'^purchases/$', 'homebudget.purchases.views.index')
    
  22. создаем новое view, редактируем файл purchases/views.py
    from django.template import Context, loader
    from homebudget.purchases.models import Purchase
    from django.http import HttpResponse
    
    def index(request):
        all_list = Purchase.objects.all()
        t = loader.get_template('purchases/index.html')
        c = Context({
            'all_list': all_list,
        })
        return HttpResponse(t.render(c))
    
  23. создадим папку templates в каталоге проекта, и подпапку purchases
    mkdir templates
    mkdir templates/purchases
    
  24. добавляем папку templates в проектные шаблоны, правим файл settings.py (путь абсолютный!)
    TEMPLATE_DIRS = (
        '/home/username/projects/homebudget/templates',
    )
    
  25. создадим файл templates/purchasesindex.html
    {% if all_list %}
        <ul>
            {% for p in all_list %}
                <li>{{p.name}} <b>{{p.price_total}}</b></li>
            {% endfor %}
        </ul>
    {% else %}
        <p>No purchases yet</p>
    {% endif %}
    
  26. запускаем сервер, в браузере переходим на страницу. ништяк? ;)
    http://127.0.0.1:8000/purchases/
    
  27. добавляем новое поле к модели (purchases/models.py)
        purchase_date = models.DateField('date of buying')
    
  28. так как джанга не поддерживает миграцию бд, делаем руками
    ./manage.py sql purchases
    
  29. получаем sql код, лезем в postgre и правим в нем таблицу
    ALTER TABLE purchases_purchase ADD COLUMN purchase_date date;
    UPDATE purchases_purchase SET purchase_date=now();
    ALTER TABLE purchases_purchase ALTER COLUMN purchase_date SET NOT NULL;
    
  30. создаем add форму
  31. в файл models.py добавляем следующий класс
    from django import forms
    
    class PurchaseItemForm(forms.Form):
        name = forms.CharField(max_length=200)
        quantity = forms.FloatField()
        price_for_one = forms.DecimalField(decimal_places=2)
        price_total = forms.DecimalField(max_digits=10, decimal_places=2)
        purchase_date = forms.DateField()
    
  32. в файл views.py добавляем новую вьюхую
    from homebudget.purchases.models import PurchaseItemForm
    def purchase_add(request):
        form = PurchaseItemForm()
        t = loader.get_template('purchases/purchase_add.html')
        c = Context({
            'form': form
        })
        return HttpResponse(t.render(c))
    
  33. создаем новый файл templates/purchases/purchase_add.html
    <form action="/purchases/add/" method="post">
    {{ form.as_p }}
    <input type="submit" value="Save" />
    </form>
    
  34. в файл urls.py прописываем новый маппинг паттерн
    (r'^purchases/add/$', 'homebudget.purchases.views.purchase_add'),
    
  35. запускам дев сервер, в браузере переходим на страницу
    http://127.0.0.1:8000/purchases/add/
    
  36. теперь добавляем сохранение данных, это будет происходить на той же самой странице по post запросу
  37. в файл views.py добавляем обработку post запроса и сохранение
    from django.http import HttpResponse, HttpResponseRedirect
    def purchase_add(request):
        if request.method == 'POST':
            form = PurchaseItemForm(request.POST)
            if form.is_valid():
                name = form.cleaned_data['name']
                quantity = form.cleaned_data['quantity']
                price_for_one = form.cleaned_data['price_for_one']
                price_total = form.cleaned_data['price_total']
                purchase_date = form.cleaned_data['purchase_date']
                item = Purchase(name=name, quantity=quantity, price_for_one=price_for_one, price_total=price_total, purchase_date=purchase_date)
                item.save()
                return HttpResponseRedirect('/purchases/') 
        else:
            form = PurchaseItemForm()
        t = loader.get_template('purchases/purchase_add.html')
        c = Context({
            'form': form
        })
        return HttpResponse(t.render(c))
    
  38. проверям в браузере, в случае удачного сохранения будет переход на список всех
  39. займемся совершенствованием
  40. в файл views.py добавляем
    from django.shortcuts import render_to_response
    
  41. ужасные loader.get_template, Context, return HttpResponse(..) заменяем на простую конструкцию
        return render_to_response('purchases/purchase_add.html', {
            'form': form
        })
    
  42. в файле models.py вставляем ModelForm взамен идиотского дублирующего кода из модели
    class PurchaseItemForm(forms.ModelForm):
        class Meta:
            model = Purchase
    
  43. да и после этого во вьюхе не нужно перечислять все поля.. теперь будет так:
    def purchase_add(request):
        if request.method == 'POST':
            form = PurchaseItemForm(request.POST)
            if form.is_valid():
                form.save()
                return HttpResponseRedirect('/purchases/') 
        else:
            form = PurchaseItemForm()
    
        return render_to_response('purchases/purchase_add.html', {
            'form': form
        })
    
  44. в файле urls.py тоже изменения:
    urlpatterns = patterns(
        'homebudget.purchases.views',
        (r'^purchases/$', 'index'),
        (r'^purchases/add/$', 'purchase_add'),
    )
    
  45. запускаем дев сервер и все перепроверяем, что работает как до рефакторинга
  46. добавляем edit для элемента, по быстрому
  47. в файл views.py
    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()
            return HttpResponseRedirect('/purchases/')
    
        form = PurchaseItemForm(instance=item)
        return render_to_response('purchases/purchase_edit.html', {
            'form': form,
            'id' : purchase_id
        })
    
  48. создадим файл templates/purchases/purchase_edit.html
    <form action="/purchases/edit/{{ id }}/" method="post">
    {{ form.as_p }}
    <input type="submit" value="Save" />
    </form>
    
  49. в файл index.html добавим линки для редактирования и добавления нового элемента:
    {% if all_list %}
        <ul>
            {% for p in all_list %}
                <li>
                    {{p.name}}
                    <b>{{p.price_total}}</b>
                    {{p.purchase_date}}
                    <a href='/purchases/edit/{{p.pk}}/'>Edit</a>
                </li>
            {% endfor %}
        </ul>
    {% else %}
        <p>No purchases yet</p>
    {% endif %}
    
    <a href='/purchases/add/'>Add</a>
    
  50. в файл urls.py
        (r'^purchases/edit/(?P<purchase_id>\d+)/$', 'purchase_edit'),
    
  51. запускаем дев сервер и наслаждаемся

сейчас инструментария хватает чтобы забивать и просматривать базу, но это все очень не удобно, так что продолжение следует

Посты по теме:

Комментариев нет: