├── .DS_Store ├── README.md ├── lesson-1 ├── homework.md └── lesson-1.pdf ├── lesson-10 └── lesson10.pdf ├── lesson-11 ├── async_fetch.py ├── block_socket.py ├── generator_socket.py ├── homework.md ├── lecture_11.pdf ├── select_socket.py └── selectors_socket.py ├── lesson-12 ├── homework.md └── lesson-12.pdf ├── lesson-13 ├── es.py ├── homework.md ├── lesson-13.pdf └── requirements.txt ├── lesson-2 ├── homework.md └── lesson-2.pdf ├── lesson-3 └── lesson-3.pdf ├── lesson-4 ├── homework.md └── lecture_4.pdf ├── lesson-5 ├── homework.md └── lesson-5.pdf ├── lesson-6 ├── homework.md ├── lesson-6.pdf └── project │ ├── manage.py │ ├── movies │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests.py │ ├── urls.py │ └── views.py │ ├── project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py │ ├── requirements.txt │ └── templates │ └── base.html ├── lesson-7 ├── lesson-7.pdf └── templates │ ├── base.html │ ├── home.html │ └── login.html ├── lesson-8 └── lesson-8.pdf └── lesson-9 ├── counter.py ├── gil_io.py ├── homework.md └── lecture_9.pdf /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Back-end разработка на Python 2 | Штучки (презентации и материалы) для курса «Back-end разработка на Python». 3 | -------------------------------------------------------------------------------- /lesson-1/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №1 2 | 3 | ### 1. Написать консольную игру крестики-нолики в виде класса. 4 | 5 | Привожу пример того, как схематично можно изобразить класс игры. 6 | 7 | ```py 8 | class TicTacGame: 9 | 10 | def show_board(): 11 | pass 12 | 13 | def validate_input(): 14 | pass 15 | 16 | def start_game(): 17 | pass 18 | 19 | def check_winner(): 20 | pass 21 | 22 | if __name__ == '__main__': 23 | game = TicTac() 24 | game.start_game() 25 | 26 | ``` 27 | 28 | Пользовательский ввод осуществляется с помощью input, не забудьте валидировать пользовательский ввод и выводить человеческое описание ошибки. 29 | Чтобы вам было комфортнее писать тесты, в методе валидации вы можете возбуждать какое-то исключение в случае неверного ввода, а в методе, где вы выводите ошибку пользователю, обрабатывать это исключение. 30 | 31 | Схема класса не обязательно должна быть, как у меня, можете добавлять и менять методы, как считаете нужным, не забывайте про грамотную организацию кода, ненужное дублирование и код-лапшу. 32 | 33 | Если хотите, можете написать вспомогательную функцию, запустив которую, компьютер сыграет сам с собой без участия человека, либо сделать возможным игру между человеком и компьютером 34 | 35 | 36 | ### 2. Написать тесты (unittest) для игры, покрыв тестами основные методы 37 | 38 | ### 3. Проверить корректность и стиль кода с помощью pylint или flake8 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /lesson-1/lesson-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-1/lesson-1.pdf -------------------------------------------------------------------------------- /lesson-10/lesson10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-10/lesson10.pdf -------------------------------------------------------------------------------- /lesson-11/async_fetch.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import time 4 | 5 | 6 | URL = 'https://loremflickr.com/320/240' 7 | 8 | 9 | async def fetch(url, session): 10 | async with session.get(url, allow_redirects=True) as resp: 11 | data = await resp.read() 12 | with open(f'photo_{time.time()}', 'wb') as f: 13 | f.write(data) 14 | 15 | 16 | async def main(): 17 | tasks = [] 18 | async with aiohttp.ClientSession() as session: 19 | for _ in range(10): 20 | tasks.append(asyncio.create_task(fetch(URL, session))) 21 | 22 | await asyncio.gather(*tasks) 23 | 24 | 25 | if __name__ == '__main__': 26 | t1 = time.time() 27 | asyncio.run(main()) 28 | t2 = time.time() 29 | print('T', t2 - t1) -------------------------------------------------------------------------------- /lesson-11/block_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 5 | server_sock.bind(('localhost', 15000)) 6 | server_sock.listen() 7 | 8 | while True: 9 | print('before accept') 10 | client_sock, addr = server_sock.accept() 11 | print('connect from', addr) 12 | 13 | while True: 14 | data = client_sock.recv(4096) 15 | 16 | if not data: 17 | break 18 | else: 19 | client_sock.send(data.decode().upper().encode()) 20 | 21 | client_sock.close() 22 | -------------------------------------------------------------------------------- /lesson-11/generator_socket.py: -------------------------------------------------------------------------------- 1 | # David Beazley algo 2 | import socket 3 | from select import select 4 | 5 | 6 | tasks = [] 7 | # sock: gen 8 | to_read = {} 9 | to_write = {} 10 | 11 | 12 | def server(): 13 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 14 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 15 | server_sock.bind(('localhost', 15000)) 16 | server_sock.listen() 17 | 18 | while True: 19 | yield 'read', server_sock 20 | client_sock, addr = server_sock.accept() # read 21 | print('connect from', addr) 22 | 23 | tasks.append(client(client_sock)) 24 | 25 | 26 | def client(client_sock): 27 | while True: 28 | yield 'read', client_sock 29 | data = client_sock.recv(4096) # read 30 | 31 | if not data: 32 | break 33 | else: 34 | yield 'write', client_sock 35 | client_sock.send(data.decode().upper().encode()) # write 36 | 37 | client_sock.close() 38 | 39 | 40 | def event_loop(): 41 | while any([tasks, to_read, to_write]): 42 | 43 | while not tasks: 44 | ready_to_read, ready_to_write, _ = select(to_read, to_write, []) 45 | 46 | for sock in ready_to_read: 47 | tasks.append(to_read.pop(sock)) 48 | 49 | for sock in ready_to_write: 50 | tasks.append(to_write.pop(sock)) 51 | 52 | try: 53 | task = tasks.pop(0) 54 | op_type, sock = next(task) 55 | 56 | if op_type == 'read': 57 | to_read[sock] = task 58 | elif op_type == 'write': 59 | to_write[sock] = task 60 | 61 | except StopIteration: 62 | pass 63 | 64 | 65 | if __name__ == '__main__': 66 | tasks.append(server()) 67 | event_loop() 68 | -------------------------------------------------------------------------------- /lesson-11/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание #11 2 | 3 | ## Асинхронный скрип обкачки 4 | Написать скрипт для обкачки списка урлов с возможностью задавать количество одновременных запросов, используя асинхронное программирование. 5 | Клиент можно использовать любой, например, из aiohttp. Так, 10 одновременных запросов могут задаваться командой: 6 | `python fetcher.py -c 10 urls.txt` 7 | 8 | 9 | ## Асинхроная запись файлов на диск 10 | В пример из лекции про загрузку файлов (`async_fetch.py`) добавить асинхронную запись скачиваемых файлов на диск. 11 | Подойдет любой работающий способ. 12 | -------------------------------------------------------------------------------- /lesson-11/lecture_11.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-11/lecture_11.pdf -------------------------------------------------------------------------------- /lesson-11/select_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from select import select 3 | 4 | to_monitor = [] 5 | 6 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 7 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 8 | server_sock.bind(('localhost', 15000)) 9 | server_sock.listen() 10 | 11 | 12 | def accept_conn(server_sock): 13 | client_sock, addr = server_sock.accept() 14 | print('Connect', addr) 15 | to_monitor.append(client_sock) 16 | 17 | 18 | def respond(client_sock): 19 | data = client_sock.recv(4096) 20 | 21 | if data: 22 | client_sock.send(data.decode().upper().encode()) 23 | else: 24 | client_sock.close() 25 | to_monitor.remove(client_sock) 26 | 27 | 28 | def event_loop(): 29 | while True: 30 | ready_to_read, _, _ = select(to_monitor, [], []) # read, write, err 31 | for sock in ready_to_read: 32 | if sock is server_sock: 33 | accept_conn(sock) 34 | else: 35 | respond(sock) 36 | 37 | 38 | if __name__ == '__main__': 39 | to_monitor.append(server_sock) 40 | event_loop() 41 | -------------------------------------------------------------------------------- /lesson-11/selectors_socket.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import selectors 3 | 4 | selector = selectors.DefaultSelector() 5 | print('selector', selector) 6 | 7 | 8 | def server(): 9 | server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 11 | server_sock.bind(('localhost', 15000)) 12 | server_sock.listen() 13 | 14 | selector.register(server_sock, selectors.EVENT_READ, accept_conn) 15 | 16 | 17 | def accept_conn(server_sock): 18 | client_sock, addr = server_sock.accept() 19 | print('Connect', addr) 20 | selector.register(client_sock, selectors.EVENT_READ, respond) 21 | 22 | 23 | def respond(client_sock): 24 | data = client_sock.recv(4096) 25 | 26 | if data: 27 | client_sock.send(data.decode().upper().encode()) 28 | else: 29 | # selector.unregister(client_sock) 30 | client_sock.close() 31 | 32 | 33 | def event_loop(): 34 | while True: 35 | events = selector.select() # (key, events_mask) 36 | 37 | for key, _ in events: 38 | # key: NamedTuple(fileobj, events, data) 39 | callback = key.data 40 | callback(key.fileobj) 41 | 42 | 43 | if __name__ == '__main__': 44 | server() 45 | event_loop() 46 | -------------------------------------------------------------------------------- /lesson-12/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание #12 2 | ### Установить docker и docker-compose 3 | ### Создание Dockerfile для Django-приложения 4 | ### Создание docker-compose для проекта: 5 | - nginx, 6 | - база данных, 7 | - Django-приложение. 8 | ### Создание Makefile для проекта; 9 | Преподаватель должен иметь возможность, имея установленными только git, docker и docker-compose склонировать проект, выполнить команды `make migrate` и увидеть успешную миграцию. 10 | -------------------------------------------------------------------------------- /lesson-12/lesson-12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-12/lesson-12.pdf -------------------------------------------------------------------------------- /lesson-13/es.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from faker import Faker 4 | from elasticsearch import Elasticsearch 5 | 6 | def main(): 7 | es_client = Elasticsearch() 8 | doc = { 9 | "author": "Антон Кухтичев" 10 | } 11 | index_name = 'test-index-msu' 12 | fake = Faker(locale="ru_RU") 13 | for num in range(10): 14 | doc['quote'] = fake.sentence(nb_words=5) 15 | doc['address'] = fake.address() 16 | res = es_client.index(index=index_name, id=num, body=doc) 17 | res = es_client.search(index=index_name, body={"query": {"match_all": {}}}) 18 | print(res) 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /lesson-13/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание #13 2 | 3 | ### Расстояние Левенштейна 4 | Написать функцию, которая будет считать расстояние Левенштейна между двумя словами. Написать тесты для неё, проверяющие корректность работы функции. 5 | 6 | 7 | ### Развернуть и наполнить тестовыми данными Elasticsearch; 8 | Данные должны соответствовать модели для Джанго-приложения из домашней работы 6. 9 | 10 | ### Реализовать поиск по пользователям, продуктам (сущностям); 11 | 12 | ### Реализовать метод API для поиска по указанным сущностям и создать страничку HTML с вёрсткой для поиска и отображения результатов. 13 | Должно быть 2 URL: на одном URL -- HTML-страничка с формой поиска, на втором URL -- API для поиска, отдающий в формате JSON результаты поиска. 14 | -------------------------------------------------------------------------------- /lesson-13/lesson-13.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-13/lesson-13.pdf -------------------------------------------------------------------------------- /lesson-13/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2020.12.5 2 | elasticsearch==7.10.1 3 | factory-boy==3.2.0 4 | Faker==5.1.0 5 | python-dateutil==2.8.1 6 | six==1.15.0 7 | text-unidecode==1.3 8 | urllib3==1.26.2 9 | -------------------------------------------------------------------------------- /lesson-2/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №2 2 | 3 | ## Реализовать LRU cache (least recently used) 4 | 5 | Класс должен содержать следующие методы 6 | ```python 7 | class ICache: 8 | def __init__(self, capacity: int=10) -> None: 9 | pass 10 | 11 | def get(self, key: str) -> str: 12 | pass 13 | 14 | def set(self, key: str, value: str) -> None: 15 | pass 16 | 17 | def delete(self, key: str) -> None: 18 | pass 19 | ``` 20 | Проверяться работоспособность должна так: 21 | ```python 22 | from cache import LRUCache 23 | 24 | cache = LRUCache(100) 25 | cache.set('Jesse', 'Pinkman') 26 | cache.set('Walter', 'White') 27 | cache.set('Jesse', 'James') 28 | cache.get('Jesse') # вернёт 'James' 29 | cache.del('Walter') 30 | cache.get('Walter') # вернёт '' 31 | ``` 32 | 33 | ## Реализовать класс, отнаследованный от списка, такой, что один список 34 | - Можно вычитать из другого [5, 1, 3] - [1, 2, 7] = [4, -1, -4]; 35 | - Можно складывать с другим; 36 | - При неравной длине, дополнять меньший список нулями только на время выполнения операции. Исходные списки не должны изменяться; 37 | - При сравнении списков должна сравниваться сумма элементов списков. 38 | -------------------------------------------------------------------------------- /lesson-2/lesson-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-2/lesson-2.pdf -------------------------------------------------------------------------------- /lesson-3/lesson-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-3/lesson-3.pdf -------------------------------------------------------------------------------- /lesson-4/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание к лекции №4 2 | 3 | ## 1. Подъем по лестнице 4 | Реализовать функцию для вычисления числа возможных вариантов подъема 5 | по лестнице из n ступенек, если на каждом шаге можно подниматься 6 | только на 1 или на 2 ступеньки. 7 | 8 | Исследуйте наивное и оптимизированные решения на потребляемую память и вызовы (профилирование). 9 | 10 | ```python 11 | def climb_stairs(n: int) -> int: 12 | pass 13 | 14 | climb_stairs(2) == 2 15 | climb_stairs(5) == 8 16 | climb_stairs(10) == 89 17 | climb_stairs(45) == 1836311903 18 | ``` 19 | 20 | ## 2. Логирование в ORM 21 | Добавить логирование debug/info/error в ORM. 22 | -------------------------------------------------------------------------------- /lesson-4/lecture_4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-4/lecture_4.pdf -------------------------------------------------------------------------------- /lesson-5/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание №5 2 | ## Разработать клиент-серверное приложение. 3 | - Есть приложение клиента, есть приложение сервера; 4 | - Сервер открывает сетевой сокет по порту и ждёт соединения; 5 | - Первым и единственным аргументом у приложения сервера задаётся путь до конфигурационного файла (конфиг); 6 | - Что должно содержаться в конфиге: 7 | - Номер порта; 8 | - Путь до лога. Может быть пустым и тогда вся информация должна печатать в стандартный поток вывода; 9 | - Режим запуска (демон или нет); 10 | - Таймаут; 11 | - Максимальное количество соединений. 12 | - Очевидно, что параметры конфига должны быть распределены по секциям; 13 | - Каждый запрос сервер должен логировать. Должно быть указано время начала обработки запроса, время окончания запроса, затраченное на обработку запроса время, размер ответа и т.д. 14 | - Сделать поддержку разного формата ответа. Клиент будет передавать параметр, в каком формате он хочет получить ответ от сервера format=[xml|json]; 15 | - Первым аргументом у приложения-клиента задаётся запрос, который необходимо отправить на сервер (запрос состоит из параметров, один из параметров format, остальные зависят от бизнес-логики); 16 | - Клиент ждёт ответ; 17 | - Сервер получает запрос, парсит его, делает бизнес-логику (например ходит за информацией в сторонний сервис по API (например API duckduckgo)), формирует результат и возвращает его клиенту; 18 | - Бизнес-логика не должна быть тривиальной. Перевод в верхний/нижний регистр входящего запроса считается тривиальной бизнес-логикой; 19 | - Клиент печатает ответ сервера в стандартный вывод, далее клиент завершает работу; 20 | - Код должен быть оформлен согласно PEP-8 (*pylint* в помощь); 21 | - В случае ошибки (таймаут, отсутствие необходимых параметров), инцидент должен быть залогирован; 22 | 23 | 24 | Пример запуска сервера (да, нужно прописать [shebang](https://ru.wikipedia.org/wiki/%D0%A8%D0%B5%D0%B1%D0%B0%D0%BD%D0%B3_(Unix)) в скриптах и сделать файлы исполняемыми при помощи chmod +x): 25 | 26 | ```bash 27 | ./server.py config 28 | ``` 29 | 30 | Пример запуска клиента: 31 | 32 | ```bash 33 | ./client.py "q=Москва&format=json&n=3" 34 | ``` 35 | 36 | Пример ответа клиента (понятное дело, что полей должно быть больше): 37 | ```json 38 | { 39 | "results" : [ 40 | { 41 | "title" : "Москва — Википедия" 42 | }, 43 | { 44 | "title" : "История / Информация / Город / Сайт Москвы" 45 | }, 46 | { 47 | "title" : "Агентство городских новостей «Москва»" 48 | } 49 | ], 50 | "error": null 51 | } 52 | ``` 53 | 54 | ##### Подсказка 55 | Чтобы распарсить конфиг можно (или нужно?) использовать библиотеку Python [configparser](https://docs.python.org/3/library/configparser.html). 56 | -------------------------------------------------------------------------------- /lesson-5/lesson-5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-5/lesson-5.pdf -------------------------------------------------------------------------------- /lesson-6/homework.md: -------------------------------------------------------------------------------- 1 | # Домашнее задание №6 2 | ## Джанго-проект 3 | 4 | Необходимо разработать собственный Джанго-проект. Тематика на выбор студента, но не заезженная (типа фильмы, блог и т.д.). 5 | - Создать виртуальное окружение; 6 | - Установить django, djangorestframework (внутри виртуального окружения само собой); 7 | - Создать requirements.txt (при помощи pip freeze); 8 | - Создаём джанго-проект (django-admin startproject <название проекта>); 9 | - Создаём Джанго-приложение (./manage.py startapp <название приложения>); 10 | - А дальше создаём класс-модель на выбор студента; 11 | - Не забываем создать и применить миграции; 12 | - Подключаем [DRF](https://www.django-rest-framework.org/) и создаём для него необходимые вьюшки и обрабатываем методы для работы с REST: 13 | - GET (получить список сущностей); 14 | - POST (обновить сущность); 15 | - PUT (создать новую сущность); 16 | - DELETE (удалить существующую сущность); 17 | - В случае успеха код вовзрата 200, иначе -- 404; 18 | - Ответ должны отдавать в формате json; 19 | - Не забываем коммитить это (venv, \_\_pycache\_\_ и прочий мусор не нужно коммитить, но миграции нужно коммитить!); 20 | 21 | #### Подсказка 22 | Для отправки запросов можно использовать [Postman](https://www.postman.com/downloads/) 23 | -------------------------------------------------------------------------------- /lesson-6/lesson-6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-6/lesson-6.pdf -------------------------------------------------------------------------------- /lesson-6/project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /lesson-6/project/movies/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-6/project/movies/__init__.py -------------------------------------------------------------------------------- /lesson-6/project/movies/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Movie 4 | 5 | @admin.register(Movie) 6 | class MovieAdmin(admin.ModelAdmin): 7 | list_display = ('title', 'year') 8 | -------------------------------------------------------------------------------- /lesson-6/project/movies/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MoviesConfig(AppConfig): 5 | name = 'movies' 6 | -------------------------------------------------------------------------------- /lesson-6/project/movies/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0 on 2020-11-03 16:23 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Movie', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('title', models.CharField(max_length=128, verbose_name='Название фильма')), 19 | ('descr', models.TextField(null=True, verbose_name='Описание фильма')), 20 | ('year', models.IntegerField(null=True, verbose_name='Год выхода')), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /lesson-6/project/movies/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-6/project/movies/migrations/__init__.py -------------------------------------------------------------------------------- /lesson-6/project/movies/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Movie(models.Model): 4 | title = models.CharField('Название фильма', max_length=128, null=False) 5 | descr = models.TextField('Описание фильма', null=True) 6 | year = models.IntegerField('Год выхода', null=True) 7 | -------------------------------------------------------------------------------- /lesson-6/project/movies/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | class MovieSerializer(serializers.Serializer): 4 | title = serializers.CharField(max_length=128) 5 | year = serializers.IntegerField() 6 | -------------------------------------------------------------------------------- /lesson-6/project/movies/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /lesson-6/project/movies/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | from movies.views import movie_view, movie_html_view, MovieView 4 | 5 | urlpatterns = [ 6 | path('', movie_view), 7 | path('memento/', movie_html_view), 8 | path('api/', MovieView.as_view()), 9 | ] 10 | -------------------------------------------------------------------------------- /lesson-6/project/movies/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import HttpResponse 3 | 4 | from rest_framework.response import Response 5 | from rest_framework.views import APIView 6 | 7 | from .models import Movie 8 | from .serializers import MovieSerializer 9 | 10 | def movie_view(request): 11 | return HttpResponse('Hello!') 12 | 13 | def movie_html_view(request): 14 | movies = Movie.objects.all() 15 | return render(request, 'base.html', {'title': 'Memento', 'movies': movies}) 16 | 17 | class MovieView(APIView): 18 | def get(self, requet): 19 | movies = Movie.objects.all() 20 | serializer = MovieSerializer(movies, many=True) 21 | return Response({'movies': serializer.data}) 22 | -------------------------------------------------------------------------------- /lesson-6/project/project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailcourses/technosphere_deep_python/80de2b40a3ed666d5bab69b27566a7194b439caa/lesson-6/project/project/__init__.py -------------------------------------------------------------------------------- /lesson-6/project/project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for project project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = '6tdmyz99uhr2n#p!+bba3od1nh6py9h19dl3ltu*6vz+(n(v*k' 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = [ 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'rest_framework', 40 | 'movies', 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | 'django.middleware.security.SecurityMiddleware', 45 | 'django.contrib.sessions.middleware.SessionMiddleware', 46 | 'django.middleware.common.CommonMiddleware', 47 | 'django.middleware.csrf.CsrfViewMiddleware', 48 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'project.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 59 | 'APP_DIRS': True, 60 | 'OPTIONS': { 61 | 'context_processors': [ 62 | 'django.template.context_processors.debug', 63 | 'django.template.context_processors.request', 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.contrib.messages.context_processors.messages', 66 | ], 67 | }, 68 | }, 69 | ] 70 | 71 | WSGI_APPLICATION = 'project.wsgi.application' 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 76 | 77 | DATABASES = { 78 | 'default': { 79 | 'ENGINE': 'django.db.backends.sqlite3', 80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 91 | }, 92 | { 93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 94 | }, 95 | { 96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 97 | }, 98 | { 99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 100 | }, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 106 | 107 | LANGUAGE_CODE = 'en-us' 108 | 109 | TIME_ZONE = 'UTC' 110 | 111 | USE_I18N = True 112 | 113 | USE_L10N = True 114 | 115 | USE_TZ = True 116 | 117 | 118 | # Static files (CSS, JavaScript, Images) 119 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 120 | 121 | STATIC_URL = '/static/' 122 | -------------------------------------------------------------------------------- /lesson-6/project/project/urls.py: -------------------------------------------------------------------------------- 1 | """project URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('movies/', include('movies.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /lesson-6/project/project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /lesson-6/project/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.0 2 | Django==3.1.3 3 | djangorestframework==3.12.1 4 | pytz==2020.4 5 | sqlparse==0.4.1 6 | -------------------------------------------------------------------------------- /lesson-6/project/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 |
4 |{{ movie.descr }}
9 |