Архив за месяц: Ноябрь 2014

Отображение дерева категорий с помощью django-mptt

Предположим что у нас есть древовидная структура, например таблица с полями (id, parent_id, name). Это может быть структура папок, иерархия животного мира, или например дерево категорий в магазине продуктов. Пример такой структуры:

Холоднокровные
Млекопитающие
-насекомоядные
—кроты
—землеройки
-грызуны
—мыши
—белки
—бобры

Как видно, из-за древовидности придется воспользоваться подходом с применением рекурсии. Однако своего велосипеда можно не изобретать, для django есть модуль, как раз работающий с подобной структурой — django-mptt . Основная документация есть на сайте http://django-mptt.github.io/django-mptt/tutorial.html#getting-started , однако мне потребовалось реализовать функционал, где листья деревьев раскрываются лишь в том случае, если выбран потомок. Таким образом, если мы выбрали бобра, то отобразиться должно вот так:

Холоднокровные
Млекопитающие
-насекомоядные
-грызуны
—мыши
—белки
—БОБРЫ

Переходим к реализации. Сперва создаем модель:

from django.db import models
from mptt.models import MPTTModel, TreeForeignKey

class Category(MPTTModel):
    name = models.CharField(max_length=64, unique=True)
    parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
    class MPTTMeta:
        order_insertion_by = ['name']

Далее переходим во views.php

from django.shortcuts import render
from django.shortcuts import render_to_response
from django.http import Http404, HttpResponse
from django.template import loader, Context, RequestContext
from versane.models import Category

def category_page(request, category_id):
    #getting detail information about current object
    current_category = Category.objects.get(id=category_id)
    root_category_id = current_category.get_root().id
    #render
    return render_to_response("category_page.html",
                          {
                              'nodes':Category.objects.all(),
                              'current_category':current_category,
                              'root_category_id':root_category_id
                          },
                          context_instance=RequestContext(request))

И самая фишка, это конечно шаблон. Дефолтный шаблон, выводит все листья, здесь же мы выводим только те категории, которые видны например при работе с папками.

{% load mptt_tags %}
<h3>Categories</h3>
{% load mptt_tags %}
<ul>
    {% recursetree nodes %}
        <li>
            <a href="/category/{{ node.id }}/">
            {{ node.name }}
            {{ node.id }}
            {{ node.level }}
            </a>
            {% if node.is_root_node %}
                {% ifequal node.id root_category_id %}
                    {% if not node.is_leaf_node %}
                        <ul class="children">
                            {{ children }}
                        </ul>
                    {% endif %}
                {% endifequal %}
            {% else %}
                {% if not node.is_leaf_node %}
                    {% if current_category.id == node.id %}
                        <ul class="children">
                            {{ children }}
                        </ul>
                    {% elif node.level < current_category.level %}
                        <ul class="children">
                            {{ children }}
                        </ul>
                    {% endif %}
                {% endif %}
            {% endif %}
        </li>
    {% endrecursetree %}
</ul>

Обратите внимание, на использование ключевых слов node, children а также их свойств node.is_root_node, node.level, node.is_leaf_node и.т.д. С помощью изменения шаблона, можно отобразить практически любую древовидную структуру так как нам надо, используя описанную модель MPTTModel

Django руководство по созданию простейшего приложения

Рассмотрим как создать сайт на django на следующей задаче: надо отобразить список товаров из базы данных. Таким образом мы познакомимся с основами фреймворка джанго: проектирование моделей, роутинг, контроллер, вьюха итд. Проще всего конечно работать в среде PyCharm, поэтому будем использовать некоторые фишки из него.
И так, сперва создаем проект PyCharm и выбираем django. Попросят ввести названия application, например (test_task).
Далее идем в settings.py и настраиваем доступ к нашей базе данных, для MySQL это примерно так

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_test',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

Запустим сервер с помощью утилиты manage.py для этого нажимаем комбинацию Ctrl+Alt+R и далее выбираем команду runserver. Если с настройками вы справились, то по адресу откроется дефолтная страница http://127.0.0.1:8000/ однако мне пришлось поставить модуль python-mysql , который требуется при работе с mysql.
Переходим к созданию моделей. Для этого открываем models.py и делаем простенькую модельку продукта

from django.db import models

# Create your models here.
class Product(models.Model):
    #id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=64)
    price = models.IntegerField()

Стоит отметить, что создание id не обязательно, django сам создаст поле id, которое и будет первичным ключом. Как видите у товара есть два свойства: название и цена. Теперь сгенерируем структуру таблицы. Чтобы просто получить sql код — делаем команду sql (через manage.py или Ctrl+Alt+R). Чтоб собственно выполнить данные sql команды, используем другую команду — syncdb, после этого у нас создастся таблица продуктов.
Далее идем в роутинг urls.py и создаем ссылки на страницы

url(r'^$', 'test_task.views.index', name='home_page'),
url(r'^product/(?P<product_id>\d+)/$', 'test_task.views.home', name='product_page'),

Подготовим шаблоны. Чтобы джанго знал куда мы складываем шаблоны — добавьте в настройки (settings.py) параметр расположения директории шаблонов django.

#directory for templates
TEMPLATE_DIRS = (
    os.path.join(BASE_DIR, 'templates'),
)

Открываем views.py и в контроллере пишем следующее

from django.shortcuts import render
from django.shortcuts import render_to_response

# Create your views here.
def home_page(request):
    return render_to_response('home_page.html')

После данных операций на начальной странице http://127.0.0.1:8000 появится всё то, что вы поместили в home_page.html , однако без данных. Давайте добавим данные:

Проще всего начальные данные внести с помощью fixtures, путем комады loaddata . В папке с django приложением создаем папку fixtures и в ней файл data.json такого содержания:

[
  {
    "model": "test_task.product",
    "pk": 1,
    "fields": {
      "name": "Product 1",
      "price": 123
    }
  },
  {
    "model": "test_task.product",
    "pk": 2,
    "fields": {
      "name": "Product 2",
      "price": 456
    }
  }
]

Также можно не создавать вручную данный файл, а выгрузить существующие данные из базы данных в помощью команды dumpdata.

Загрузив данные можно переходить к выборке определеных товаров в контроллере:

from django.shortcuts import render
from django.shortcuts import render_to_response
from django.http import Http404, HttpResponse
from django.template import loader, Context, RequestContext
from test_task.models import Product

def home_page(request):
    product_list = Product.objects.all() #.order_by('name')
    t = loader.get_template('home_page.html')
    c = Context({
        'product_list': product_list,
    })
    return HttpResponse(t.render(c))

def product_page(request, product_id):
    product_object = Product.objects.get(id=product_id)
    t = loader.get_template('product_page.html')
    c = Context({
        'product_object': product_object,
    })
    return HttpResponse(t.render(c))

Теперь данные передаются в шаблон, осталось написать подходящий html . В джанго используется специальный синтаксис шаблонизатора. Однако он весьма прост. Ниже приводится пример home_page.html

<!DOCTYPE html>
<html>
<head>
    <title>Main page</title>
</head>
<body>
    <h1>Products</h1>
    {% if product_list %}
        <ul>
            {% for product in product_list %}
                <li>
                    <a href="/product/{{ product.id }}/">
                        {{ product.name }} ${{ product.price }}
                    </a>
                </li>
            {% endfor %}
        </ul>
    {% endif %}
</body>
</html>

Таким образом вы познакомились с основами джанго, теперь можно добавлять свои модели, дополнять контроллер и красиво отображать содержимое в html шаблонах.