62
Сергей Бурма #kranonit e20

kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Embed Size (px)

Citation preview

Page 1: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Сергей Бурма

#kranonit e20

Page 2: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Обо мне

● использую Python в своих корыстных и не

очень целях уже пятый год;

● поучаствовал в нескольких больших

незапустившихся проектах;

● приложил руку к сайтам wokifood.ru,

dewote.com, biz-translate.com;

● изредка пишу на хабр (ник batment).

Page 3: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

О чем вы сегодня узнаете

● немного о Python;

● много о фреймворке Django;

● о том как сделать очень простой

интернет-магазин с админкой.

Page 4: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Особенности Python

● приоритет на читаемость;

● данные бывают изменяемые и

неизменяемые (mutable, immutable)

● жесткие стандарты оформления кода;

● любая сущность - это объект;

● очень своеобразное ООП;

● магические методы.

Page 5: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Дисклеймер

● мы используем Python 3;

● код представлен максимально наглядно,

но в нем скорее всего есть неувязки и

ошибки, будьте осторожны.

Page 6: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Почему Django?

● очень популярен, активно развивается;

● покрывает большинство нужд веб-

разработчиков;

● минимизирует рутину;

● достаточно приятен в обращении.

Page 7: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Составляющие Django-приложения

● основная парадигма - Models Views

Templates;

● модели;

● формы;

● представления;

● шаблоны;

● маршрутизаторы.

Page 8: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Недостатки Django

● однопоточный и как следствие тормозной;

● нестандартные вещи делать сложно (но

зато более-менее красиво);

● имеет жесткую структуру и идеологию,

что в редких случаях может помешать

разработке.

Page 9: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Что понадобится

● настроенная машина с Linux (лучше всего

Ubuntu, подойдет виртуальная) или

MacOS;

● редактор Python и HTML. Для начала

рекомендую PyCharm Professional Edition.

● или вместо всего этого онлайн-среда,

например c9.io

Page 10: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Подготовка системы

sudo apt-get install build-essential python3-dev python3 python3-virtualenv python3-

pip zlib1g-dev libpng12-dev libjpeg-dev git

virtualenv -p /usr/bin/python3 venv

source venv/bin/activate

git clone https://github.com/batment/kranonit-shop

cd kranonit-shop

pip install -r requirements.txt

Page 11: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Инициализация проекта

cd ..

django-admin startproject shop

cd shop

python manage.py startapp catalog

python manage.py startapp cart

python manage.py migrate

python manage.py syncdb

python manage.py runserver

Page 12: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Структура интернет-магазина

Page 13: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Настройки Django-проекта

● параметры подключения к базам данных;

● расположение директорий со

статическими файлами (JS, CSS, etc.) и

загружаемыми файлами;

● локализация, интернационализация и т.д.;

● подключенные приложения;

● параметры безопасности.

Page 14: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Настройки проекта ч.1

import dj_database_url

DEFAULT_DB = 'sqlite:///' + BASE_DIR + '/db.sqlite3'

DATABASES = {

'default': dj_database_url.config(default=DEFAULT_DB)

}

STATIC_URL = '/static/'

MEDIA_URL = '/media/'

TEMPLATE_DIRS = (

os.path.join(BASE_DIR, 'templates'),

)

STATICFILES_DIRS = (

os.path.join(BASE_DIR, ‘static’),

)

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Page 15: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Настройки проекта ч.2

INSTALLED_APPS = (

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'catalog',

'cart',

)

SECRET_KEY = '0!+6_x3g49_do4d5)e=)07ke%wza^1)&@*=@!aw1x&%&kv24j3'

# SECURITY WARNING: don't run with debug turned on in production!

DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []

ROOT_URLCONF = 'shop.urls'

WSGI_APPLICATION = 'shop.wsgi.application'

Page 16: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Описание модели в Django

● класс, наследуемый от models.Model;

● нужные поля с данными описываются как

поля класса;

● класс Meta, объявленный внутри модели

содержит ее настройки;

● методы __str__ или __unicode__ упростят

текстовый вывод модели.

Page 17: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Модель ProductCategory

class ProductCategory(models.Model):

name = models.CharField(max_length=128)

class Meta:

verbose_name = 'Category'

verbose_name_plural = 'Categories'

def __str__(self):

return self.name

Page 18: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Базовые типы полей

● CharField - простое текстовое поле

ограниченной длинны;

● IntegerField - для хранения целых чисел;

● BooleanField - для хранения логических

данных (True, False)

● ForeignKey - для связи с другой моделью;

● ManyToManyField - для связи со многими

моделями.

Page 19: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Загружаемые файлы

● в базовой поставке FileField и

ImageFileField;

● в базе хранится только имя файла;

● файл хранится в MEDIA_ROOT;

● файлы нужно удалять вручную;

● в поле нужно указать upload_to;

● для картинок рекомендую easy_thumbnails

Page 20: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Модель Product

class Product(models.Model):

name = models.CharField(max_length=128)

description = models.TextField(max_length=10000, blank=True, null=True)

price = models.DecimalField(max_digits=8, decimal_places=2)

category = models.ForeignKey('catalog.ProductCategory')

image = models.ImageField(upload_to='products')

class Meta:

verbose_name = 'Product'

verbose_name_plural = 'Products'

def __str__(self):

return self.name

Page 21: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Модель Order

class Order(models.Model):

created_at = models.DateTimeField(auto_now_add=True, verbose_name='Opened at')

closed_at = models.DateTimeField(verbose_name='Closed at', blank=True, null=True)

is_closed = models.BooleanField(default=False, verbose_name='Is closed')

is_processed = models.BooleanField(default=False, verbose_name='Is processed')

phone = models.CharField(max_length=32, null=True)

address = models.CharField(max_length=256, null=True)

name = models.CharField(max_length=64, verbose_name='Contact name', null=True)

class Meta:

verbose_name = 'Order'

verbose_name_plural = 'Orders'

def price(self):

total_price = 0

for p in self.positions.all():

total_price += p.price()

return total_price

Page 22: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Модель OrderPosition

class OrderPosition(models.Model):

order = models.ForeignKey('cart.Order', related_name='positions')

product = models.ForeignKey('catalog.Product')

count = models.PositiveIntegerField(default=0)

class Meta:

verbose_name = 'Order position'

verbose_name_plural = 'Order positions'

ordering = ['-count']

def price(self):

return self.product.price * self.count

Page 23: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Формы в Django

● класс, наследуемый от forms.Form;

● описывается аналогично моделям;

● в описании полей можно указать

необходимый контрол (widget);

● могут генерировать готовый HTML-код.

Page 24: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Формы для моделей (ModelForm)

● в параметрах нужно только указать

модель, форма сгенерируется

автоматически;

● можно также указать включенные поля и

их порядок вывода;

● вместо этого можно указать исключенные

поля.

Page 25: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Набор форм (FormSet)

● предназначен для ситуаций, когда нужно

редактировать сразу много однотипных

данных;

● генерируется при помощи специальных

функций-фабрик;

● формы-составляющие являются

обычными формами.

Page 26: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Форма для добавления в корзину

class AddToCartForm(forms.Form):

product = forms.ModelChoiceField(

Product.objects.all(),

widget=forms.HiddenInput

)

Форма для оформления заказа

class OrderForm(forms.ModelForm):

class Meta:

model = Order

fields = ['name', 'address', 'phone']

Page 27: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Форма для позиций заказа

class OrderPositionForm(forms.ModelForm):

class Meta:

model = OrderPosition

fields = ['product', 'count']

OrderPositionFormset = forms.inlineformset_factory(

Order,

OrderPosition,

OrderPositionForm,

extra=0,

)

Page 28: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Представления в Django

● берут данные из GET или POST-массива,

моделей и форм, передают в шаблон;

● могут быть функцией - минимум

параметров и полная свобода в

функционале или классом на основе

базовых представлений, с настройкой

готового кода.

Page 29: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

● get_context_data - формирует данные для

вывода в шаблон, можно добавить туда

свои;

● get_queryset - отправляет запрос в базу,

можно изменить любым образом;

● get_template_name - определяет шаблон,

можно подставлять другой при, например,

ajax-запросе.

Функционал Class Based Views

Page 30: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

ListView

● служит для вывода списка моделей;

● поддерживает разбитие на страницы

(pagination).

DetailView

● служит для вывода одной модели.

Page 31: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Список товаров ч.1

class ProductList(ShopMixin, ListView):

model = Product

template_name = 'product_list.html'

context_object_name = 'products'

def __init__(self, *args, **kwargs):

self.category = None

super().__init__(*args, **kwargs)

Page 32: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Список товаров ч.2

def get_category(self):

category_id = self.kwargs.get('category_id')

category = None

if category_id:

category = get_object_or_404(

ProductCategory,

id=category_id,

)

self.category = category

return category

def get_queryset(self):

queryset = super().get_queryset()

if self.category:

queryset = queryset.filter(

category=self.category,

)

return queryset

Page 33: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Просмотр товара

class ProductDetailed(ShopMixin, DetailView):

model = Product

template_name = 'product.html'

context_object_name = 'product'

Примесь ShopMixin

class ShopMixin(object):

"""Adds categories and current order to render context"""

def get_context_data(self, **kwargs):

data = super().get_context_data(**kwargs)

categories = ProductCategory.objects.all()

data['categories'] = categories

data['order'] = get_order(self.request)

return data

Page 34: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Представление на базе функции

● принимает объект запроса и параметры

URL;

● нужно все делать вручную;

● позволяет вызывать любые побочные

эффекты;

● более явный и понятный код.

Page 35: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Представление add_to_cart

def add_to_cart(request):

order = get_order(request)

if not order:

order = Order.objects.create()

request.session['order_id'] = order.id

form = AddToCartForm(request.POST)

if form.is_valid():

product = form.cleaned_data['product']

order_position, created = OrderPosition.objects.get_or_create(

product=product,

order=order,

)

order_position.count += 1

order_position.save()

return redirect('cart')

Page 36: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

● обычный словарь в request.session;

● у каждого пользователя свой на основе

cookie session_id;

● может хранится в базе, в redis или в

cookies (не рекомендуется);

● может быть в Pickles или JSON;

● хранить лучше не модели, а их id.

Сессии в Django

Page 37: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Функция get_order

def get_order(request):

order_id = request.session.get('order_id')

order = None

if order_id:

try:

order = Order.objects.get(id=order_id)

except Order.DoesNotExist:

pass

return order

Page 38: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

CreateView

UpdateView

● для редактирования моделей;

● обязательно принимает pk или slug.

● для создания моделей;

● может сам генерировать форму;

● содержит функции form_valid, form_invalid.

Page 39: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Редактирование заказа ч.1

class OrderDetails(UpdateView):

model = Order

template_name = 'cart.html'

form_class = OrderForm

success_url = '/finish'

def get_object(self, queryset=None):

return get_order(self.request)

def get_formset(self):

return OrderPositionFormset(**self.get_form_kwargs())

Page 40: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Редактирование заказа ч.2

def get_context_data(self, **kwargs):

data = super().get_context_data(**kwargs)

data['formset'] = self.get_formset()

return data

def form_valid(self, form):

formset = self.get_formset()

if formset.is_valid():

for position_form in formset:

position_form.save()

return super().form_valid(form)

else:

return self.form_invalid(form)

Page 41: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Завершение заказа

def close_order(request):

if request.POST:

order = get_order(request)

order.is_closed = True

order.closed_at = datetime.datetime.now()

order.save()

return render(request, 'close_order.html', {

'order': order,

})

else:

return redirect('order')

Page 42: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

TemplateView

class AboutUs(TemplateView):

template_name = 'about_us.html'

Страница About us

● для вывода статичной или частично статичной страницы без моделей.

Page 43: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Админ-сайт в Django

● небольшая встроенная CMS, которой вам

скорее всего хватит;

● позволяет настроить очень многое, но не

все;

● настраивается для каждой модели

отдельно, плюс можно по связям сделать

встроенную админку.

Page 44: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Настройка админки каталога

from django.contrib import admin

from catalog.models import Product, ProductCategory

admin.site.register(Product)

admin.site.register(ProductCategory)

Page 45: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Настройка админки заказов

class OrderPositionInline(admin.TabularInline):

model = OrderPosition

class OrderAdmin(admin.ModelAdmin):

inlines = [

OrderPositionInline,

]

readonly_fields = ['created_at', 'closed_at']

admin.site.register(Order, OrderAdmin)

Page 46: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Маршрутизация в Django● ROOT_URLCONF со списком url_patterns;

● каждый элемент - это regexp с

соответствующим представлением.

● CBV должны вызвать метод as_view для

вставки в список;

● для представлений-функций достаточно

указать ссылку ‘app.views.view’;

● можно включать другие файлы c

маршрутами через include

Page 47: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Маршруты приложения ч.1

from django.conf.urls import patterns, include, url

from django.contrib import admin

from django.conf import settings

from django.conf.urls.static import static

from catalog.views import ProductDetailed, ProductList, AboutUs

from cart.views import OrderDetails

urlpatterns = patterns(

'',

url(r'^(?P<category_id>\d+)?$', ProductList.as_view(), name='products'),

url(r'^product/(?P<pk>\d+)/$', ProductDetailed.as_view(), name='product'),

url(r'^add/$', 'cart.views.add_to_cart', name='add_to_cart'),

url(r'^cart/$', OrderDetails.as_view(), name='cart'),

url(r'^finish/$', 'cart.views.close_order', name='finish'),

url(r'^about/$', AboutUs.as_view(), name='about'),

url(r'^admin/', include(admin.site.urls)),

)

Page 48: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Маршруты приложения ч.2

urlpatterns += [static(

settings.STATIC_URL

) + static(

settings.MEDIA_URL,

document_root=settings.MEDIA_ROOT

)]

Page 49: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Язык шаблонов в Django

● очень простой;

● состоит из тегов и фильтров;

● теги нужны для вывода контента;

● фильтры нужны для обработки

переменных или результатов работы

других фильтров;

● исключения по возможности

подавляются.

Page 50: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Главный шаблон

● в основе лежит скелет getskeleton.com

● для встраивания используется тег {% include ‘filename’ %}

● для наследования используется тег {% extends ‘filename’ %}

● тег extends должен быть на первой

строке файла.

Page 51: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Код главного шаблона<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<title>{% block title %}{% endblock %}</title>

<link rel="stylesheet" href="/static/css/normalize.css">

<link rel="stylesheet" href="/static/css/skeleton.css">

</head>

<body>

<div class="container">

{% include ’header.html’ %}

{% block body %}

<div class="row">

<div class="twelve columns">

{% block content %}{% endblock %}

</div>

</div>

{% endblock %}

</div>

</body>

</html>

Page 52: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Используемые теги

● url для вставки ссылок;

● csrf_token - нужен для POST-форм;

● if ... else … endif - условный тег

● for … in … endfor - цикл;

● forloop - объект с описанием текущей

итерации цикла, работает только внутри

for … endfor

Page 53: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Шаблон-заголовок

<div class="row">

<a href="{% url 'about' %}">About us</a>

Cart: {% if order %}

<a href="{% url 'cart' %}">{{ order.price }}$</a>

{% else %}empty{% endif %}

</div>

Page 54: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Страница списка товаров ч.1

{% extends 'base.html' %}

{% block title %}Shop{% endblock %}

{% block body %}

<div class="row">

{% for category in categories %}

<a href="{% url 'products' category.id %}>{{ category }}</a>

{% endfor %}

</div>

{% for product in products %}

{% if not forloop.counter|divisibleby:"2" %}

<div class="row">

{% endif %}

Page 55: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Страница списка товаров ч.2<div class="six columns">

<div>

<img src="{{ product.image.url }}" alt="{{ product }}">

</div>

<div>

<a href="{% url 'product' product.id %}"><h4>{{ product }}</h4></a>

</div>

<div>

{{ product.price }}$

</div>

<form action="{% url 'add_to_cart' %}" method="post">

{% csrf_token %}

<input type="hidden" value="{{ product.id }}">

<input type="submit" value="Add to cart">

</form>

</div>

{% if forloop.counter|divisibleby:"2" %}

</div>

{% endif %}

{% endfor %}

{% endblock %}

Page 56: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Страница товара

{% extends 'base.html' %}

{% block title %}{{ product }}{% endblock %}

{% block content %}

<div><img src="{{ product.image.url }}" alt="{{ product }}"></div>

<div>

<h3>{{ product }}</h3>

</div>

<div>{{ product.price }}$</div>

<div>{{ product.description }}</div>

<form action="{% url 'add_to_cart' %}" method="post">

{% csrf_token %}

<input type="hidden" value="{{ product.id }}">

<input type="submit" value="Add to cart">

</form>

{% endblock %}

Page 57: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Корзина ч.1

{% extends 'base.html' %}

{% block title %}Cart{% endblock %}

{% block body %}

<form action="{% url 'cart' %}" method="post">

{% csrf_token %}

<div class="row">

{{ form.as_ul }}

<input type="submit" value="Update order">

</div>

Page 58: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Корзина ч.2

<div class="row">

{% for position_form in formset %}

{{ position_form.as_ul }}

{% endfor %}

</div>

<input type="submit" value="Update order">

</form>

<div class="row">

<form action="{% url 'finish' %}">

{% csrf_token %}

<input type="submit" value="Finish order">

</form>

</div>

{% endblock %}

Page 59: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Страница завершения заказа

{% extends 'base.html' %}

{% block title %}Thank you!{% endblock %}

{% block content %}

Thank you! Your order is {{ order.id }}. You will not be called soon.

{% endblock %}

Страница About us

{% extends 'base.html' %}

{% block title %}About us{% endblock %}

{% block content %}

Glad to see you here!

{% endblock %}

Page 60: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Вопросы?

Page 61: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Ссылки

● https://github.com/SergeyBurma/kranonit-

shop

● https://www.djangoproject.com/

● http://djbook.ru/

● https://pypi.python.org/pypi?%3Aaction=sear

ch&term=django&submit=search

● http://habrahabr.ru/post/159575/

● https://www.jetbrains.com/pycharm/

Page 62: kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно

Спасибо!