{
    "version": "https:\/\/jsonfeed.org\/version\/1",
    "title": "Блог об аналитике, визуализации данных, data science и BI, заметки с тегом: sqlalchemy",
    "home_page_url": "http:\/\/test.leftjoin.ru\/tags\/sqlalchemy\/",
    "feed_url": "http:\/\/test.leftjoin.ru\/tags\/sqlalchemy\/json\/",
    "icon": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg",
    "author": {
        "name": "Николай Валиотти",
        "url": "http:\/\/test.leftjoin.ru\/",
        "avatar": "http:\/\/test.leftjoin.ru\/user\/userpic@2x.jpg"
    },
    "items": [
        {
            "id": "94",
            "url": "http:\/\/test.leftjoin.ru\/all\/tranzakcii-v-sqlalchemy\/",
            "title": "Транзакции в SQLAlchemy",
            "content_html": "<p>Транзакция — последовательность действий, связанных с базой данных. Их основная польза заключается в том, что при возникновении какой-то ошибки или достижении других нужных условий всю транзакцию можно отменить, и все изменения, примененные к базе данных, будут отменены. Сегодня мы напишем небольшой скрипт, который при помощи транзакций SQLAlchemy пишет информацию о подписчиках сообщества в базу данных MySQL, а при возникновении ошибки отменяет текущую транзакцию.<\/p>\n<h2>Сбор информации об участниках через VK API<\/h2>\n<p>Для начала напишем пару маленьких функций — первая будет возвращать число подписчиков сообщества, а вторая — отправлять запрос и формировать датафрейм с информацией о подписчиках сообщества.<\/p>\n<p class=\"note\">Подробнее о том, как получить токен, можно прочитать в материале <a href=\"http:\/\/test.leftjoin.ru\/all\/get-data-from-vk\/\" class=\"nu\">«<u>Собираем данные по рекламным кампаниям ВКонтакте<\/u>»<\/a><\/p>\n<pre class=\"e2-text-code\"><code>from sqlalchemy import create_engine\r\nimport pandas as pd\r\nimport requests\r\nimport time\r\n\r\ntoken = '42hj2ehd3djdournf48fjurhf9r9o2eurnf48fjurhf9r9734'\r\ngroup_id = 'leftjoin'<\/code><\/pre><p>Чтобы узнать число подписчиков достаточно отправить метод groups.getMembers с любыми параметрами — в ответе всегда возвращается количество в поле count.<\/p>\n<pre class=\"e2-text-code\"><code>def get_subs_count(group_id):\r\n    count = requests.get('https:\/\/api.vk.com\/method\/groups.getMembers', params={\r\n        'access_token':token,\r\n        'v':5.103,\r\n        'group_id':group_id\r\n    }).json()['response']['count']\r\n    return count<\/code><\/pre><p>Для примера будем брать имена, id, фамилии подписчиков, некоторую расширенную информацию и получать только по 10 подписчиков за раз, чтобы рассмотреть работу транзакций детально — каждые 10 подписчиков будут вставляться одной транзакцией. Введём дополнительное поле offset, чтобы знать, в какой итерации добавлены строки.<\/p>\n<pre class=\"e2-text-code\"><code>def get_subs_info(group_id, offset):\r\n    response = requests.get('https:\/\/api.vk.com\/method\/groups.getMembers', params={\r\n        'access_token':token,\r\n        'v':5.103,\r\n        'group_id':group_id,\r\n        'offset':offset,\r\n        'count':10,\r\n        'fields':'sex, has_mobile, relation, can_post'\r\n    }).json()['response']['items']\r\n    df = pd.DataFrame(response)\r\n    df['offset'] = offset\r\n    return df<\/code><\/pre><h2>Транзакции<\/h2>\n<p>Наконец, можем подсоединиться к базе данных при помощи SQLAlchemy:<\/p>\n<pre class=\"e2-text-code\"><code>engine = create_engine('mysql+mysqlconnector:\/\/' +\r\n                           'root' + ':' + '' + '@' +\r\n                           'localhost' + '\/' +\r\n                           'transaction', echo=False)<\/code><\/pre><p>У транзакций всегда должно быть начало — begin, и конец — commit. В случае, если произошла какая-то ошибка, можно сделать откат — rollback. Сперва получаем число подписчиков сообщество, и в каждой итерации цикла при помощи контекстного менеджера with ... as создаём новое подключение. Сразу после объявляем начало транзакции по этому подключению и с обработчиком исключений пробуем получить информацию о десяти подписчиках через функцию get_subs_info. Вставляем полученный датафрейм в таблицу методом to_sql и завершаем транзакцию при помощи метода commit(). В случае, если возникла какая-то ошибка — печатаем её на экран и отменяем транзакцию.<\/p>\n<pre class=\"e2-text-code\"><code>offset = 0\r\nsubs_count = get_subs_count(group_id)\r\nwhile offset &lt; subs_count:\r\n    with engine.connect() as conn:\r\n        transaction = conn.begin()\r\n        try:\r\n            df = get_subs_info(group_id, offset)\r\n            df.to_sql('subscribers', con=conn, if_exists='append', index=False)\r\n            transaction.commit()\r\n        except Exception as E:\r\n            print(E)\r\n            transaction.rollback()\r\n    time.sleep(1)\r\n    offset += 10<\/code><\/pre><p>Чтобы протестировать работу транзакций слегка обновим последний блок кода — добавим вызов ошибки ValueError после вставки данных в базу, если текущий offset равен 10.<\/p>\n<pre class=\"e2-text-code\"><code>offset = 0\r\nsubs_count = get_subs_count(group_id)\r\nwhile offset &lt; subs_count:\r\n    with engine.connect() as conn:\r\n        transaction = conn.begin()\r\n        try:\r\n            df = get_subs_info(group_id, offset)\r\n            df.to_sql('subscribers', con=conn, if_exists='append', index=False)\r\n            if offset == 10:\r\n                raise(ValueError)\r\n            transaction.commit()\r\n        except Exception as E:\r\n            print(E)\r\n            transaction.rollback()\r\n    time.sleep(1)\r\n    offset += 10<\/code><\/pre><p>Как и планировалось, данные за итерацию с offset = 10 не занесены в таблицу. Несмотря на то, что ошибка возникла уже после добавления новых данных, транзакция была прервана методом rollback() и завершение транзакции было отменено.<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"http:\/\/test.leftjoin.ru\/pictures\/1-23.png\" width=\"759\" height=\"562\" alt=\"\" \/>\n<\/div>\n",
            "date_published": "2021-02-12T11:10:22+03:00",
            "date_modified": "2021-02-08T13:11:02+03:00",
            "image": "http:\/\/test.leftjoin.ru\/pictures\/1-23.png",
            "_date_published_rfc2822": "Fri, 12 Feb 2021 11:10:22 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "94",
            "_e2_data": {
                "is_favourite": false,
                "links_required": [
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css",
                    "system\/library\/highlight\/highlight.js",
                    "system\/library\/highlight\/highlight.css"
                ],
                "og_images": [
                    "http:\/\/test.leftjoin.ru\/pictures\/1-23.png"
                ]
            }
        }
    ],
    "_e2_version": 3365,
    "_e2_ua_string": "E2 (v3365; Aegea)"
}