Web осень 2013 лекция 6

Post on 07-Jul-2015

269 views 0 download

Transcript of Web осень 2013 лекция 6

Работа с базой. Django ORM

Александр Бекбулатов

На клиенте

• Cookie (до 4Кб)

• HTML5 Web Storage (до 5Мб)

• В памяти

• На диске

• На диске и в памяти

Где хранить данные

2

На сервере

1. Представление о СУБД

2. Проектирование баз данных

3. Основные операции SQL

4. Работа с базами данных в python

5. Работа с базами данных в django

Работа с базой

3

База данных

взаимосвязанная информация (данные) об

объектах, которая организованна специальным

образом и хранится на каком-либо носителе.

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

средств общего или специального назначения,

обеспечивающих управление созданием и

использованием баз данных.

Представление о СУБД

4

СУБД

Функции СУБД

управление данными на дисках и в оперативной памяти

журнализация изменений, резервное копирование и

восстановление базы данных после сбоев;

поддержка языков БД (язык определения данных, язык

манипулирования данными).

Представление о СУБД

5

Таблица – отношение, relation

Строка – кортеж, tuple

Столбец – атрибут, column

Реляционная модель данных

6

Car

Key Model Color Year

1 Toyota Camry 1 2010

2 Audi A4 2 2013

3 Honda Civic 2 2013

Color

Key Name

1 Black

2 Pink

Реляционная модель данных

7

Cousine

Key Name Type

1 Baked fish Main Course

2 Pork Chops Main Course

CousineIngredient

CousineKey IngredientKey

1 3

2 1

Ingredient

Key Name

1 Pork

2 Lamb

3 Salmon

Задачи проектирования

Обеспечение хранения в БД всей необходимой информации.

Обеспечение возможности получения данных по всем

необходимым запросам.

Сокращение избыточности и дублирования данных.

Обеспечение целостности данных (правильности их

содержания): исключение противоречий в содержании данных,

исключение их потери и т.д.

Реляционная модель данных

8

Проектирование на практике

Логическое разделение сущностей

Выделение синтетических первичных ключей

Связи 1:N, N:1 реализуются через внешний ключ

Связи N:M реализуются через промежуточную таблицу

Атрибут с фиксированным числом значений – внешняя

таблица либо поле типа enum

Реляционная модель данных

9

Основные операции SQL

10

SELECT * FROM users WHERE age > 10;

SELECT * FROM users WHERE name = 'masha';

SELECT max(age) FROM users;

SELECT id, name, length(name) AS len

FROM users

WHERE email LIKE '%@mail.ru' AND age > 10

ORDER BY name DESC

LIMIT 10 OFFSET 15

SELECT

11

Агрегатные функции:

COUNT, SUM, AVG, GROUP_CONCAT

SELECT name, count(id) cnt

FROM users

GROUP BY name

ORDER BY cnt

HAVING cnt > 1

SELECT. Агрегация

12

SELECT h.name, a.name

FROM heroes h, abilities a

WHERE h.id = a.hero_id

SELECT h.name, a.name

FROM heroes h

INNER JOIN abilities a ON h.id = a.hero_id

SELECT h.name, a.name

FROM heroes h

LEFT JOIN abilities a ON h.id = a.hero_id

SELECT. JOIN

13

SELECT title

FROM article t1

JOIN (

SELECT rubric_id, MAX(id) max_id

FROM article

GROUP BY rubric_id LIMIT 5

) t2

ON t1.id = t2.max_id;

SELECT. Вложенные запросы

14

INSERT INTO users (name, age) VALUES ('Petr', 10);

UPDATE users SET age = 20 WHERE name = 'Petr';

UPDATE users SET rating = rating + 1;

DELETE FROM users WHERE name = 'Masha';

DELETE FROM users WHERE age > 150;

INSERT, UPDATE, DELETE

15

Проектирование на практике

Создавать индексы для полей, по которым происходит JOIN

Создавать индексы для полей, по которым фильтруются записи (WHERE)

Создавать индексы для полей, по которым идет сортировка (ORDER)

Проверять план выполнения запроса (EXPLAIN)

Управление оптимизатором (принудительное использование индекса, порядок соединения таблиц)

Индексы

16

Работа с базами данных в

python

17

import MySQLdb

db = MySQLdb.connect(

host="localhost", user="joebob",

passwd="moonpie", db="thangs")

cursor = db.cursor()

MySQLdb

18

cursor.execute('update users set age = age + 1

where name = %s', (name,))

cursor.execute('select * from users')

users = cursor.fetchall()

cursor.execute('select * from users where name =

%s', (name,))

user = cursor.fetchone()

MySQLdb

19

cursor.executemany(

"INSERT INTO users (name, age) VALUES (%s,

%s)",

[

("Igor", 18 ),

("Petr", 16 ),

("Dasha", 17 )

])

db.close()

MySQLdb

20

Зачем плейсхолдеры?

email = "' OR '1'='1"

cursor.execute("SELECT * FROM users WHERE email =

'%s'" % email)

Документация

http://mysql-python.sourceforge.net/MySQLdb.html

MySQLdb

21

Работа с базами данных в

django

22

class Employee(object):

def __init__(self, name, salary):

self.name = name

self.salary = salary

def __unicode__(self):

return unicode(self.name)

def get_info(self):

return "Name : %s, Salary: %s" % (self.name,

self.salary)

class Programmer(Employee):

def __init__(self, name, salary, lang):

super(Programmer, self).__init__(name,

salary)

self.lang = lang

Классы в Python

23

>>> e1 = Programmer(u'Vasya', 100, 'VB')

>>> print u'Welcome %s' % e1

Welcome Vasya

>>> e1.get_info()

u'Name : Vasya, Salary: 100'

>>> e1.lang

'VB'

Классы в Python

24

cursor.execute('select * from users where age >

18')

for user in cursor.fetchall():

pk, name, age = user

print name

VS

for user in User.objects.filter(age__gt=18):

print user.name

Raw SQL vs ORM

25

class Post(models.Model):

title = models.CharField(max_length=255)

content = models.TextField()

creation_date =

models.DateTimeField(default=datetime.datetime.now)

def __unicode__(self):

return self.title

def get_absolute_url(self):

return '/post/%d/' % self.pk

def get_next_post(self):

"""Return the next entry"""

class Meta:

ordering = ['-creation_date’]

Модели и типы данных

26

Модели и типы данных

27

Типы данных Mysql Типы полей Django

varchar(N) CharField

EmailField

SlugField

ImageField

longtext TextField

tinyint(1) BooleanField

int(11) IntegerField

smallint(5) unsigned PositiveSmallIntegerField

date DateField

datetime DateTimeField

Свойства полей моделей

null, blank, choices, db_index, default, editable,

help_text, max_length, primary_key, unique, unique_for_date,

unique_for_month, unique_for_year, verbose_name

https://docs.djangoproject.com/en/dev/ref/models/fields/

Модели и типы данных

28

class Post(models.Model):

title = models.CharField(max_length=255)

content = models.TextField()

creation_date =

models.DateTimeField(default=datetime.datetime.now)

category = models.ForeignKey(Category,

on_delete=models.SET_NULL)

tags = models.ManyToManyField(Tag)

Связи между таблицами

29

ForeignKey, OneToOneField

ForeignKey = IntegerField + Constraint

OneToOneField = ForeignKey + unique

post.category – связанный объект (отдельный запрос к

связанной сущности)post.category_id – значение атрибута модели (просто id)

В обратную сторону:

category.post_set.all()

Связи между таблицами

30

ForeignKey, OneToOneField

Изменение/удаление внешнего ключа

• RESTRICT -> models.PROTECT

• CASCADE -> models.CASCADE

• SET NULL -> models.SET_NULL

• NO ACTION -> models.DO_NOTHING

Связи между таблицами

31

Связи между таблицами

32

Post

id

title

content

creation_date

category

Tagidtitledescription

PostTag

post_idtag_id

ManyToManyField

ManyToManyField

post.tags – related managerpost.tags.all()

В обратную сторону:tag.post_set.all()

Связи между таблицами

33

python manage.py shell

>>> from blog.models import Category, Post

>>> from django.db import connection

>>> # Вставка и замена

>>> c = Category(title="Python")

>>> c.save()

>>> c.id

1

>>> c.title="About Python"

>>> c.save()

ORM API

34

python manage.py shell

>>> print '\n'.join(q['sql'] for q in

connection.queries)

INSERT INTO `blog_category` (`title`,

`description`) VALUES ('Python', '')

SELECT (1) AS `a` FROM `blog_category` WHERE

`blog_category`.`id` = 1 LIMIT 1

UPDATE `blog_category` SET `title` = 'About

Python', `description` = '' WHERE

`blog_category`.`id` = 1

ORM API

35

>>> # Выборка

>>> Category.objects.all()

[<Category: About Python>]

>>> print Category.objects.all().query

SELECT `blog_category`.`id`,

`blog_category`.`title`,

`blog_category`.`description` FROM `blog_category`

>>> Category.objects.filter(id=1)

[<Category: About Python>]

>>> c = Category.objects.get(id=1)

>>> c

<Category: About Python>

>>> c.post_set.all()

[]

ORM API

36

>>> c.post_set.create(title="New post",

content="Many words")

<Post: New post>

>>> c.post_set.count()

1

>>> p = Post.objects.get(title="New post")

>>> p.category

<Category: About Python>

>>> p.category_id

1

>>> Post.objects.filter(category__title="About

Python")

<Post: New post>

>>> c.delete()

ORM API

37

Debug Toolbarhttps://github.com/django-debug-

toolbar/django-debug-toolbar

38

Методы Manager

slice [10:20]

.all(), .filter(), .exclude(), .order_by()

.values(), .values_list()

.get(), .get_or_create(), .count(), .exists()

.select_related(), .prefetch_related()

.update(), .delete()

Manager и RelatedManager

39

Методы RelatedManager

.create(**kwargs)

.add(obj1[, obj2, ...])

.remove(obj1[, obj2, ...])

.clear()

Manager и RelatedManager

40

Queryset ленивый

qs = Category.objects.all()

if not user.is_admin:

qs = qs.filter(active=True)

print qs.count()

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

41

Избегайте лишних запросов в базу

Foreign key кешируется

post.category

Queryset – нет.

Если нужно использовать несколько раз – кешируйте явно

question_list = list(Question.objects.all())

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

42

Атомарное обновление

Question.objects.filter(pk=10) \

.update(rating=models.F('rating') + 1)

https://docs.djangoproject.com/en/dev/ref/models/querysets/

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

43

CREATE DATABASE ask_db CHARACTER SET utf8

COLLATE utf8_general_ci;

GRANT ALL PRIVILEGES ON ask_db.* TO

ask_user@localhost identified BY 'secret';

Создание базы

44

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.mysql',

'NAME': 'ask_db',

'USER': 'ask_user',

'PASSWORD': 'secret',

'HOST': '',

'PORT': '',

}

}

Подключение базы

45

Проверка моделей

python manage.py validate

Вывод SQL

python manage.py sqlall <app_name>

Выполнение SQL в базе

python manage.py syncdb

Полезные команды

46

CREATE TABLE `blog_post` (

`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,

`title` varchar(255) NOT NULL,

`content` longtext NOT NULL,

`creation_date` datetime NOT NULL,

`category_id` integer NOT NULL

)

;

ALTER TABLE `blog_post` ADD CONSTRAINT

`category_id_refs_id_e0428883` FOREIGN KEY

(`category_id`) REFERENCES `blog_category` (`id`);

ALTER TABLE `blog_post_tags` ADD CONSTRAINT

`post_id_refs_id_c9e37cca` FOREIGN KEY (`post_id`)

REFERENCES `blog_post` (`id`);

CREATE INDEX `blog_post_42dc49bc` ON `blog_post`

(`category_id`);

SQL. Создание таблиц

47

DROP TABLE blog_post;

ALTER TABLE users ADD COLUMN language

enum('ru', 'en') NOT NULL DEFAULT 'ru' after

name;

ALTER TABLE users DROP COLUMN langauge;

ALTER TABLE users ADD INDEX ('name');

ALTER TABLE users CHANGE nickname fullname

varchar(255) NOT NULL;

SQL. Удаление и изменение таблиц

48

1. DROP DATABASE, CREATE DATABASE

2. DROP TABLE, CREATE TABLE

3. ALTER TABLE

4. South

Миграции

49

• Автоматическое создание миграций

• Поддержка различных СУБД

• Прямые и обратные миграции

South

50

1. Создать миграцию

./manage.py schemamigration ask --initial

2. Применить миграцию

./manage.py migrate ask

3. Изменить модель

4. Создать миграцию

./manage.py schemamigration ask --auto

5. Применить миграцию

./manage.py migrate ask

South Workflow

51

1. MySQL http://dev.mysql.com/doc/refman/5.5/en/index.html

2. Mysqldb http://mysql-python.sourceforge.net/MySQLdb.html

3. Django http://docs.djangoproject.com/en/dev/

4. Django Debug Toolbar https://github.com/django-debug-toolbar/django-debug-toolbar

52

1. Установить Django Debug Toolbar

2. Установить MySQL, python-mysqldb. Создать базу данных и пользователя для работы с базой

3. Описать модели для проекта «Вопросы и ответы»

4. Наполнить базу тестовыми данными

53

Спасибо за внимание

Александр Бекбулатов, a.bekbulatov@corp.mail.ru