Конвертация строки в объект datetime

Бывает так, что при импорте данных необходимо извлечь дату из текстового поля и преобразовать ее в формат datetime для последующей вставки в базу данных через ORM Django. В общем случае дата может быть в произвольном строковом формате.

Примеры:

Jun 3 2014 5:23PM 
Aug 28 1999 12:00AM

Для такой типовой задачи ожидаемо существует решение в стандартной библиотеке Python — метод strptime модуля datetime. В свою очередь вызов datetime.strptime(date_string, format) является эквивалентом вызова datetime(*(time.strptime(date_string, format)[0:6])).

В итоге задача решается так:

from datetime import datetime
date = datetime.strptime("Jun 3 2014 5:23PM", '%b %d %Y %I:%M%p')

Маска форматирования даты задается аналогично методу strftime(), который реализует обратную по смыслу функциональность.

Python LiveReload: автоматическая перезагрузка страницы

Python LiveReload — это вспомогательная утилита для веб-разработчиков. Запускается как сервер в директории проекта и отслеживает изменения файлов. Работает в паре с LiveReload расширением вашего браузера. Это позволяет избавиться от утомительного нажатия F5 или Ctrl+R в браузере после каждой правки кода.

Установка сервера

pip install livereload

Установка расширения для Chrome

https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei

Запуск

cd ~/my/project/path
livereload

Сервер по умолчанию слушает порт 35729. То есть, чтобы все завелось, нужно открыть ссылку http://localhost:35729/ в браузере.

Дополнительно

livereload --help

Документация: http://livereload.readthedocs.org/en/latest/

Страница проекта: https://github.com/lepture/python-livereload

Автор проекта: Hsiaoming Yang

Vim: совместное использование YouCompleteMe и Virtualenv

Способ 1

Запускать Vim из терминала с уже активированным окружением Virtualenv.

Способ 2

В Vim после активации виртуального окружения с помощью vim-virtualenv:

:VirtualEnvActivate myenv

перезапустить серверную часть YouCompleteMe:

:YcmRestartServer

Библиотека Requests: быстрый старт

Готовы начать? Эта страница дает достаточное представление о том, как начать работу с Requests.

Во-первых, убедитесь, что:

  1. Requests установлена
  2. Requests обновлена

Давайте начнем с нескольких простых примеров.

Создание запроса

Создание запроса с помощью Requests — это очень просто.

Начните с импорта модуля Requests:

>>> import requests

Теперь попробуем получить веб-страницу. Например, давайте получим публичный тайм-лайн GitHub.

>>> r = requests.get('https://api.github.com/events')

Теперь у нас есть объект Response с именем r. Мы можем получить всю необходимую информацию из этого объекта.

Простой API Requests означает, что все формы HTTP запросов являются очевидными. Например, вот как вы можете сделать HTTP POST запрос:

>>> r = requests.post("http://httpbin.org/post")

Круто? А как насчет других типов HTTP запроса: PUT, DELETE, HEAD и OPTIONS? Их выполнить так же просто:

>>> r = requests.put("http://httpbin.org/put")
>>> r = requests.delete("http://httpbin.org/delete")
>>> r = requests.head("http://httpbin.org/get")
>>> r = requests.options("http://httpbin.org/get") 

Это уже хорошо. Даже здорово. Но это далеко не все из того, что может делать Requests.

Передача параметров в URL

Часто вы хотите послать какие-то данные в строке запроса URL. Если вы строите URL вручную, то эти данные будут представлены в нем в виде пар ключ-значение после знака вопроса. Например, httpbin.org/get?key=val. Requests позволяет передать эти аргументы в качестве словаря, используя аргумент params. В качестве примера, если вы хотите передать key1=value1 и key2=value2 ресурсу httpbin.org/get, вы должны использовать следующий код:

>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get("http://httpbin.org/get", params=payload)

Вы можете видеть, что URL был закодирован правильно:

>>> print(r.url)
http://httpbin.org/get?key2=value2&key1=value1

Заметим, что любой ключ словаря, значение которого None, не будет добавлен к строке запроса URL.

Содержимое ответа

Мы можем читать содержимое ответа сервера. Рассмотрим тайм-лайн GitHub снова:

>>> import requests
>>> r = requests.get('https://api.github.com/events') 
>>> r.text 
u'[{"repository":{"open_issues":0,"url":"https://github.com/... 

Requests будет автоматически декодировать содержимое ответа сервера. Большинство Unicode кодировок без проблем декодируются.

Когда вы делаете запрос, Requests делает предположение о кодировке, основанное на заголовках HTTP. Кодировка текста, угаданная Requests, используется при обращение к r.text. Вы можете узнать, какую кодировку использует Requests, и изменить её воспользовавшись свойством r.encoding:

>>> r.encoding
'utf-8' 
>>> r.encoding = 'ISO-8859-1' 

Если вы измените кодировку, Requests будет использовать новое значение r.encoding всякий раз, когда вы будете использовать r.text. Вы можете сделать это в любой ситуации, где нужна более специализированная логика работы с кодировкой содержимого ответа. Например, в HTML и XML есть возможность задавать кодировку прямо в теле документа. В подобных ситуациях вы должны использовать r.content, чтобы найти кодировку, а затем установить r.encoding. Это позволит вам использовать r.text с правильной кодировкой.

Requests может также использовать пользовательские кодировки в случае, если вы в них нуждаетесь. Если вы создали свою собственную кодировку и зарегистрировали её в модуле codecs, вы можете просто использовать название кодека в качестве значения r.encoding, и Requests будет работать с этой кодировкой для вас.

Бинарное содержимое ответа

Вы также можете получить доступ к телу ответа в виде байтов для нетекстовых запросов:

>>> r.content 
b'[{"repository":{"open_issues":0,"url":"https://github.com/... 

Передача со сжатием gzip и deflate автоматически декодируется.

Например, чтобы создать изображение из бинарных данных, возвращаемых в ответ на запрос, вы можете использовать следующий код:

>>> from PIL import Image 
>>> from StringIO import StringIO 
>>> i = Image.open(StringIO(r.content)) 

JSON содержимое ответа

С библиотекой Requests также поставляется встроенный JSON декодер на случай, если вы имеете дело с данными в формате JSON:

>>> import requests 
>>> r = requests.get('https://api.github.com/events') 
>>> r.json() 
[{u'repository': {u'open_issues': 0, u'url': 'https://github.com/... 

В случае, если декодирование JSON не удается, r.json выбрасывает исключение. Например, если приходит ответ с кодом статуса 401 (неавторизованный), попытка обращения к r.json выбрасывает исключение ValueError: No JSON object could be decoded.

Необработанное содержимое ответа

В редких случаях, когда вы хотите получить доступ к сырому ответу сервера на уровне сокета, вы можете обратиться к r.raw. Если вы хотите сделать это, убедитесь, что вы установили stream=True в вашем первом запросе. После этого вы уже можете проделать следующее:

>>> r = requests.get('https://api.github.com/events', stream=True) 
>>> r.raw 
<requests.packages.urllib3.response.HTTPResponse object at 0x101194810> 
>>> r.raw.read(10) 
'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03' 

Теперь вы можете использовать подобный код как шаблон, чтобы сохранить получаемый поток в файл:

>>> with open(filename, 'wb') as fd: 
>>>     for chunk in r.iter_content(chunk_size): 
>>>         fd.write(chunk) 

Использование Response.iter_content будет обрабатывать многое из того, с чем бы вам пришлось иметь дело при использовании Response.raw напрямую. Описанное выше является предпочтительным и рекомендуемым способом извлечения содержимого при потоковой загрузке.

Пользовательские HTTP заголовки

Если вы хотите добавить HTTP-заголовки в запрос, просто передайте соответствующий словарь в параметре headers.

Например, мы не указали заголовок content-type в предыдущем примере:

>>> import json
>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some': 'data'} 
>>> headers = {'content-type': 'application/json'}
>>> r = requests.post(url, data=json.dumps(payload), headers=headers)

Более сложные POST запросы

Зачастую вы хотите послать некоторые form-encoded данные также как это делается в HTML форме. Чтобы сделать это, просто передайте соответствующий словарь в аргументе data. Ваш словарь данных в таком случае будет автоматически закодирован как HTML форма, когда будет сделан запрос:

>>> payload = {'key1': 'value1', 'key2': 'value2'} 
>>> r = requests.post("http://httpbin.org/post", data=payload) 
>>> print(r.text) 
{ ... "form": { "key2": "value2", "key1": "value1" }, ... } 

Но есть много случаев, когда вы можете захотеть отправить данные, которые не закодированы методом form-encoded. Если вы передадите в запрос строку вместо словаря, то данные будут отправлены в неизменном виде.

Например, API v3 GitHub принимает JSON-закодированные POST/PATCH данные:

>>> import json url = 'https://api.github.com/some/endpoint' 
>>> payload = {'some': 'data'}
>>> r = requests.post(url, data=json.dumps(payload)) 

Как послать Multipart-Encoded файл

Requests позволяет легко послать на сервер Multipart-Encoded файлы:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': open('report.xls', 'rb')}
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "<censored...binary...data>" }, ... } 

Вы можете установить имя файла, content-type и заголовки в явном виде:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})} 
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "<censored...binary...data>" }, ... } 

Если вы хотите, вы можете отправить строки, которые будут приняты в виде файлов:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
>>> r = requests.post(url, files=files) 
>>> r.text 
{ ... "files": { "file": "some,data,to,send\nanother,row,to,send\n" }, ... } 

В случае, если вы отправляете очень большой файл как multipart/form-data, вы можете захотеть отправить запрос потоком. По умолчанию requests не поддерживает этого, но есть отдельный пакет, который это делает — requests-toolbelt. Ознакомьтесь с документацию Toolbelt.

Для отправки нескольких файлов в одном запросе, обратитесь к дополнительной документации.

Коды состояния ответа

Мы можем проверить код состояния ответа:

>>> r = requests.get('http://httpbin.org/get') 
>>> r.status_code 
200 

Для удобства Requests также поставляется со встроенным объектом подстановок кодов состояния:

>>> r.status_code == requests.codes.ok 
True 

Если мы сделали плохой запрос (ошибка 4XX клиента или ошибка 5XX ответа сервера), то мы можем возбудить исключение с помощью Response.raise_for_status() :

>>> bad_r = requests.get('http://httpbin.org/status/404') 
>>> bad_r.status_code 
404
>>> bad_r.raise_for_status() 
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error
Traceback (most recent call last):
  File "requests/models.py", line 832, in raise_for_status
    raise http_error
requests.exceptions.HTTPError: 404 Client Error 

Но если status_code для г оказался 200, то когда мы вызываем raise_for_status() мы получаем:

>>> r.raise_for_status() 
None 

Это значит, что все в порядке.

Заголовки ответов

Мы можем просматривать заголовки ответа сервера, используя словарь Python:

>>> r.headers { 'content-encoding': 'gzip', 'transfer-encoding': 'chunked', 'connection': 'close', 'server': 'nginx/1.0.4', 'x-runtime': '148ms', 'etag': '"e1ca502697e5c9317743dc078f67693f"', 'content-type': 'application/json' } 

Однако это словарь особого рода: он сделан специально для HTTP заголовков. Согласно RFC 7230, имена заголовков HTTP нечувствительны к регистру.

Таким образом, мы можем получить доступ к заголовков с капитализацией или без, если захотим:

>>> r.headers['Content-Type'] 
'application/json' 
>>> r.headers.get('content-type') 
'application/json' 

Cookies

Если ответ содержит cookie, вы можете быстро получить к ним доступ:

>>> url = 'http://example.com/some/cookie/setting/url' 
>>> r = requests.get(url)
>>> r.cookies['example_cookie_name'] 
'example_cookie_value' 

Для отправки собственных cookie на сервер, вы можете использовать параметр cookies:

>>> url = 'http://httpbin.org/cookies' 
>>> cookies = dict(cookies_are='working')
>>> r = requests.get(url, cookies=cookies) 
>>> r.text 
'{"cookies": {"cookies_are": "working"}}' 

Редиректы и история

По умолчанию Requests будет выполнять редиректы для всех HTTP глаголов, кроме HEAD.

Мы можем использовать свойство history объекта Response, чтобы отслеживать редиректы.

Список Response.history содержит объекты Response, которые были созданы во время выполнения запроса. Список сортируется от более ранних к более поздним ответам.

Например, GitHub перенаправляет все HTTP запросы на HTTPS:

>>> r = requests.get('http://github.com') 
>>> r.url 'https://github.com/' 
>>> r.status_code 
200 
>>> r.history 
[<Response [301]>] 

Если вы используете GET, OPTIONS, POST, PUT, PATCH или DELETE, вы можете отключить обработку редиректов с помощью параметра allow_redirects:

>>> r = requests.get('http://github.com', allow_redirects=False) 
>>> r.status_code 
301 
>>> r.history 
[] 

Если вы используете HEAD, вы можете включить обработку редиректов:

>>> r = requests.head('http://github.com', allow_redirects=True) 
>>> r.url 'https://github.com/' 
>>> r.history 
[<Response [301]>]

Тайм-ауты

Вы можете сказать Requests прекратить ожидание ответа после определенного количества секунд с помощью параметра timeout:

>>> requests.get('http://github.com', timeout=0.001) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)

Примечание: timeout это не ограничение по времени полной загрузки ответа. Исключение возникает, если сервер не дал ответ за timeout секунд (точнее, если ни одного байта не было получено от основного сокета за timeout секунд).

Ошибки и исключения

В случае неполадок в сети (например, отказа DNS, отказался соединения и т.д.), Requests возбудит исключение ConnectionError.

В более редком случае неверного HTTP ответа, Requests возбудит исключение HTTPError.

Если превышено время ожидания ответа, возбуждается исключение Timeout.

Если запрос превышает заданное значение максимального количества редиректов, то возбуждается исключение TooManyRedirects.

Все исключения, которые возбуждает непосредственно Requests, унаследованы от requests.exceptions.RequestException.

Готовы для большего?

Ознакомьтесь с дополнительным разделом документации.

Эта статья — перевод официальной документации Requests. Оригинал: Quickstart — Requests documentation.

Django: загрузка FileField и ImageField из файловой системы

Не знаю как вы, а я чаще стал сталкиваться с ситуацией, когда мне нужно программно загрузить файл из локальной файловой системы или с помощью удаленного URL в модель Django. Это может понадобиться в контексте работы в оболочке Python, исполнения отдельного скрипта или команды управления Django.

В типичном веб-приложении такая задача встречается нечасто. Я заметил, что мы это делаем, когда переносим сайт с какой-то другой технологии на Django, и привязываем изображение к некой контентной сущности.

Если вы поищете ответ как сделать это в Google, то обнаружите документацию Django File Uploads, File objects и Managing Files, которые сами по себе отличные. Тем не менее они не содержат последовательного руководства для решения нашей задачи.

Первый порыв — присвоить полю модели путь к файлу, но это ведет к потере информации об относительном пути к файлу, в которой нуждается Django.

Я выделил время, чтобы написать этот пост в основном для собственного блага, чтобы не искать заново решение в будущем. Но, надеюсь, вы также получите некоторую пользу.

Этапы загрузки локального файла в Django ImageField

Действия, которые необходимо выполнить:

  1. Получить файл (если он удаленный) и сохранить его локально
  2. Открыть файл как обычный объект файла Python
  3. Конвертировать открытый файл в Django FIle
  4. Привязать его к полю вашей модели

Пример модели

Предположим, у нас есть модель вроде этой:

from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=100) 
    logo = models.ImageField()

Давайте создадим запись для RevSys, скачав логотип с помощью библиотеки requests:

import requests
from django.core.files import File

from .models import Company

r = requests.get(“http://media.revsys.com/img/revsys-logo.png”)

with open(“/tmp/revsys-logo.png”, “wb”) as f:
    f.write(r.content)

reopen = open(“/tmp/revsys-logo.png”, “rb”)
django_file = File(reopen)

revsys = Company()
revsys.name = “Revolution Systems”
revsys.logo.save(“revsys-logo.png”, django_file, save=True)

Последняя строка здесь самая важная. Методу save передается три аргумента:

  1. Относительный путь и имя файла внутри MEDIA_ROOT.
  2. Файл, открытый с помощью File объекта Django.
  3. Логическое значение, показывающее хотим ли мы сохранить экземпляр revsys модели Company после того, как изображение сохранено.

Иногда, если вы работаете с набором изображений, вам требуется распарсить URL или пути к файлам для извлечения имен файлов. Нижеследующий код послужит отправной точкой. Обратите внимание, что тут используется Python 3:

import os 
from urllib.parse import urlparse

# from urlparse import urlparse for Python 2.7

url = “http://media.revsys.com/img/revsys-logo.png” 
filename = os.path.basename(urlparse(url).path)

# This returns ‘revsys-logo.png’ from the URL

…

revsys.logo.save(filename, django_file, save=True)`

Надеюсь, это вам поможет!

Эта статья перевод. Оригинал: Loading Django FileField and ImageFields from the file system

Top-20 самых активных питонистов

Наткнулся сегодня на рейтинг самых активных пользователей GitHub за 2013 год. Думаю, это люди, у которых есть чему поучиться.

Это список top-20 питонистов из рейтинга со ссылками на профиль GitHub и личные сайты.

#19 fsouza (Francisco Souza) Rio de Janeiro, Brazil http://f.souza.cc
#36 alex (Alex Gaynor) San Francisco http://alexgaynor.net
#41 andrewsmedina (Andrews Medina) Rio de Janeiro http://www.andrewsmedina.com
#45 dcramer (David Cramer) San Francisco, CA http://justcramer.com
#80 pydanny (Daniel Greenfeld) Inland Empire, CA http://pydanny.com
#89 ask (Ask Solem Hoel) London, UK https://twitter.com/asksol
#114 stephenmcd (Stephen McDonald) Sydney Australia http://blog.jupo.org
#129 benoitc (Benoit Chesneau) Creil, France http://benoitc.im
#131 kennethreitz (Kenneth Reitz) Winchester, VA http://kennethreitz.org
#137 jtauber (James Tauber) Burlington, MA http://jtauber.com
#143 ericholscher (Eric Holscher) Portland, Or http://ericholscher.com
#165 audreyr (Audrey Roy) Inland Empire, CA http://www.audreymroy.com
#180 jezdez (Jannis Leidel) Berlin, Germany https://twitter.com/jezdez
#181 bitprophet (Jeff Forcier) Lafayette, CA http://bitprophet.org
#189 klen (Kirill Klenov) Russia, Moscow http://klen.github.io
#191 yuvipanda (Yuvi Panda) Chennai http://yuvi.in
#202 mitsuhiko (Armin Ronacher) United Kingdom / Austria http://lucumr.pocoo.org
#221 bryanveloso (Bryan Veloso) Los Angeles, CA http://avalonstar.com
#223 toastdriven (Daniel Lindsley) Lynnwood, WA http://www.toastdriven.com
#225 jakevdp (Jake Vanderplas) Seattle WA http://jakevdp.github.io

Почти все персонажи уже были знакомы, но несколько новых лент я добавил в Feedly.

Web Development with Python and Django

ОтКройТе ГлаЗА!
Вчера утром после деплоя, я посмотрел на этот мир по другому. Почему вы так живете? Почему вы не можете открыть глаза? Мне не нравится что в этом городе нет солнца, что везде заводы и трубы, что холодно и деревья без листьев. Метро – Муравейник роботов. Кто – то из вас тоже робот который боится выйти за границу своего тела, подняться выше метро и заводов. А кто – то меня поймет. Мы другие. Мы выше и мудрее вас. И нас больше чем вы думаете. Я не говорю про Lisp, его я боюсь. Я говорю про Python. Сегодня я уже радовался всем мелочам. Обращая внимание на мелочи рождается совершенство, а совершенство это не мелочи!!! И только Django дало мне это понять. Разве это плохо? Разве плохо быть другим?

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

Недавно перенес оба-два свои блога на WordPress с Петерхоста на Locum. Пока полет нормальный — Locum дешевле, удобней и стабильнее. Также я рекомендовал этот хостинг заказчику для размещения небольшого Django сайта, который я сейчас разрабатываю. О том, как развернуть Django 1.4 проект на виртуальном хостинге Locum.ru, и пойдет речь в этом посте.

Итак, в панели управления создаем новый проект, например, mysite.

Заходим на сервер по SSH и скачиваем последний virtualenv:


wget https://raw.github.com/pypa/virtualenv/master/virtualenv.py

Создаем виртуальное Python окружение:


python virtualenv.py ~/env/mysite

Флаг --no-site-packages указывать не нужно — теперь это поведение по умолчанию.

Активируем окружение:


source ~/env/mysite/bin/activate

На локальной машине создаем и редактируем файл зависимостей проекта:


pip freeze > req.txt

Заливаем файл на сервер и устанавливаем все необходимые пакеты:


pip install -r req.txt

С подготовкой окружения закончили, переходим непосредственно к работе с проектом. Все проекты находятся в директории ~/projects, в том числе и наш mysite. Locum создает дефолтный проект автоматом, он не нужен — чистим директории apps, media и static. Заливаем проект и дамп базы. Удаляем wsgi.py файл, использовавшийся на локальной машине, правим файл setting.py. Правим django.wsgi файл, созданный хостингом:


#!/usr/bin/python
# -*- coding: utf-8 -*-
activate_this = '/home/hosting_login/env/mysite/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
import os
import sys

sys.path.insert(0, '/home/hosting_login/env/mysite/lib/python2.6/site-packages')

sys.path.insert(0, '/home/hosting_login/projects/mysite/apps')

os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Собираем статику:


python manage.py collectstatic

Перезапускаем приложение кнопкой в панели хостинга или изменением времени редактирования файла django.wsgi


touch django.wsgi

Не заработал sorl-thumbnail в Ubuntu 12.04

Для ресайза изображений в Django-проектах я пользуюсь батарейкой sorl-thumbnail. Одно из достоинств это приложения — возможность выбора движка для работы с изображениями (по умолчанию PIL). Так вот, после очередного апгрейда Ubuntu, хумбнейлы молча перестали генерироваться.

Даю sorl-thumbnail голос — в settings.py проекта:


THUMBNAIL_DEBUG = True

Вижу причину отказа: IOError: «decoder zip not available». Суть ошибки в том, что в последних версиях Ubuntu (кажется, 11.04) сменилось расположение библиотек libfreetype.so, libjpeg.so и libz.so. А в PIL последней версии эти изменения не учтены. Значит необходимо либо прописать новые пути в setup.py PIL, как описано здесь. Либо поставить симлинки старых путей в системе, как описано здесь. Я выбрал второй вариант, так как ставлю PIL в свое окружение virtualenv для каждого проекта.

Не помешает также установить и все зависимости PIL:


sudo apt-get build-dep python-imaging