2. Обо мне
● использую Python в своих корыстных и не
очень целях уже пятый год;
● поучаствовал в нескольких больших
незапустившихся проектах;
● приложил руку к сайтам wokifood.ru,
dewote.com, biz-translate.com;
● изредка пишу на хабр (ник batment).
3. О чем вы сегодня узнаете
● немного о Python;
● много о фреймворке Django;
● о том как сделать очень простой
интернет-магазин с админкой.
4. Особенности Python
● приоритет на читаемость;
● данные бывают изменяемые и
неизменяемые (mutable, immutable)
● жесткие стандарты оформления кода;
● любая сущность - это объект;
● очень своеобразное ООП;
● магические методы.
5. Дисклеймер
● мы используем Python 3;
● код представлен максимально наглядно,
но в нем скорее всего есть неувязки и
ошибки, будьте осторожны.
6. Почему Django?
● очень популярен, активно развивается;
● покрывает большинство нужд веб-
разработчиков;
● минимизирует рутину;
● достаточно приятен в обращении.
8. Недостатки Django
● однопоточный и как следствие тормозной;
● нестандартные вещи делать сложно (но
зато более-менее красиво);
● имеет жесткую структуру и идеологию,
что в редких случаях может помешать
разработке.
9. Что понадобится
● настроенная машина с Linux (лучше всего
Ubuntu, подойдет виртуальная) или
MacOS;
● редактор Python и HTML. Для начала
рекомендую PyCharm Professional Edition.
● или вместо всего этого онлайн-среда,
например c9.io
15. Настройки проекта ч.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'
16. Описание модели в Django
● класс, наследуемый от models.Model;
● нужные поля с данными описываются как
поля класса;
● класс Meta, объявленный внутри модели
содержит ее настройки;
● методы __str__ или __unicode__ упростят
текстовый вывод модели.
18. Базовые типы полей
● CharField - простое текстовое поле
ограниченной длинны;
● IntegerField - для хранения целых чисел;
● BooleanField - для хранения логических
данных (True, False)
● ForeignKey - для связи с другой моделью;
● ManyToManyField - для связи со многими
моделями.
19. Загружаемые файлы
● в базовой поставке FileField и
ImageFileField;
● в базе хранится только имя файла;
● файл хранится в MEDIA_ROOT;
● файлы нужно удалять вручную;
● в поле нужно указать upload_to;
● для картинок рекомендую easy_thumbnails
20. Модель 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
21. Модель 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
22. Модель 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
23. Формы в Django
● класс, наследуемый от forms.Form;
● описывается аналогично моделям;
● в описании полей можно указать
необходимый контрол (widget);
● могут генерировать готовый HTML-код.
24. Формы для моделей (ModelForm)
● в параметрах нужно только указать
модель, форма сгенерируется
автоматически;
● можно также указать включенные поля и
их порядок вывода;
● вместо этого можно указать исключенные
поля.
25. Набор форм (FormSet)
● предназначен для ситуаций, когда нужно
редактировать сразу много однотипных
данных;
● генерируется при помощи специальных
функций-фабрик;
● формы-составляющие являются
обычными формами.
26. Форма для добавления в корзину
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']
27. Форма для позиций заказа
class OrderPositionForm(forms.ModelForm):
class Meta:
model = OrderPosition
fields = ['product', 'count']
OrderPositionFormset = forms.inlineformset_factory(
Order,
OrderPosition,
OrderPositionForm,
extra=0,
)
28. Представления в Django
● берут данные из GET или POST-массива,
моделей и форм, передают в шаблон;
● могут быть функцией - минимум
параметров и полная свобода в
функционале или классом на основе
базовых представлений, с настройкой
готового кода.
29. ● get_context_data - формирует данные для
вывода в шаблон, можно добавить туда
свои;
● get_queryset - отправляет запрос в базу,
можно изменить любым образом;
● get_template_name - определяет шаблон,
можно подставлять другой при, например,
ajax-запросе.
Функционал Class Based Views
30. ListView
● служит для вывода списка моделей;
● поддерживает разбитие на страницы
(pagination).
DetailView
● служит для вывода одной модели.
31. Список товаров ч.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)
32. Список товаров ч.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
33. Просмотр товара
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
34. Представление на базе функции
● принимает объект запроса и параметры
URL;
● нужно все делать вручную;
● позволяет вызывать любые побочные
эффекты;
● более явный и понятный код.
35. Представление 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')
36. ● обычный словарь в request.session;
● у каждого пользователя свой на основе
cookie session_id;
● может хранится в базе, в redis или в
cookies (не рекомендуется);
● может быть в Pickles или JSON;
● хранить лучше не модели, а их id.
Сессии в Django
38. CreateView
UpdateView
● для редактирования моделей;
● обязательно принимает pk или slug.
● для создания моделей;
● может сам генерировать форму;
● содержит функции form_valid, form_invalid.
39. Редактирование заказа ч.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())
40. Редактирование заказа ч.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)
43. Админ-сайт в Django
● небольшая встроенная CMS, которой вам
скорее всего хватит;
● позволяет настроить очень многое, но не
все;
● настраивается для каждой модели
отдельно, плюс можно по связям сделать
встроенную админку.
44. Настройка админки каталога
from django.contrib import admin
from catalog.models import Product, ProductCategory
admin.site.register(Product)
admin.site.register(ProductCategory)
45. Настройка админки заказов
class OrderPositionInline(admin.TabularInline):
model = OrderPosition
class OrderAdmin(admin.ModelAdmin):
inlines = [
OrderPositionInline,
]
readonly_fields = ['created_at', 'closed_at']
admin.site.register(Order, OrderAdmin)
46. Маршрутизация в Django
● ROOT_URLCONF со списком url_patterns;
● каждый элемент - это regexp с
соответствующим представлением.
● CBV должны вызвать метод as_view для
вставки в список;
● для представлений-функций достаточно
указать ссылку ‘app.views.view’;
● можно включать другие файлы c
маршрутами через include
47. Маршруты приложения ч.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)),
)
49. Язык шаблонов в Django
● очень простой;
● состоит из тегов и фильтров;
● теги нужны для вывода контента;
● фильтры нужны для обработки
переменных или результатов работы
других фильтров;
● исключения по возможности
подавляются.
50. Главный шаблон
● в основе лежит скелет getskeleton.com
● для встраивания используется тег
{% include ‘filename’ %}
● для наследования используется тег
{% extends ‘filename’ %}
● тег extends должен быть на первой
строке файла.
52. Используемые теги
● url для вставки ссылок;
● csrf_token - нужен для POST-форм;
● if ... else … endif - условный тег
● for … in … endfor - цикл;
● forloop - объект с описанием текущей
итерации цикла, работает только внутри
for … endfor
59. Страница завершения заказа
{% 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 %}