-
-
Save C1eriC/1f171e0d4886a03615f99f88f7eddaf5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="ru" data-vue-meta="lang"> | |
<head data-vue-meta=""> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width,user-scalable=0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title data-vue-meta="true">Крадущийся тигр, затаившийся SQLAlchemy. Основы / Комментарии / Хабр</title> | |
<meta name="habr-version" content="2.5.20"> | |
<meta name="csrf-token" content="LtsFKmQo-4w5m7NrVWeUNvqHkbUmL2ni39Gw"> | |
<meta data-vue-meta="true" name="apple-mobile-web-app-status-bar-style" content="#77a2b6"><meta data-vue-meta="true" name="application-name" content="habr"><meta data-vue-meta="true" name="msapplication-TileColor" content="#77a2b6"><meta data-vue-meta="true" name="theme-color" content="#77a2b6"><meta data-vue-meta="true" name="google-play-app" content="app-id=ru.habrahabr"><meta data-vue-meta="true" property="al:android:app_name" content="Habrahabr"><meta data-vue-meta="true" property="al:android:package" content="ru.habrahabr"><meta data-vue-meta="true" property="fb:app_id" content="444736788986613"><meta data-vue-meta="true" property="fb:pages" content="472597926099084"><meta data-vue-meta="true" name="twitter:card" content="summary_large_image"><meta data-vue-meta="true" name="twitter:site" content="@habrahabr"><meta data-vue-meta="true" property="og:locale" content><meta data-vue-meta="true" property="og:type" content="article"><meta data-vue-meta="true" property="al:android:url" content="habrahabr:/undefined"><meta data-vue-meta="true" property="og:url" content="https://habr.com/ru/post/470285/"><meta data-vue-meta="true" itemprop="name" content="Крадущийся тигр, затаившийся SQLAlchemy. Основы"><meta data-vue-meta="true" property="og:title" content="Крадущийся тигр, затаившийся SQLAlchemy. Основы"><meta data-vue-meta="true" name="twitter:title" content="Крадущийся тигр, затаившийся SQLAlchemy. Основы"><meta data-vue-meta="true" name="description" content="Доброго дня. Сегодня хочу рассказать про ORM SQLAlchemy. Поговорим о том, что это, про его возможности и гибкость, а также рассмотрим случаи, которые не всегда..."><meta data-vue-meta="true" itemprop="description" content="Доброго дня. Сегодня хочу рассказать про ORM SQLAlchemy. Поговорим о том, что это, про его возможности и гибкость, а также рассмотрим случаи, которые не всегда..."><meta data-vue-meta="true" property="og:description" content="Доброго дня. Сегодня хочу рассказать про ORM SQLAlchemy. Поговорим о том, что это, про его возможности и гибкость, а также рассмотрим случаи, которые не всегда..."><meta data-vue-meta="true" name="twitter:description" content="Доброго дня. Сегодня хочу рассказать про ORM SQLAlchemy. Поговорим о том, что это, про его возможности и гибкость, а также рассмотрим случаи, которые не всегда..."><meta data-vue-meta="true" itemprop="image" content="/img/habr.png?v=1"><meta data-vue-meta="true" property="og:image" content="/img/habr.png?v=1"><meta data-vue-meta="true" name="twitter:image" content="/img/habr.png?v=1"> | |
<link href="https://fonts.googleapis.com/css?family=Fira+Sans:500&subset=cyrillic" rel="stylesheet"> | |
<link data-vue-meta="true" rel="shortcut icon" type="image/png" sizes="16x16" href="/img/favicons/favicon-16x16.png"><link data-vue-meta="true" rel="shortcut icon" type="image/png" sizes="32x32" href="/img/favicons/favicon-32x32.png"><link data-vue-meta="true" rel="apple-touch-icon" type="image/png" sizes="180x180" href="/img/favicons/apple-touch-icon.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/iphone5_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/iphone6_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)" href="/img/splashes/iphoneplus_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)" href="/img/splashes/iphonex_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/iphonexr_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3)" href="/img/splashes/iphonexsmax_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/ipad_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/ipadpro1_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/ipadpro3_splash.png"><link data-vue-meta="true" rel="apple-touch-startup-image" media="(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)" href="/img/splashes/ipadpro2_splash.png"><link data-vue-meta="true" rel="mask-icon" color="#77a2b6" href="/img/favicons/safari-pinned-tab.svg"><link data-vue-meta="true" crossorigin="use-credentials" href="/manifest.webmanifest" rel="manifest"><link data-vue-meta="true" href="https://habr.com/ru/post/470285/" rel="canonical" data-vmid="canonical"><link data-vue-meta="true" rel="image_src" href="/img/habr.png"> | |
<script data-vue-meta="true" src="/js/ads.js" onload="window['zhY4i4nJ9K'] = true" data-vmid="checkad"></script> | |
<script src="//www.googletagservices.com/tag/js/gpt.js" async></script> | |
<style> | |
.grecaptcha-badge { | |
visibility: hidden; | |
} | |
</style> | |
<link rel="preload" href="/css/vendors.6362ef2d.css" as="style"><link rel="preload" href="/js/vendors.983b12ad.js" as="script"><link rel="preload" href="/css/app.9aeeaa75.css" as="style"><link rel="preload" href="/js/app.a99c6101.js" as="script"><link rel="prefetch" href="/css/chunk-2bb78559.5d479b5b.css"><link rel="prefetch" href="/css/chunk-90d5e190.ddbbe8a5.css"><link rel="prefetch" href="/css/feedback.6f78b178.css"><link rel="prefetch" href="/css/gallery.83b62403.css"><link rel="prefetch" href="/css/search.1ed3a8d5.css"><link rel="prefetch" href="/css/topic.410b9918.css"><link rel="prefetch" href="/js/ads.js"><link rel="prefetch" href="/js/chunk-2bb78559.5c504855.js"><link rel="prefetch" href="/js/chunk-90d5e190.295e3cd9.js"><link rel="prefetch" href="/js/feedback.23554fc6.js"><link rel="prefetch" href="/js/gallery.d2d25c86.js"><link rel="prefetch" href="/js/hljs.f11899d4.js"><link rel="prefetch" href="/js/photoswipe.35c689c8.js"><link rel="prefetch" href="/js/pikaday.62da341d.js"><link rel="prefetch" href="/js/search.6e03fd00.js"><link rel="prefetch" href="/js/topic.1b149f75.js"><link rel="prefetch" href="/precache-manifest.302d70e8d93f5b20e5cf1b129ee6aaa2.js"><link rel="prefetch" href="/service-worker.js"><link rel="stylesheet" href="/css/vendors.6362ef2d.css"><link rel="stylesheet" href="/css/app.9aeeaa75.css"></head> | |
<body> | |
<div id="app" data-server-rendered="true"><div class="tm-layout__wrapper tm-fira-loaded"><div></div> <div></div> <header class="tm-header"><div class="tm-header__container"><span class="tm-svg-icon__wrapper tm-header__burger"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#header-burger"></use></svg></span> <span class="tm-header__logo-wrap"><a href="/ru/" class="tm-svg-icon__wrapper tm-header__logo"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#logo-habrcom"></use></svg></a> <span class="tm-header__beta-sign" style="display:none;">β</span></span> <div class="tm-header__user tm-header__user_logged-in"><div class="tm-entity-image"><img src="https://habrastorage.org/getpro/habr/avatars/d01/fe1/46a/d01fe146a28e3996dcf06dde41059a90.jpg" class="tm-entity-image__pic"></div></div></div></header> <!----> <!----> <div class="tm-layout"><div class="tm-page-progress-bar"></div> <div id="adbanner460648" class="tm-ad-banner"></div> <!----> <!----> <!----> <main class="tm-layout__container"><div class="tm-page tm-page_narrow"><!----> <div style="display:;"><div class="tm-article-hubs"><div class="tm-article-hubs__hub"><a href="/ru/hub/python/" class="tm-article-hubs__link">Python</a></div><div class="tm-article-hubs__hub"><a href="/ru/hub/postgresql/" class="tm-article-hubs__link">PostgreSQL</a></div><div class="tm-article-hubs__hub"><a href="/ru/hub/sql/" class="tm-article-hubs__link">SQL</a></div></div> <article class="tm-article tm-page-article-comments__content tm-article_preview"><!----> <div class="tm-user-meta"><a href="/ru/users/NapoleonIT/posts/" class="tm-user-info"><div class="tm-user-info__userpic"><div class="tm-entity-image"><img src="https://habrastorage.org/r/w32/getpro/habr/avatars/5a2/462/845/5a2462845b5b5599bb8a75992e25eaa6.png" class="tm-entity-image__pic"></div></div> <span class="tm-user-info__username">NapoleonIT</span></a> <span title="2019-10-06, 10:39" class="tm-user-meta__date"><span>вчера в 10:39</span></span></div> <!----> <h2 class="tm-article-title tm-article-title_preview tm-article-title_preview"><a href="/ru/post/470285/" class="tm-article-title__link router-link-active">Крадущийся тигр, затаившийся SQLAlchemy. Основы</a></h2> <!----> <div></div> <!----> <div class="tm-article__counters"><div class="tm-article__sticked-counters-wrapper" style="display:none;"><div class="tm-data-icons tm-article__sticked-counters"><div class="tm-article-rating tm-article-icons__item"><div class="tm-votes-lever tm-article-rating__votes-switcher tm-votes-lever_medium"><span class="tm-svg-icon__wrapper tm-votes-icon tm-votes-lever__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></span> <span class="tm-votes-score tm-votes-lever__score tm-votes-score_positive">+5 </span> <span class="tm-svg-icon__wrapper tm-votes-icon tm-votes-lever__icon tm-votes-lever__icon_down"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></span></div></div> <!----> <!----> <span class="tm-icon-text tm-icon-text-button tm-data-icons__item"><span class="tm-icon-text__icon"><svg height="16" width="16" class="tm-svg-img tm-data-icons__icon tm-data-icons__icon_bookmarks"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></span> <span class="tm-icon-text__text"><span class="tm-data-icons__counter tm-data-icons__counter_bookmarks">58</span></span></span> <!----></div></div> <div class="tm-data-icons"><!----> <div class="tm-votes-meter tm-votes-meter_small"><span class="tm-svg-icon__wrapper tm-votes-icon tm-votes-meter__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#counter-rating"></use></svg></span> <span class="tm-votes-score tm-votes-meter__score tm-votes-score_positive">+5 </span></div> <span class="tm-icon-text tm-data-icons__item"><span class="tm-icon-text__icon"><span class="tm-data-icons__icon tm-data-icons__icon_views"></span><svg height="16" width="16" class="tm-svg-img tm-data-icons__icon tm-data-icons__icon_views"><use xlink:href="/img/megazord-v7.svg#counter-views"></use></svg></span> <span class="tm-icon-text__text"><span class="tm-data-icons__counter tm-data-icons__counter_views">2,7k </span></span></span> <span class="tm-icon-text tm-icon-text-button tm-data-icons__item"><span class="tm-icon-text__icon"><svg height="16" width="16" class="tm-svg-img tm-data-icons__icon tm-data-icons__icon_bookmarks"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></span> <span class="tm-icon-text__text"><span class="tm-data-icons__counter tm-data-icons__counter_bookmarks">58</span></span></span> <!----></div></div></article> <div id="adbanner460649" class="tm-ad-banner tm-page-article-comments__top-banner"></div> <div class="tm-page-article-comments__wrapper"><div class="tm-page-article-comments__title"> | |
Комментарии | |
<span class="tm-page-article-comments__comments-count"> | |
5 | |
</span></div> <div class="tm-page-article-comments__inner"><section><!----> <div class="tm-comments-list__comment-wrapper"><div class="tm-comments-list__comment"><section><div class="tm-comment__collapsed" style="padding-left:0px;display:none;"><div class="tm-comment__circle-block"><div class="tm-comment__circle"></div></div> <div class="tm-comment__collapsed-text"><span>Раскрыть ветку (0)</span></div> <div class="tm-comment__collapsed-line"></div></div> <article class="tm-comment" style="opacity:1;padding-left:0px;display:;"><a name="comment_20718557"></a> <div></div> <header class="tm-comment__header"><div class="tm-comment-score tm-comment-head__score"> | |
0 | |
</div> <div class="tm-comment-head__inner"><a href="/ru/users/Andy_U/" class="tm-user-info tm-comment-head__user"><div class="tm-user-info__userpic"><div class="tm-entity-image"><svg height="16" width="16" color="blue" type="user" class="tm-svg-img tm-image-placeholder tm-image-placeholder_blue"><use xlink:href="/img/megazord-v7.svg#placeholder-user"></use></svg></div></div> <span class="tm-user-info__username">Andy_U</span></a> <time class="tm-comment-datetime tm-comment-head__datetime"><a href="#comment_20718557" class="tm-comment-datetime__link"><span>06.10.2019 в 13:39</span></a></time></div></header> <section><div></div> <div></div> <div class="tm-comment-body__content"><div xmlns="http://www.w3.org/1999/xhtml">Один я не знал, что так можно?<br/> | |
<br/> | |
<pre><code class="python"> def __repr__(self): | |
return "<{0.__class__.__name__}(id={0.id!r})>".format(self)</code></pre></div></div></section> <footer class="tm-user-comments-footer tm-comment__footer"><!----> <div class="tm-comment__footer-item tm-comment-button"><span>Ответить</span></div> <div class="tm-comment__footer-item"><div class="tm-comment-button tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg> <svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon tm-comment-button-bookmarks_down"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></div> <!----></div> <button class="tm-comment-button tm-comment__footer-item tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></button> <!----> <!----></footer></article></section></div> <!----> <!----> <!----></div><div class="tm-comments-list__comment-wrapper"><div class="tm-comments-list__comment"><section><div class="tm-comment__collapsed" style="padding-left:20px;display:none;"><div class="tm-comment__circle-block"><div class="tm-comment__circle"></div></div> <div class="tm-comment__collapsed-text"><span>Раскрыть ветку (0)</span></div> <div class="tm-comment__collapsed-line"></div></div> <article class="tm-comment" style="opacity:1;padding-left:20px;display:;"><a name="comment_20718791"></a> <div class="tm-comment__breadcrumbs" style="width:20px;"><div class="tm-comment__circle"></div></div> <header class="tm-comment__header"><div class="tm-comment-score tm-comment-head__score"> | |
0 | |
</div> <div class="tm-comment-head__inner"><a href="/ru/users/onegreyonewhite/" class="tm-user-info tm-comment-head__user"><div class="tm-user-info__userpic"><div class="tm-entity-image"><img src="//habrastorage.org/r/w32/getpro/habr/avatars/fa9/942/f93/fa9942f936a90d8c8e53832682f0290e.png" class="tm-entity-image__pic"></div></div> <span class="tm-user-info__username">onegreyonewhite</span></a> <time class="tm-comment-datetime tm-comment-head__datetime"><a href="#comment_20718791" class="tm-comment-datetime__link"><span>06.10.2019 в 15:17</span></a></time></div></header> <section><div></div> <div></div> <div class="tm-comment-body__content"><div xmlns="http://www.w3.org/1999/xhtml"><p>Судя по всему да.<br/> | |
Вообще можно даже значение словаря получить по ключу (пишите так же квадратные скобки, но значение ключа без кавычек) или значение массива по индексу.</p></div></div></section> <footer class="tm-user-comments-footer tm-comment__footer"><!----> <div class="tm-comment__footer-item tm-comment-button"><span>Ответить</span></div> <div class="tm-comment__footer-item"><div class="tm-comment-button tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg> <svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon tm-comment-button-bookmarks_down"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></div> <!----></div> <button class="tm-comment-button tm-comment__footer-item tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></button> <!----> <!----></footer></article></section></div> <!----> <!----> <!----></div><div class="tm-comments-list__comment-wrapper"><div class="tm-comments-list__comment"><section><div class="tm-comment__collapsed" style="padding-left:40px;display:none;"><div class="tm-comment__circle-block"><div class="tm-comment__circle"></div></div> <div class="tm-comment__collapsed-text"><span>Раскрыть ветку (0)</span></div> <div class="tm-comment__collapsed-line"></div></div> <article class="tm-comment" style="opacity:1;padding-left:40px;display:;"><a name="comment_20719147"></a> <div class="tm-comment__breadcrumbs" style="width:40px;"><div class="tm-comment__circle"></div></div> <header class="tm-comment__header"><div class="tm-comment-score tm-comment-head__score"> | |
0 | |
</div> <div class="tm-comment-head__inner"><a href="/ru/users/Andy_U/" class="tm-user-info tm-comment-head__user"><div class="tm-user-info__userpic"><div class="tm-entity-image"><svg height="16" width="16" color="blue" type="user" class="tm-svg-img tm-image-placeholder tm-image-placeholder_blue"><use xlink:href="/img/megazord-v7.svg#placeholder-user"></use></svg></div></div> <span class="tm-user-info__username">Andy_U</span></a> <time class="tm-comment-datetime tm-comment-head__datetime"><a href="#comment_20719147" class="tm-comment-datetime__link"><span>06.10.2019 в 18:21</span></a></time></div></header> <section><div></div> <div></div> <div class="tm-comment-body__content"><div xmlns="http://www.w3.org/1999/xhtml">Я, честно говоря, не очень внимательно документацию читал, но не помню там описания такого трюка. Ну и вообще, это какая-то дверь в ад, типа eval и exec:<br/> | |
<br/> | |
<pre><code class="python">class Tst: | |
def __init__(self): | |
self._a = 1 | |
@property | |
def a(self): | |
print('hahaha') | |
return self._a | |
if __name__ == '__main__': | |
tst = Tst() | |
print('{0.a}'.format(tst)) | |
</code></pre></div></div></section> <footer class="tm-user-comments-footer tm-comment__footer"><!----> <div class="tm-comment__footer-item tm-comment-button"><span>Ответить</span></div> <div class="tm-comment__footer-item"><div class="tm-comment-button tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg> <svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon tm-comment-button-bookmarks_down"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></div> <!----></div> <button class="tm-comment-button tm-comment__footer-item tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></button> <!----> <!----></footer></article></section></div> <!----> <!----> <!----></div><div class="tm-comments-list__comment-wrapper"><div class="tm-comments-list__comment"><section><div class="tm-comment__collapsed" style="padding-left:60px;display:none;"><div class="tm-comment__circle-block"><div class="tm-comment__circle"></div></div> <div class="tm-comment__collapsed-text"><span>Раскрыть ветку (0)</span></div> <div class="tm-comment__collapsed-line"></div></div> <article class="tm-comment" style="opacity:1;padding-left:60px;display:;"><a name="comment_20720189"></a> <div class="tm-comment__breadcrumbs" style="width:60px;"><div class="tm-comment__circle"></div></div> <header class="tm-comment__header"><div class="tm-comment-score tm-comment-head__score"> | |
0 | |
</div> <div class="tm-comment-head__inner"><a href="/ru/users/onegreyonewhite/" class="tm-user-info tm-comment-head__user"><div class="tm-user-info__userpic"><div class="tm-entity-image"><img src="//habrastorage.org/r/w32/getpro/habr/avatars/fa9/942/f93/fa9942f936a90d8c8e53832682f0290e.png" class="tm-entity-image__pic"></div></div> <span class="tm-user-info__username">onegreyonewhite</span></a> <time class="tm-comment-datetime tm-comment-head__datetime"><a href="#comment_20720189" class="tm-comment-datetime__link"><span>07.10.2019 в 03:21</span></a></time></div></header> <section><div></div> <div></div> <div class="tm-comment-body__content"><div xmlns="http://www.w3.org/1999/xhtml"><p>Как по мне, дверью в ад являются f-strings (Python>=3.6), которые упомянули ниже.<br/> | |
Там напрямую функции легко можно вызвать.<br/> | |
Поэтому форматирование нужно использовать обдуманно и не форматировать строки извне, а только те, которым вы доверяете (т.е. находящимся внутри кода).</p></div></div></section> <footer class="tm-user-comments-footer tm-comment__footer"><!----> <div class="tm-comment__footer-item tm-comment-button"><span>Ответить</span></div> <div class="tm-comment__footer-item"><div class="tm-comment-button tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg> <svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon tm-comment-button-bookmarks_down"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></div> <!----></div> <button class="tm-comment-button tm-comment__footer-item tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></button> <!----> <!----></footer></article></section></div> <!----> <!----> <!----></div><div class="tm-comments-list__comment-wrapper"><div class="tm-comments-list__comment"><section><div class="tm-comment__collapsed" style="padding-left:20px;display:none;"><div class="tm-comment__circle-block"><div class="tm-comment__circle"></div></div> <div class="tm-comment__collapsed-text"><span>Раскрыть ветку (0)</span></div> <div class="tm-comment__collapsed-line"></div></div> <article class="tm-comment" style="opacity:1;padding-left:20px;display:;"><a name="comment_20719013"></a> <div class="tm-comment__breadcrumbs" style="width:20px;"><div class="tm-comment__circle"></div></div> <header class="tm-comment__header"><div class="tm-comment-score tm-comment-head__score tm-comment-score_positive"> | |
+1 | |
</div> <div class="tm-comment-head__inner"><a href="/ru/users/Tihon_V/" class="tm-user-info tm-comment-head__user"><div class="tm-user-info__userpic"><div class="tm-entity-image"><img src="//habrastorage.org/r/w32/getpro/habr/avatars/246/b53/a61/246b53a61e696f4bd9b14e73095f2e6d.png" class="tm-entity-image__pic"></div></div> <span class="tm-user-info__username">Tihon_V</span></a> <time class="tm-comment-datetime tm-comment-head__datetime"><a href="#comment_20719013" class="tm-comment-datetime__link"><span>06.10.2019 в 17:02</span></a></time></div></header> <section><div></div> <div></div> <div class="tm-comment-body__content"><div xmlns="http://www.w3.org/1999/xhtml">Как по мне — f-strings тут к месту<br/> | |
<pre><code class="python">def __repr__(self): | |
return f"<{type(self).__name__}(id={self.id})>" | |
</code></pre></div></div></section> <footer class="tm-user-comments-footer tm-comment__footer"><!----> <div class="tm-comment__footer-item tm-comment-button"><span>Ответить</span></div> <div class="tm-comment__footer-item"><div class="tm-comment-button tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg> <svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon tm-comment-button-bookmarks_down"><use xlink:href="/img/megazord-v7.svg#counter-vote"></use></svg></div> <!----></div> <button class="tm-comment-button tm-comment__footer-item tm-comment_transparent"><svg height="16" width="16" class="tm-svg-img tm-comment-button-bookmarks__icon"><use xlink:href="/img/megazord-v7.svg#counter-bookmarks"></use></svg></button> <!----> <!----></footer></article></section></div> <!----> <!----> <!----></div></section> <div class="tm-comment-navigation tm-comment-navigation__block tm-comment-navigation__has-new_reverse"><span class="tm-svg-icon__wrapper tm-comment-navigation__refresh"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#refresher-comments"></use></svg></span> <div class="tm-comment-navigation__wrapper" style="display:none;"><span class="tm-svg-icon__wrapper tm-comment-navigation__button tm-comment-navigation__button_up"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#refresher-up"></use></svg></span> <div class="tm-comment-navigation__counter"> | |
0 | |
</div> <span class="tm-svg-icon__wrapper tm-comment-navigation__button tm-comment-navigation__button_down"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#refresher-up"></use></svg></span></div></div></div></div> <div class="tm-article-comments-form"><!----> <form class="tm-comment-form"><div class="tm-comment-form__title"><span>Написать комментарий</span></div> <!----> <textarea cols="30" rows="5" class="tm-comment-form__input"></textarea> <div class="tm-comment-form__controls"><button disabled="disabled" class="tm-comment-form-controls__button tm-comment-form-controls__button_preview"><span>Предпросмотр</span></button> <button disabled="disabled" class="tm-comment-form-controls__button tm-comment-form-controls__button_send"><span>Отправить</span></button></div></form></div></div></div></main> <!----></div> <div class="tm-footer"><div class="tm-footer__container"><div class="tm-footer__title"><a href="/" class="tm-svg-icon__wrapper tm-footer__title-link"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#logo-habrcom"></use></svg></a></div> <div class="tm-footer__social"><a href="https://www.facebook.com/habrahabr.ru" target="_blank" class="tm-svg-icon__wrapper tm-social-icons__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#social-logo-facebook"></use></svg></a><a href="https://twitter.com/habr_com" target="_blank" class="tm-svg-icon__wrapper tm-social-icons__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#social-logo-twitter"></use></svg></a><a href="https://vk.com/habr" target="_blank" class="tm-svg-icon__wrapper tm-social-icons__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#social-logo-vkontakte"></use></svg></a><a href="https://telegram.me/habr_com" target="_blank" class="tm-svg-icon__wrapper tm-social-icons__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#social-logo-telegram"></use></svg></a><a href="https://www.youtube.com/channel/UCd_sTwKqVrweTt4oAKY5y4w" target="_blank" class="tm-svg-icon__wrapper tm-social-icons__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#social-logo-youtube"></use></svg></a><a href="https://zen.yandex.ru/habr" target="_blank" class="tm-svg-icon__wrapper tm-social-icons__icon"><svg height="16" width="16" class="tm-svg-img tm-svg-icon"><use xlink:href="/img/megazord-v7.svg#social-logo-zen"></use></svg></a></div> <div class="tm-footer__link"><svg height="16" width="16" class="tm-svg-img tm-footer__icon"><use xlink:href="/img/megazord-v7.svg#lang"></use></svg> <span>Настройка языка</span></div> <a href="https://habr.com/ru/post/470285/comments/?mobile=no" class="tm-footer__link"><span>Полная версия</span></a> <div></div> <div class="tm-footer-copyright"><span class="tm-copyright"><span class="tm-copyright__years">© 2006–2019 </span> <span class="tm-copyright__name">«<a href="https://tmtm.ru" target="_blank" class="tm-copyright__link">TM</a>»</span></span></div></div></div></div></div><script>window.__INITIAL_STATE__={"adblock":{"hasAcceptableAdsFilter":false,"hasAdblock":false},"articlesList":{"articlesList":{"470285":{"author":{"alias":"NapoleonIT","id":"1937959","image":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F5a2\u002F462\u002F845\u002F5a2462845b5b5599bb8a75992e25eaa6.png","isSubscribed":false,"route":{"name":"articles_list_by_user","params":{"login":"NapoleonIT","hl":"ru"}},"shortInfo":"IT-компания","title":"Napoleon IT","karmaScore":7,"type":{"isReadAndComment":false,"isReadOnly":false},"username":"NapoleonIT","counters":{"posts":"3","comments":"0","followed":"0","followers":"2","favorites":"1"},"fl":null,"hl":null,"badges":[{"id":"1","title":"Захабренный","alias":"habred","description":"Пользователь с кармой \u003E0","url":null,"is_disabled":false,"is_removable":false}],"contactsLinks":[],"followersCount":2,"ratingScore":4,"registrationDate":"2019-01-17T07:43:32.000Z","invitationDate":"","gender":"0","votes":{"isEnabled":true,"voteState":0},"fullname":"Napoleon IT","imageSrc":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F5a2\u002F462\u002F845\u002F5a2462845b5b5599bb8a75992e25eaa6.png","paymentsMethods":[],"login":"NapoleonIT"},"companyAlias":0,"canComment":true,"commentsCount":5,"commentsRoute":{"name":"article_comments","params":{"id":"470285","companyName":""}},"commentsUnreadCount":0,"contentHTML":"<div xmlns="http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml"><p><img src="\u002Fimg\u002Fpx.gif" data-src="https:\u002F\u002Fhabrastorage.org\u002Fwebt\u002F3o\u002Fji\u002Fei\u002F3ojiei46dcl8pihke7rjjojlgn4.jpeg"\u002F><\u002Fp><br\u002F>\r\n<p>Доброго дня.<\u002Fp><br\u002F>\r\n<p>Сегодня хочу рассказать про ORM SQLAlchemy. Поговорим о том, что это, про его возможности и гибкость, а также рассмотрим случаи, которые не всегда понятно описаны.<\u002Fp><br\u002F>\r\n<p>Данная ORM имеет порог вхождения выше среднего, поэтому я попытаюсь объяснить всё простым языком и с примерами. Статья будет полезна тем, кто уже работает с sqlalchemy и хочет прокачать свои навыки или только знакомится с этой библиотекой.<\u002Fp><a name="habracut"><\u002Fa><br\u002F>\r\n<p>Используемый язык программирования — python 3.6.<br\u002F>\r\nБД — PostgreSQL.<br\u002F>\r\nСсылка на <a href="https:\u002F\u002Fgithub.com\u002Fsandix90\u002Fsqlalchemy_basics">github<\u002Fa><\u002Fp><br\u002F>\r\n<p>Итак, что такое ORM? <\u002Fp><br\u002F>\r\n<p>ORM (Object-Relational Mapping) — это технология, которая позволяет сопоставлять модели, типы которых несовместимы. Например: таблица базы данных и объект языка программирования. <\u002Fp><br\u002F>\r\n<p>Иными словами, можно обращаться к объектам классов для управления данными в таблицах БД. Также можно создавать, изменять, удалять, фильтровать и, самое главное, наследовать объекты классов, сопоставленные с таблицами БД, что существенно сокращает наполнение кодовой базы. <\u002Fp><br\u002F>\r\n<p>Чтобы использовать возможности SQLAlchemy, необходимо понять принцип его работы.<\u002Fp><br\u002F>\r\n<p>Разработчикам, которые используют Django-ORM, придется немного перестроить образ мышления для создания ORM запросов. На мой взгляд, SQLAlchemy — функциональный монстр, возможностями которого можно и нужно пользоваться, но нужно понимать, что ORM не всегда идеальны. Поэтому обсудим моменты, когда использование этой технологии целесообразно.<\u002Fp><br\u002F>\r\n<p>В SQLAlchemy есть понятие декларативных и недекларативных определений моделей. <\u002Fp><br\u002F>\r\n<p>Недекларативные определения подразумевают использования mapper(), описывающего сопоставление каждой колонки БД и классом модели. <\u002Fp><br\u002F>\r\n<p>В данной статье используется декларативное определение моделей. <\u002Fp><br\u002F>\r\n<p>Подробнее <a href="https:\u002F\u002Fdocs.sqlalchemy.org\u002Fen\u002F13\u002Form\u002Fextensions\u002Fdeclarative\u002Fapi.html#sqlalchemy.ext.declarative.declarative_base">здесь<\u002Fa><\u002Fp><br\u002F>\r\n<h3 id="struktura-bd">Структура БД<\u002Fh3><br\u002F>\r\n<p>Для полной консистентности данных давайте создадим следующие таблицы.<\u002Fp><br\u002F>\r\n<p>Базовая модель служит для определения базовых колонок в БД.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class BaseModel(Base):\n __abstract__ = True\n\n id = Column(Integer, nullable=False, unique=True, primary_key=True, autoincrement=True)\n created_at = Column(TIMESTAMP, nullable=False)\n updated_at = Column(TIMESTAMP, nullable=False)\n\n def __repr__(self):\n return "&lt;{0.__class__.__name__}(id={0.id!r})>".format(self)<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Employee — таблица, описывающая работника, который работает в офисе<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class Employee(BaseModel):\n __tablename__ = 'employees'\n\n first_name = Column(VARCHAR(255), nullable=False)\n last_name = Column(VARCHAR(255), nullable=False)\n phone = Column(VARCHAR(255), unique=True, nullable=True)\n description = Column(VARCHAR(255), nullable=True)<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>EmployeeWithSkills — не таблица. Класс наследуемый от Employee. Отличная возможность разделить логику и использовать класс, будто это отдельная таблица.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class EmployeeWithSkills(Employee):\n skills = relation(Skill, secondary=EmployeesSkills.__tablename__, lazy='joined')<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Department — отдел, в котором работает этот сотрудник. Человек может состоять в нескольких отделах.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class Department(BaseModel):\n __tablename__ = 'departments'\n\n name = Column(VARCHAR(255), nullable=False)\n description = Column(VARCHAR(255), nullable=False)<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Таблица соответствий работника и подразделений, в которых он состоит.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class EmployeeDepartments(BaseModel):\n __tablename__ = 'employee_departments'\n\n employee_id = Column(Integer, ForeignKey('employees.id', ondelete='CASCADE'), nullable=False, index=True)\n department_id = Column(Integer, ForeignKey('departments.id', ondelete='CASCADE'), nullable=False, index=True)<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Таблица соответствий сотрудников и их умений.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class EmployeesSkills(BaseModel):\n__tablename__ = 'employees_skills'\n\nemployee_id = Column(ForeignKey('employee.id', ondelete='CASCADE'), nullable=False, index=True)\nskill_id = Column(ForeignKey('skills.id', ondelete='CASCADE'), nullable=False, index=True)<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Создаем миграции с помощью пакета alembic, позволяющего генерировать их автоматически. В рамках данного урока автогенерация миграций вполне допустима.<\u002Fp><br\u002F>\r\n<p>В последней миграции присутствуют тестовые данные, которые наполнят базу.<br\u002F>\r\nКак настроить alembic можно почитать <a href="https:\u002F\u002Falembic.sqlalchemy.org\u002Fen\u002Flatest\u002F">здесь<\u002Fa><br\u002F>\r\nВыполняем заветные alembic upgrade head, чтобы выполнить миграцию.<\u002Fp><br\u002F>\r\n<h3 id="zaprosy-i-relations">Запросы и relations<\u002Fh3><br\u002F>\r\n<p>Давайте сделаем первый запрос и получим информацию о сотруднике по его id.<br\u002F>\r\nЗапрос будет выглядеть так:<\u002Fp><br\u002F>\r\n<p>lesson1:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">employee = session.query(Employee).filter(Employee.id == eid).one()\n\noutput:\n ID: 2, Tony Stark<\u002Fcode><\u002Fpre><br\u002F>\r\n<p><code>.one()<\u002Fcode> в конце обозначает, что мы намерены получить только одну запись. Если записей будет несколько, возникнет соответствующее исключение.<\u002Fp><br\u002F>\r\n<p>Если мы захотим получить все имеющиеся отделы, то можно воспользоваться следующим запросом c использованием <code>.all()<\u002Fcode><\u002Fp><br\u002F>\r\n<p>lesson2:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">emmployee = session.query(Department).all()\n\noutput:\n ID: 2, name: Guards\n ID: 4, name: Legions<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Рассмотрим работу с функциями агрегации. <\u002Fp><br\u002F>\r\n<p>Мы можем получить количество имеющихся департаментов с помощью встроенной функции<br\u002F>\r\n<code>.count()<\u002Fcode> или использовать <code>func.count()<\u002Fcode>. С помощью второго метода можно обращаться к любым функциям SQL, используя для <code>select<\u002Fcode> или для вычисления промежуточных результатов. <\u002Fp><br\u002F>\r\n<p>lesson3:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">def get_departments_count(session: DBSession) -> int:\n count = session.query(Department).count()\n\n return count\n\ndef get_departments_func_count(session: DBSession) -> int:\n count = session.query(func.count(Department.id)).scalar()\n\n return count<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Многие разработчики используют функцию <code>count()<\u002Fcode> для проверки наличия данных в запросе. Это не очень хорошая практика, порождающая использование дополнительных ресурсов БД и увеличение времени выполнения запроса. Хорошим решением будет использование функции <code>exists()<\u002Fcode>, возвращающей скалярное значение:<br\u002F>\r\nlesson3:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">def check_department_exists(session: DBSession, department_name: str) -> bool:\n\n is_exists = session.query(exists().where(Department.name == department_name)).scalar()\n\n return is_exists<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Двигаясь дальше, усложним задачу и познакомимся с сущностью <code>relation<\u002Fcode> или <code>relationship<\u002Fcode>. Дело в том, что в <code>SQLAlchemy<\u002Fcode> кроме использования foreign_key<br\u002F>\r\nна уровне базы данных, используются еще и отношения между объектами. <\u002Fp><br\u002F>\r\n<p>Таким образом мы можем получить зависимую по foreign key строку БД в объекте.<br\u002F>\r\nЭти объекты являются проекцией на таблицы БД, связанные между собой.<\u002Fp><br\u002F>\r\n<p><code>Relations<\u002Fcode> в <code>SQLAlchemy<\u002Fcode> имеют гибкую настройку, позволяя получать данные из БД разными способами в разное время с помощью именованного аргумента <code>lazy<\u002Fcode>.<\u002Fp><br\u002F>\r\n<p>Основные степени "ленивости":<\u002Fp><br\u002F>\r\n<ul>\r\n<li><code>select<\u002Fcode> — по умолчанию. ORM делает запрос только тогда, когда обращаются к данным. Осуществляется отдельным запросом.<\u002Fli>\r\n<li><code>dynamic<\u002Fcode> — позволяет получить объект запроса, который можно модифицировать по желанию. Получает данные из БД только после вызова all() или one() или любых других доступных методов.<\u002Fli>\r\n<li><code>joined<\u002Fcode> — в основной запрос добавляется с помощью LEFT JOIN. Выполняется сразу.<\u002Fli>\r\n<li><code>subquery<\u002Fcode> — похож на select, но выполняется как подзапрос.<\u002Fli>\r\n<\u002Ful><br\u002F>\r\n<p>По умолчанию — <code>select<\u002Fcode>.<\u002Fp><br\u002F>\r\n<p>Фильтрация в запросах может быть статической и динамической. Динамическая фильтрация позволяет наполнить запрос фильтрами, которые могут изменяться в зависимости от хода выполнения функции.<\u002Fp><br\u002F>\r\n<p>lesson4:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">def dynamic_filter(session: DBSession, filter: DFilter = None):\n\n query = session.query(Employee)\n\n if filter is not None:\n query = query.filter(*filter.conds)\n\n employees = query.all()\n\n return employees<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>В классе фильтра DFilter указаны фильтры на основе каких-либо входных данных. Если класс фильтра определён, но далее в запросе применяются условия.<\u002Fp><br\u002F>\r\n<p>Функция .filter() принимает принимает бинарные условия SQLAlchemy, поэтому может быть представлена с помощью * <\u002Fp><br\u002F>\r\n<p>Применение динамических фильтров ограничивается только фантазией. Результат выполнения запроса показывает, какие герои сейчас неактивны.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">output:\n Inactive_heros:\n Name: Tony Stark\n Name: Scott Lang\n Name: Peter Parker <\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Предлагаю поработать с отношением many-to-many.<\u002Fp><br\u002F>\r\n<p>Мы имеем таблицу Employee, в которой присутствует relation к таблице соответствий EmployeesSkills. Она содержит foreign_key на таблицу сотрудников и foreign_key<br\u002F>\r\nна таблицу умений.<\u002Fp><br\u002F>\r\n<p>lesson 5:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">def get_employee_with_skills(session: DBSession, eid: int):\n employee = session.query(EmployeeWithSkills).filter(EmployeeWithSkills.id == eid).one()\n\n return employee\n\noutput:\n Employee Tony Stark has skills:\n Skill: Fly, Desc: I belive I can Fly. I belive I can touch the sky\n Skill: Light Shield, Desc: Light protect. Perfect for everything<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Используя класс EmployeeWithSkills в запросе выше, мы обращаемся к нему, как к таблице БД, но на самом деле такой таблицы не существует. Это класс отличается от Employee наличие relation, которое имеет отношение many-to-many. Так мы можем разграничивать логику работы классов, наполняя разным набором relations. В результате запроса мы увидим умения одного из сотрудников.<\u002Fp><br\u002F>\r\n<p>Так как сотрудник может состоять в нескольких подразделениях, создадим relation, позволяющий получить эту информацию.<\u002Fp><br\u002F>\r\n<p>Создадим класс EmployeeWithDepartments, наследуемый от Employee и добавим следующее:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">class EmployeeWithDepartments(Employee):\n departments = relation(\n Department,\n # primaryjoin=EmployeeDepartments.employee_id == Employee.id,\n secondary=EmployeeDepartments.__tablename__,\n # secondaryjoin=EmployeeDepartments.department_id == Department.id,\n )<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Созданный класс не является новой таблицей БД. Это все та же таблица Employee, только расширенная c помощью <code>relation<\u002Fcode>. Таким образом, вы можете обращаться к таблице <code>Employee<\u002Fcode> или <code>EmployeeWithDepartments<\u002Fcode> в запросах. Разница будет лишь в отсутствии\u002Fналичии <code>relation<\u002Fcode>.<\u002Fp><br\u002F>\r\n<p>Первый аргумент указывает к какой таблице мы будем создавать <code>relation<\u002Fcode>.<br\u002F>\r\n<code>primaryjoin<\u002Fcode> — это условие, по которому будет подключаться вторая таблица до её присоединения к объекту.<br\u002F>\r\n<code>secondary<\u002Fcode> — имя таблицы, содержащее foreign_keys для сопоставления. Используется в случае many-to-many.<br\u002F>\r\n<code>secondaryjoin<\u002Fcode> — условия сопоставления промежуточной таблицы с последней.<\u002Fp><br\u002F>\r\n<p><code>primaryjoin<\u002Fcode> и <code>secondaryjoin<\u002Fcode> служат для явного указания соответствий в сложных ситуациях.<\u002Fp><br\u002F>\r\n<p>Порой возникают ситуации, когда необходимо создавать фильтры, поля которых объявлены в отношениях, а отношения в свою очередь являются отношениями исходного класса.<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">EmployeeWithCadreMovements -> relation(CadreMovement) -> field<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>Если отношение отображает список значений, то нужно использовать .any(), если значение предусмотрено только одно, то необходимо использовать .has()<\u002Fp><br\u002F>\r\n<p>Для лучшего понимания, данная конструкция будет интерпретирована на SQL языка в конструкцию exists().<\u002Fp><br\u002F>\r\n<p>Вызовем функцию получения с указанием параметра причины <code>reason<\u002Fcode>, например, <code>simple<\u002Fcode>.<\u002Fp><br\u002F>\r\n<p>lesson6<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">def has_in_relations(session: DBSession, reason: str):\n employees = session.query(EmployeeWithCadreMovements).filter(EmployeeWithCadreMovements.cadre_movements.any(CadreMovement.reason == reason)).all()\n return employees\n\noutput:\n [Steve Rogers, Tony Stark]<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>lession7<\u002Fp><br\u002F>\r\n<p>Рассмотрим возможность получения relation с помощью функции агрегации. Например, получим последнее кадровое движение определенного пользователя.<br\u002F>\r\nprimaryjoin является условием присоединения таблиц (в случае использования lazy='joined'). Напомним, что по умолчанию используется select.<br\u002F>\r\nВ этом случае, формируется отдельный запрос при обращении к атрибуту класса. Именно для этого запроса мы и можем указать условия фильтрации.<br\u002F>\r\nКак известно, нельзя использовать функции агрегации в "чистом" виде в WHERE условии, поэтому мы можем реализовать данную возможность, указав relation<br\u002F>\r\nсо следующими параметрами:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">last_cadre_movement = relation(\n CadreMovement,\n primaryjoin=and_(\n CadreMovement.employee == Employee.id,\n uselist=False,\n CadreMovement.id == select([func.max(CadreMovement.id)]).where(CadreMovement.employee == Employee.id)\n )\n)<\u002Fcode><\u002Fpre><br\u002F>\r\n<p>При выполнении запрос скомпилируется так:<\u002Fp><br\u002F>\r\n<pre><code class="plaintext">SELECT \n cadre_movements.id AS cadre_movements_id, \n cadre_movements.created_at AS cadre_movements_created_at, \n cadre_movements.updated_at AS cadre_movements_updated_at, \n cadre_movements.employee AS cadre_movements_employee, \n cadre_movements.old_department AS cadre_movements_old_department, \n cadre_movements.new_department AS cadre_movements_new_department, \n cadre_movements.reason AS cadre_movements_reason \nFROM cadre_movements \nWHERE cadre_movements.employee = %(param_1)s \n AND cadre_movements.id = (\n SELECT max(cadre_movements.id) AS max_1 \n FROM cadre_movements \n WHERE cadre_movements.employee = %(param_1)s\n )<\u002Fcode><\u002Fpre><br\u002F>\r\n<p><a href="https:\u002F\u002Fgithub.com\u002Fsandix90\u002Fsqlalchemy_basics">Ссылка на github<\u002Fa><\u002Fp><br\u002F>\r\n<h3 id="itog">Итог<\u002Fh3><br\u002F>\r\n<p>SQLAlchemy является мощнейшим инструментом в построении запросов, который уменьшает время разработки, поддерживая наследование.<\u002Fp><br\u002F>\r\n<p>Но стоит соблюдать тонкую грань между использованием ORM и написанием сложных запросов. В некоторых случаях ORM может запутать разработчика или сделать код громоздким и нечитаемым.<br\u002F>\r\nУдачи!<\u002Fp><\u002Fdiv>","date":"2019-10-06T07:39:29.000Z","favoritesCount":58,"flags":[],"flows":[{"alias":"develop","id":"1","name":"Разработка","path":"\u002Fflows\u002Fdevelop\u002F","route":{"name":"articles_list_by_flow","params":{"flowName":"develop"}}}],"hubs":[{"alias":"python","id":340,"image":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Fhub\u002F1c6\u002F724\u002F437\u002F1c6724437b83f3d71abd08e5c2877a7a.png","isSubscribed":false,"route":{"name":"articles_list_by_hub","params":{"name":"python","hl":"ru"}},"shortInfo":"Высокоуровневый язык программирования","title":"Python","flow":{"alias":"develop","id":"1","name":"Разработка","path":"\u002Fflows\u002Fdevelop\u002F","route":{"name":"articles_list_by_flow","params":{"flowName":"develop"}}},"isCompany":false,"meta":{},"ratingScore":239},{"alias":"postgresql","id":358,"image":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Fhub\u002F391\u002F633\u002F8e2\u002F3916338e24104572809b971807686a8f.png","isSubscribed":false,"route":{"name":"articles_list_by_hub","params":{"name":"postgresql","hl":"ru"}},"shortInfo":"Свободная объектно-реляционная СУБД","title":"PostgreSQL","flow":{"alias":"develop","id":"1","name":"Разработка","path":"\u002Fflows\u002Fdevelop\u002F","route":{"name":"articles_list_by_flow","params":{"flowName":"develop"}}},"isCompany":false,"meta":{},"ratingScore":31},{"alias":"sql","id":594,"image":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Fhub\u002F64b\u002F22b\u002F8b6\u002F64b22b8b6c4677778423e971d52eb05d.png","isSubscribed":false,"route":{"name":"articles_list_by_hub","params":{"name":"sql","hl":"ru"}},"shortInfo":"Формальный непроцедурный язык программирования","title":"SQL","flow":{"alias":"develop","id":"1","name":"Разработка","path":"\u002Fflows\u002Fdevelop\u002F","route":{"name":"articles_list_by_flow","params":{"flowName":"develop"}}},"isCompany":false,"meta":{},"ratingScore":49}],"id":"470285","isBookmarked":false,"isMegapost":false,"isCorporative":false,"isNews":false,"metadata":{"description":"Доброго дня. Сегодня хочу рассказать про ORM SQLAlchemy. Поговорим о том, что это, про его возможности и гибкость, а также рассмотрим случаи, которые не всегда...","images":[],"isShowBanners":true,"pageType":"publish_ugc_ru","scripts":[],"styles":[]},"origin":null,"route":{"name":"article_comments","params":{"id":"470285","companyName":""}},"score":5,"tags":[{"name":"sqlalchemy"},{"name":"python"},{"name":"postgresql"}],"title":"Крадущийся тигр, затаившийся SQLAlchemy. Основы","url":"https:\u002F\u002Fhabr.com\u002Fru\u002Fpost\u002F470285\u002F","viewsCount":2766,"votes":{"isEnabled":true,"voteState":0},"lang":"ru","hasPolls":false,"polls":{}}},"articlesIds":{},"isLoading":false,"pagesCount":{},"route":{"name":"article_comments","params":{"hl":"ru","id":"470285"}},"karma":{}},"comments":{"commentsList":[{"author":{"alias":"Andy_U","image":"https:\u002F\u002Fhabr.com\u002Fimages\u002Favatars\u002Fstub-user-middle.gif","route":{"name":"user_profile","params":{"login":"Andy_U","hl":"ru"}},"karmaScore":0,"type":{},"username":"Andy_U","fl":null,"hl":null,"isTopicAuthor":false,"imageSrc":"https:\u002F\u002Fhabr.com\u002Fimages\u002Favatars\u002Fstub-user-middle.gif","userpic":"https:\u002F\u002Fhabr.com\u002Fimages\u002Favatars\u002Fstub-user-middle.gif"},"canReply":true,"content":"<div xmlns="http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml">Один я не знал, что так можно?<br\u002F>\r\n<br\u002F>\r\n<pre><code class="python"> def __repr__(self):\n return "&lt;{0.__class__.__name__}(id={0.id!r})>".format(self)<\u002Fcode><\u002Fpre><\u002Fdiv>","countCollapsed":0,"datetime_published":"2019-10-06T10:39:53.000Z","id":20718557,"indent_level":0,"isCollapsed":false,"isBookmarked":false,"isNew":0,"isReadonly":false,"isUfo":false,"parent_id":0,"rating":0,"hasChildren":true,"hasCollapsed":false,"votes":{"isEnabled":true,"voteState":0},"attachedForm":null,"backTo":null,"isVotesSwitcherOpened":false,"parentComment":null},{"author":{"alias":"onegreyonewhite","image":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Ffa9\u002F942\u002Ff93\u002Ffa9942f936a90d8c8e53832682f0290e.png","route":{"name":"user_profile","params":{"login":"onegreyonewhite","hl":"ru"}},"karmaScore":0,"type":{},"username":"onegreyonewhite","fl":null,"hl":null,"isTopicAuthor":false,"imageSrc":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Ffa9\u002F942\u002Ff93\u002Ffa9942f936a90d8c8e53832682f0290e.png","userpic":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Ffa9\u002F942\u002Ff93\u002Ffa9942f936a90d8c8e53832682f0290e.png"},"canReply":true,"content":"<div xmlns="http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml"><p>Судя по всему да.<br\u002F>\r\nВообще можно даже значение словаря получить по ключу (пишите так же квадратные скобки, но значение ключа без кавычек) или значение массива по индексу.<\u002Fp><\u002Fdiv>","countCollapsed":0,"datetime_published":"2019-10-06T12:17:54.000Z","id":20718791,"indent_level":1,"isCollapsed":false,"isBookmarked":false,"isNew":0,"isReadonly":false,"isUfo":false,"parent_id":20718557,"rating":0,"hasChildren":true,"hasCollapsed":false,"votes":{"isEnabled":true,"voteState":0},"attachedForm":null,"backTo":null,"isVotesSwitcherOpened":false,"parentComment":null},{"author":{"alias":"Andy_U","image":"https:\u002F\u002Fhabr.com\u002Fimages\u002Favatars\u002Fstub-user-middle.gif","route":{"name":"user_profile","params":{"login":"Andy_U","hl":"ru"}},"karmaScore":0,"type":{},"username":"Andy_U","fl":null,"hl":null,"isTopicAuthor":false,"imageSrc":"https:\u002F\u002Fhabr.com\u002Fimages\u002Favatars\u002Fstub-user-middle.gif","userpic":"https:\u002F\u002Fhabr.com\u002Fimages\u002Favatars\u002Fstub-user-middle.gif"},"canReply":true,"content":"<div xmlns="http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml">Я, честно говоря, не очень внимательно документацию читал, но не помню там описания такого трюка. Ну и вообще, это какая-то дверь в ад, типа eval и exec:<br\u002F>\r\n<br\u002F>\r\n<pre><code class="python">class Tst:\n\n def __init__(self):\n self._a = 1\n\n @property\n def a(self):\n print('hahaha')\n return self._a\n\n\nif __name__ == '__main__':\n\n tst = Tst()\n print('{0.a}'.format(tst))\n<\u002Fcode><\u002Fpre><\u002Fdiv>","countCollapsed":0,"datetime_published":"2019-10-06T15:21:10.000Z","id":20719147,"indent_level":2,"isCollapsed":false,"isBookmarked":false,"isNew":0,"isReadonly":false,"isUfo":false,"parent_id":20718791,"rating":0,"hasChildren":true,"hasCollapsed":false,"votes":{"isEnabled":true,"voteState":0},"attachedForm":null,"backTo":null,"isVotesSwitcherOpened":false,"parentComment":null},{"author":{"alias":"onegreyonewhite","image":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Ffa9\u002F942\u002Ff93\u002Ffa9942f936a90d8c8e53832682f0290e.png","route":{"name":"user_profile","params":{"login":"onegreyonewhite","hl":"ru"}},"karmaScore":0,"type":{},"username":"onegreyonewhite","fl":null,"hl":null,"isTopicAuthor":false,"imageSrc":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Ffa9\u002F942\u002Ff93\u002Ffa9942f936a90d8c8e53832682f0290e.png","userpic":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Ffa9\u002F942\u002Ff93\u002Ffa9942f936a90d8c8e53832682f0290e.png"},"canReply":true,"content":"<div xmlns="http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml"><p>Как по мне, дверью в ад являются f-strings (Python>=3.6), которые упомянули ниже.<br\u002F>\r\nТам напрямую функции легко можно вызвать.<br\u002F>\r\nПоэтому форматирование нужно использовать обдуманно и не форматировать строки извне, а только те, которым вы доверяете (т.е. находящимся внутри кода).<\u002Fp><\u002Fdiv>","countCollapsed":0,"datetime_published":"2019-10-07T00:21:28.000Z","id":20720189,"indent_level":3,"isCollapsed":false,"isBookmarked":false,"isNew":0,"isReadonly":false,"isUfo":false,"parent_id":20719147,"rating":0,"hasChildren":true,"hasCollapsed":false,"votes":{"isEnabled":true,"voteState":0},"attachedForm":null,"backTo":null,"isVotesSwitcherOpened":false,"parentComment":null},{"author":{"alias":"Tihon_V","image":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F246\u002Fb53\u002Fa61\u002F246b53a61e696f4bd9b14e73095f2e6d.png","route":{"name":"user_profile","params":{"login":"Tihon_V","hl":"ru"}},"karmaScore":0,"type":{},"username":"Tihon_V","fl":null,"hl":null,"isTopicAuthor":false,"imageSrc":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F246\u002Fb53\u002Fa61\u002F246b53a61e696f4bd9b14e73095f2e6d.png","userpic":"\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F246\u002Fb53\u002Fa61\u002F246b53a61e696f4bd9b14e73095f2e6d.png"},"canReply":true,"content":"<div xmlns="http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml">Как по мне — f-strings тут к месту<br\u002F>\r\n<pre><code class="python">def __repr__(self):\n return f"&lt;{type(self).__name__}(id={self.id})>"\n<\u002Fcode><\u002Fpre><\u002Fdiv>","countCollapsed":0,"datetime_published":"2019-10-06T14:02:53.000Z","id":20719013,"indent_level":1,"isCollapsed":false,"isBookmarked":false,"isNew":0,"isReadonly":false,"isUfo":false,"parent_id":20718557,"rating":1,"hasChildren":true,"hasCollapsed":false,"votes":{"isEnabled":true,"voteState":0},"attachedForm":null,"backTo":null,"isVotesSwitcherOpened":false,"parentComment":null}],"commentWithFormCache":null,"isFormDisabled":false,"previewComment":null,"pageArticleComments":{"freshComments":[],"lastViewedComment":0,"postId":"470285"}},"companies":{"profilesList":{"isLoading":false,"items":[],"cache":{"key":null,"page":null,"flowAlias":null,"timestamp":null},"pagesCount":0}},"companyProfile":{"categoryProfile":{"profileData":null}},"desktopState":{"desktopFl":null,"desktopHl":null,"isChecked":false,"isLoginDemanded":false},"dfp":{"slotsDict":{}},"flows":{"flows":[]},"hubPage":{"categoryProfile":{"profileData":null}},"hubs":{"profilesList":{"isLoading":false,"items":[],"cache":{"key":null,"page":null,"flowAlias":null,"timestamp":null},"pagesCount":0}},"i18n":{"fl":"ru","hl":"ru","messages":{"Already voted":"Вы уже проголосовали","ARTICLE_COMMENTS_COUNT":"{count, plural,\n=0 { Комментировать }\nother { Комментарии { count } }\n}","ARTICLE_COMMENTS_TITLE":"Комментарии","ARTICLE_DONATE_TITLE":"Поддержать автора","ARTICLE_DONATE_BUTTON":"Отправить деньги ","ARTICLE_DONATE_POPUP":"Платежная система","ARTICLE_ORIGIN_TITLE":"Автор оригинала:","ARTICLE_SHARING_TITLE":"Поделиться публикацией","ARTICLE_TAGS_TITLE":"Теги:","CALENDAR_MONTH_APRIL_TITLE":"Апрель","CALENDAR_MONTH_AUGUST_TITLE":"Август","CALENDAR_MONTH_DECEMBER_TITLE":"Декабрь","CALENDAR_MONTH_FEBRUARY_TITLE":"Февраль","CALENDAR_MONTH_JANUARY_TITLE":"Январь","CALENDAR_MONTH_JULY_TITLE":"Июль","CALENDAR_MONTH_JUNE_TITLE":"Июнь","CALENDAR_MONTH_MARCH_TITLE":"Март","CALENDAR_MONTH_MAY_TITLE":"Май","CALENDAR_MONTH_NOVEMBER_TITLE":"Ноябрь","CALENDAR_MONTH_OCTOBER_TITLE":"Октябрь","CALENDAR_MONTH_SEPTEMBER_TITLE":"Сентябрь","CALENDAR_NEXT_MONTH_TITLE":"Следующий месяц","CALENDAR_PREVIOUS_MONTH_TITLE":"Предыдущий месяц","CALENDAR_WEEKDAY_FRIDAY_SHORT_TITLE":"Пт","CALENDAR_WEEKDAY_FRIDAY_TITLE":"Пятница","CALENDAR_WEEKDAY_MONDAY_SHORT_TITLE":"Пн","CALENDAR_WEEKDAY_MONDAY_TITLE":"Понедельник","CALENDAR_WEEKDAY_SATURDAY_SHORT_TITLE":"Сб","CALENDAR_WEEKDAY_SATURDAY_TITLE":"Суббота","CALENDAR_WEEKDAY_SUNDAY_SHORT_TITLE":"Вс","CALENDAR_WEEKDAY_SUNDAY_TITLE":"Воскресенье","CALENDAR_WEEKDAY_THURSDAY_SHORT_TITLE":"Чт","CALENDAR_WEEKDAY_THURSDAY_TITLE":"Четверг","CALENDAR_WEEKDAY_TUESDAY_SHORT_TITLE":"Вт","CALENDAR_WEEKDAY_TUESDAY_TITLE":"Вторник","CALENDAR_WEEKDAY_WEDNESDAY_SHORT_TITLE":"Ср","CALENDAR_WEEKDAY_WEDNESDAY_TITLE":"Среда","CHARGE_KARMA_NOT_ENOUGH":"У вас недостаточно кармы для голосования","CHARGE_KARMA_VOTE_USERS_POSTS":"{type, plural,\n =1 { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голос за карму и публикации}\n =2 { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голоса за карму и публикации}\n other { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голосов за карму и публикации}\n }","CHARGE_KARMA_VOTE_POSTS":"{type, plural,\n =1 { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голос за публикации}\n =2 { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голоса за публикации}\n other { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голосов за публикации}\n }","CHARGE_KARMA_VOTE_COMMENTS":"{type, plural,\n =1 { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голос за комментарии}\n =2 { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голоса за комментарии}\n other { У вас \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голосов за комментарии}\n }","CHARGE_KARMA_VOTE_AND_COMMENTS":"{type, plural,\n =1 { , и еще \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голос за комментарии}\n =2 { , и еще \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голоса за комментарии}\n other { , и еще \u003Cstrong\u003E{count}\u003C\u002Fstrong\u003E голосов за комментарии}\n }","CHARGE_KARMA_HOW_TO_INCREASE":"Как поднять карму?","CHARGE_KARMA_TOO_SMALL":"Вы не можете голосовать из-за маленькой кармы","CHARGE_EMPTY_FOR_TODAY":"На сегодня голоса закончились","COMMENT_ACTION_APPROVE":"Одобрить","COMMENT_ACTION_LOOK":"Посмотреть","COMMENT_ACTION_PREVIEW":"Предпросмотр","COMMENT_ACTION_REJECT":"Отклонить","COMMENT_ACTION_REPLY":"Ответить","COMMENT_ACTION_SAVE":"Сохранить","COMMENT_ACTION_SEND":"Отправить","COMMENT_ACTION_VOTE":"Голосовать","COMMENT_COLLAPSED":"Раскрыть ветку ({count})","COMMENT_EMPTY":"К сожалению, здесь пока нет ни одного комментария","COMMENT_NOTICE_UNABLE":"К сожалению, вы не можете оставлять комментарии.","COMMENT_NOTICE_UNAUTH_ACTION":"Войдите","COMMENT_NOTICE_UNAUTH_ACTION_SUFFIX":", пожалуйста.","COMMENT_NOTICE_UNAUTH_DECLARATION":"Только полноправные пользователи могут оставлять комментарии.","COMMENT_PLACEHOLDER_EMPTY":"НЛО прилетело и опубликовало эту надпись здесь","COMMENT_PLACEHOLDER_VOTED":"Проголосовал","ERROR_PAGE_AUTH":"Войти","ERROR_PAGE_BACK_TO_MAIN":"Вернуться на главную","ERROR_PAGE_MESSAGE_INTERNAL":"Что-то пошло не так","ERROR_PAGE_MESSAGE_NOT_AUTHORIZED":"Для просмотра этой страницы необходимо авторизоваться","ERROR_PAGE_MESSAGE_NOT_FOUND":"Страница устарела, была удалена или не существовала вовсе","ERROR_PAGE_MESSAGE_NOT_IMPLEMENTED":"Воспользуйтесь полной версией сайта, пожалуйста","ERROR_PAGE_MESSAGE_TIMEOUT":"Проверьте состояние вашего подключения и попробуйте обновить страницу.","ERROR_PAGE_REFRESH":"Обновить","ERROR_PAGE_TITLE_INTERNAL":"Внутренняя ошибка","ERROR_PAGE_TITLE_NOT_AUTHORIZED":"Ошибка авторизации.","ERROR_PAGE_TITLE_NOT_FOUND":"Страница не найдена","ERROR_PAGE_TITLE_NOT_IMPLEMENTED":"Для этого раздела мобильная версия еще не готова","ERROR_PAGE_TITLE_TIMEOUT":"Соединение потеряно","ERROR_PAGE_TITLE_ARTICLE_IN_DRAFTS":"Доступ к публикации закрыт","ERROR_PAGE_MESSAGE_ARTICLE_IN_DRAFTS":"Вы пытаетесь открыть публикацию, написанную пользователем \u003Ca class=\"tm-page__link\" href=\"{userProfileLink}\"\u003E{userName}\u003C\u002Fa\u003E, однако, публикация скрыта в черновики (самим автором или НЛО)","ERROR_PAGE_TITLE_ARTICLE_USER_INACTIVE":"Доступ к публикации закрыт","ERROR_PAGE_MESSAGE_ARTICLE_USER_INACTIVE":"Вы пытаетесь открыть публикацию, написанную пользователем \u003Ca class=\"tm-page__link\" href=\"{userProfileLink}\"\u003E{userName}\u003C\u002Fa\u003E, который в настоящее время заблокирован\u002Fдеактивирован или удалён","ERROR_PAGE_TITLE_ARTICLE_USER_BANNED":"Доступ к публикации закрыт","ERROR_PAGE_MESSAGE_ARTICLE_USER_BANNED":"Вы пытаетесь открыть публикацию, написанную пользователем \u003Ca class=\"tm-page__link\" href=\"{userProfileLink}\"\u003E{userName}\u003C\u002Fa\u003E, который в настоящее время заблокирован","EXAMPLE_COMPANY_DESC":"Компания","EXAMPLE_COMPANY":"MAIL.RU","EXAMPLE_COUNTER":"234 публикации","EXAMPLE_NICKNAME":"Boomburum","EXAMPLE_USERNAME":"Alexey Shavelev","FEEDBACK_TITLE":"Обратная связь","FEEDBACK_SUBJECT":"Укажите тему обращения:","FEEDBACK_EMAIL":"Ваш адрес электронной почты:","FEEDBACK_EMAIL_TIP":"На этот адрес будет отправлен ответ службы поддержки","FEEDBACK_MESSAGE":"Текст вашего сообщения:","FEEDBACK_PERSONAL":"Даю согласие на","FEEDBACK_PERSONAL_LINK":"обработку своих персональных данных","FEEDBACK_SEND":"Отправить","FEEDBACK_SENT":"Ваше сообщение отправлено. Сохраняйте терпение, в ближайшее время вы получите ответ.","FLAG_SANDBOX":"Из песочницы","FLAG_TRANSLATION":"Перевод","FLAG_TUTORIAL":"Tutorial","FOOTER_DESKTOP":"Полная версия","FOOTER_LANG":"Настройка языка","FOOTER_HELP":"Помощь","JOBS_ALL":"Все вакансии","JOBS_EMPTY":"На данный момент у компании нет открытых вакансий на «\u003Ca class=\"tm-company-vacancies__link\" href=\"https:\u002F\u002Fmoikrug.ru\u002Fvacancies?utm_source=tm_habrahabr&utm_medium=companyadmin&utm_campaign=vacancies\" target=\"_blank\"\u003EМоём круге\u003C\u002Fa\u003E»","JOBS_REMOTE":"удалённая работа","JOBS_TITLE":"{ company, select,\n none { Вакансии }\n other { Вакансии { company } }\n}","HELP_COMPANIES":"Компании","HELP_FEED":"Ленты","HELP_HABRACENTRE":"Хабрацентр","HELP_HABRAMAIL":"Диалоги","HELP_HUBS":"Хабы","HELP_KARMA":"Карма и рейтинг","HELP_LENTA":"Ленты","HELP_OTHER":"Разное","HELP_POSTS":"Публикации","HELP_PROBLEMS":"Проблемы с почтой","HELP_REGISTRATION":"Регистрация и приглашения","HELP_RULES":"Правила","HELP_SANDBOX":"Песочница","HELP_SETTINGS":"Настройки","HELP_SPONSORSHIP-INFO":"Спонсорство","HELP_TRACKER":"Трекер","MEGAPROJECTS_DESCRIPTION":"Мегапроекты Хабра — проекты, созданные контент-студией Хабра: яркие истории, тесты, квесты про IT-индустрию и все, что с ней связано.","NAV_ARTICLES":"Публикации","NAV_BLOG":"Блог","NAV_BOOKMARKS":"Закладки","NAV_BOOKMARKS_ARTICLES":"Публикации","NAV_BOOKMARKS_COMMENTS":"Комментарии","NAV_COMMENTS":"Комментарии","NAV_COMPANIES":"Компании","NAV_FEEDBACK":"Обратная связь","NAV_FLOWS":"Потоки","NAV_FLOW_ADMIN":"Администрирование","NAV_FLOW_DESIGN":"Дизайн","NAV_FLOW_DEVELOP":"Разработка","NAV_FLOW_GEEKTIMES":"Гиктаймс","NAV_FLOW_MANAGEMENT":"Управление","NAV_FLOW_MARKETING":"Маркетинг","NAV_FLOW_MISC":"Разное","NAV_HUBS":"Хабы","NAV_LOGOUT":"Выход","NAV_MEGAPROJECTS":"Мегапроекты","NAV_MY_FEED":"Моя лента","NAV_NEXT_PAGE":"туда","NAV_NEWS":"Новости","NAV_PREV_PAGE":"сюда","NAV_PROFILE":"Профиль","NAV_SEARCH":"Поиск","NAV_SETTINGS_LANGUAGE":"Настройка языка","NAV_TABS_ALL":"Все подряд","NAV_TABS_FEED":"По подписке","NAV_TABS_TOP":"Лучшие","NAV_TABS_TOP_DAILY":"День","NAV_TABS_TOP_MONTHLY":"Месяц","NAV_TABS_TOP_WEEKLY":"Неделя","NAV_VACANCIES":"Вакансии","NAV_USERS":"Пользователи","NEWS_DESCRIPTION":"Хабр — крупнейший в Европе ресурс для IT-специалистов. Сюда приходят обсудить новости индустрии и поделиться опытом.","NEWS_COMMENTS":"Комментарии:","NEWS_ALL_NEWS":"Все новости","BETA_TEST_NO":"Нет, спасибо","BETA_TEST_TEXT":"","BETA_TEST_TITLE":"","BETA_TEST_SWITCH_BACK":"Отключить β-тестирование","BETA_TEST_YES":"Да, давайте","OFFLINE_CLOSE":"Закрыть","OFFLINE_TITLE":"Интернет-соединение потеряно","REFRESH_TITLE":"Мы тут кое-что обновили. Чтобы «Хабр» работал корректно, обновите страницу.","REFRESH_ACTION":"Перезагрузить страницу","POINTS_ERROR_VOTING_DISABLED":"Недостаточно заряда для голосования","POINTS_LABEL_KARMA":"Карма","POINTS_LABEL_RATING":"Рейтинг","POINTS_LABEL_SUBSCRIBERS":"{type, plural,\n =5 { {count} подписчиков }\n =2 { {count} подписчика }\n =1 { {count} подписчик }\n =0 { }\n }","POLLS_VOTE":"Голосовать","POLLS_DECLINE":"Воздержаться","POLLS_STAT_VOTED":"{type, plural,\n =5 { Проголосовали {votesCount} пользователей. }\n =2 { Проголосовали {votesCount} пользователя. }\n =1 { Проголосовал {votesCount} пользователь. }\n =0 { Никто еще не голосовал. }\n }","POLLS_STAT_PASSED":"{type, plural,\n =5 { Воздержались {passCount} пользователей. }\n =2 { Воздержались {passCount} пользователя. }\n =1 { Воздержался {passCount} пользователь. }\n =0 { Воздержавшихся нет. }\n }","PROFILE_ACTION_EXPAND":"Показать все","PROFILE_ACTION_FOLLOW":"Подписаться","PROFILE_ACTION_FOLLOWING":"Подписан","PROFILE_ACTION_SUBSCRIBE":"Подписаться","PROFILE_ACTION_SUBSCRIBED":"Подписан","PROFILE_PAGE_TITLE_ARTICLES":"Публикации { username }","PROFILE_PAGE_TITLE_BOOKMARKS":"Закладки { username }","PROFILE_PAGE_TITLE_MAIN":"Профиль { username }","PROFILE_TEXT_BY_INVITATION":"по приглашению от","PROFILE_TEXT_UFO":"НЛО","PROFILE_TITLE_ABOUT":"О себе","PROFILE_TITLE_BADGES":"Значки","PROFILE_TITLE_COMPANIES":"{gender, plural,\n =2 { Подписана на компании }\n =1 { Подписан на компании }\n =0 { Подписан на компании }\n }","PROFILE_TITLE_CONTACTS":"Контактная информация","PROFILE_TITLE_HUBS":"Состоит в хабах","PROFILE_TITLE_INVITED":"{gender, plural,\n =2 { Приглашена }\n =1 { Приглашен }\n =0 { Приглашен }\n }","PROFILE_TITLE_INVITED_TO_SITE":"{gender, plural,\n =2 { Пригласила на сайт }\n =1 { Пригласил на сайт }\n =0 { Пригласил на сайт }\n }","PROFILE_TITLE_REGISTERED":"{gender, plural,\n =2 { Зарегистрирована }\n =1 { Зарегистрирован }\n =0 { Зарегистрирован }\n }","PROFILE_COMPANY_TITLE_ABOUT":"О компании","PROFILE_COMPANY_TITLE_BLOG":"Блог компании","PROFILE_COMPANY_TITLE_NEWS":"Новости компании","PROFILE_COMPANY_TITLE_DOMAINS":"Отрасли","PROFILE_COMPANY_TITLE_EMPLOYEES_COUNT":"Численность","PROFILE_COMPANY_TITLE_FOUNDATION_DATE":"Дата основания","PROFILE_COMPANY_TITLE_LOCATION":"Местоположение","PROFILE_COMPANY_TITLE_REGISTRATION_DATE":"Дата регистрации","PROFILE_COMPANY_TITLE_TEAM":"Команда","PROFILE_COMPANY_TITLE_VACANCIES":"Вакансии компании","PROFILE_COMPANY_TITLE_WEBSITE":"Сайт","RECAPTCHA_POLICY_TEXT":"Защита от спама reCAPTCHA.","RECAPTCHA_POLICY":"Политика конфиденциальности","RECAPTCHA_POLICY_AND":"","RECAPTCHA_POLICY_TERMS_OF_SERVICE":"Условия использования","RECAPTCHA_POLICY_APPLY":"","SEARCH_EMPTY_ARTICLES":"Сожалеем, поиск в публикациях не дал результатов","SEARCH_EMPTY_USERS":"Сожалеем, поиск по пользователямх не дал результатов","SEARCH_INPUT_PLACEHOLDER":"Поиск","SEARCH_TAB_ARTICLES":"Публикации","SEARCH_TAB_USERS":"Пользователи","SETTINGS_AGREEMENT_FINISH":"Завершить настройку","SETTINGS_ACTION_SAVE":"Сохранить настройки","SETTINGS_ARTICLES_ERROR":"Должен быть выбран хотя бы один язык","SETTINGS_ARTICLES_TITLE":"Публикации","SETTINGS_EMAIL_LABEL":"Получать письма об изменениях на сайте (не чаще 1 письма в месяц)","SETTINGS_FL_LANG_ENGLISH":"Английский","SETTINGS_FL_LANG_RUSSIAN":"Русский","SETTINGS_LANG_ENGLISH":"English","SETTINGS_LANG_RUSSIAN":"Русский","SETTINGS_RULES":"правила сайта","SETTINGS_TITLE":"Настройка языка","SETTINGS_UI_TITLE":"Интерфейс","TITLE_FEED":"Моя лента","TITLE_LATEST":"Все публикации подряд","TITLE_SIMILAR":"Похожие публикации","TITLE_TOP_DAILY":"Популярное за сутки","TITLE_MEGAPROJECTS":"Мегапроекты","TITLE_TOP_MONTHLY":"Лучшие публикации за месяц","TITLE_TOP_WEEKLY":"Лучшие публикации за неделю","TITLE_UNKNOWN":"","TOPIC_FORM_TITLE":"Создание публикации","TOPIC_HUBS_DESCRIPTION":"Выберите от 1 до 5 хабов; чем больше хабов будет указано, тем больше пользователей увидят публикацию","TOPIC_HUBS_TITLE":"Хабы","TOPIC_SCHEDULE_TITLE":"Запланировать публикацию","TOPIC_MODERATION_TITLE":"Публикация проверена модератором","TOPIC_NAME_DESCRIPTION":"Введите краткий и емкий заголовок, отражающий суть публикации","TOPIC_NAME_TITLE":"Заголовок","TOPIC_POLL_ADD_ANSWER_TITLE":"Добавить ответ","TOPIC_POLL_ADD_TITLE":"+ Добавить опрос","TOPIC_POLL_ANSWERS_TITLE":"Варианты ответа","TOPIC_POLL_CARD_TITLE":"Опрос №{ number }","TOPIC_POLL_DUE_DATE_TITLE":"Опрос имеет срок действия","TOPIC_POLL_MODE_MULTIPLE_TITLE":"Несколько","TOPIC_POLL_MODE_SINGLE_TITLE":"Один","TOPIC_POLL_SUBJECT_TITLE":"Тема опроса","TOPIC_TAGS_DESCRIPTION":"Метки необходимо разделять запятой","TOPIC_TAGS_TITLE":"Метки","TOPIC_TEXT_TITLE":"Текст","TOPIC_TRANSLATE_AUTHOR_DESCRIPTION":"Например: Martin McDonagh","TOPIC_TRANSLATE_AUTHOR_TITLE":"Автор оригинальной публикации","TOPIC_TRANSLATE_LINK_DESCRIPTION":"Например: https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FThe_Pillowman","TOPIC_TRANSLATE_LINK_TITLE":"Ссылка на оригинальную публикацию","TOPIC_TYPE_TITLE":"Тип публикации","TOPIC_TYPE_TRANSLATE_DESCRIPTION":"Собственный перевод иностранной статьи или публикации","TOPIC_TYPE_TRANSLATE_TITLE":"Перевод","TOPIC_TYPE_TUTORIAL_DESCRIPTION":"Обучающий материал в простой форме","TOPIC_TYPE_TUTORIAL_TITLE":"Туториал","WHEN_FULL_DATE":"{ day } { month, select,\n 0 { января }\n 1 { февраля }\n 2 { марта }\n 3 { апреля }\n 4 { мая }\n 5 { июня }\n 6 { июля }\n 7 { августа }\n 8 { сентября }\n 9 { октября }\n 10 { ноября }\n 11 { декабря }\n } { year }","WHEN_FULL_DATE_AT":"{ day } { month, select,\n 0 { января }\n 1 { февраля }\n 2 { марта }\n 3 { апреля }\n 4 { мая }\n 5 { июня }\n 6 { июля }\n 7 { августа }\n 8 { сентября }\n 9 { октября }\n 10 { ноября }\n 11 { декабря }\n } { year } в { time }","WHEN_TODAY_AT":"сегодня в { time }","WHEN_AT":"{ time }","WHEN_YESTERDAY_AT":"вчера в { time }","WELCOME_ACCOUNT":"3. Аккаунт","WELCOME_COMPANIES":"Компании","WELCOME_EMAIL":"5. Рассылка","WELCOME_FEED":"1. Лента","WELCOME_HIDE_HUBS_LIST":"Свернуть","WELCOME_HUBS":"Хабы","WELCOME_HUBS_LIST":"Состав подборки","WELCOME_KARMA":"4. Карма и рейтинг","WELCOME_NEXT_STEP":"Следующий шаг","WELCOME_PREV_STEP":"Предыдущий шаг","WELCOME_SHOW_ALL":"Показать все","WELCOME_SUBSCRIPTIONS":"2. Подписки","COMMENT_FORM_TITLE_BASE":"Написать комментарий","COMPANY":"Компания","DATE_FORMAT":"{ date } в { time }","EMPTY_POSTS_MESSAGE":"К сожалению, здесь пока нет ни одной публикации","HUB":"Хаб","NUMBER_FORMAT":"{ order, select,\nnone { { number } }\nthousands { { number }k }\n}","SITENAME":"Хабр","USER":"Пользователь"}},"info":{"infoPage":{}},"jobsList":{"jobsCompany":null,"jobsList":[]},"location":{"urlStruct":{"protocol":null,"slashes":null,"auth":null,"host":null,"port":null,"hostname":null,"hash":null,"query":{},"pathname":"\u002Fru\u002Fpost\u002F470285\u002Fcomments\u002F","path":"\u002Fru\u002Fpost\u002F470285\u002Fcomments\u002F","href":"\u002Fru\u002Fpost\u002F470285\u002Fcomments\u002F"}},"me":{"user":{"alias":"C1eriC","id":"179769","image":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002Fd01\u002Ffe1\u002F46a\u002Fd01fe146a28e3996dcf06dde41059a90.jpg","isSubscribed":false,"route":{"name":"user_profile","params":{"login":"C1eriC"}},"shortInfo":null,"title":"Александр","crc":1105603988,"charge":{"votesPosts":17,"votesComments":36,"restrictionComments":5,"restrictionPosts":5,"restrictionUsers":5},"karmaScore":17.7,"type":{"isReadAndComment":false,"isReadOnly":false},"username":"C1eriC","email":"c1er@mail.ru","counters":{"posts":"1","comments":"10","followed":"5","followers":"1","favorites":"35"},"groups":["normal"],"fl":"ru","hl":"ru"},"errorMessage":""},"navMenu":{"delta":0,"isOpened":false},"newsBlock":{"newsItems":{},"requestTime":{}},"online":{"isOfflineInformed":false,"isOnline":true},"promoBlock":{"promoItems":[],"title":""},"rootClassName":{"classNamesDict":{"tm-fira-loaded":true}},"settingsOther":{"inputs":{"uiLang":{"errors":[],"ref":null,"value":""},"articlesLangEnglish":{"errors":[],"ref":null,"value":false},"articlesLangRussian":{"errors":[],"ref":null,"value":false},"agreement":{"errors":[],"ref":null,"value":false},"email":{"errors":[],"ref":null,"value":true}}},"similarList":{"similarList":[]},"ssr":{"error":null,"isDataLoaded":true,"isDataLoading":false,"isHydrationFailed":false,"isI18nLoaded":true,"isServer":false},"tabs":{"isFilterOpened":false},"userComments":{"comments":[],"isLoading":false,"pagesCount":0,"route":null},"userMenu":{"delta":0,"isOpened":false},"userProfile":{"companies":[],"hubs":[],"invitations":[],"categoryProfile":{"profileData":{"alias":"NapoleonIT","id":"1937959","image":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F5a2\u002F462\u002F845\u002F5a2462845b5b5599bb8a75992e25eaa6.png","isSubscribed":false,"route":{"name":"articles_list_by_user","params":{"login":"NapoleonIT","hl":"ru"}},"shortInfo":"IT-компания","title":"Napoleon IT","karmaScore":7,"type":{"isReadAndComment":false,"isReadOnly":false},"username":"NapoleonIT","counters":{"posts":"3","comments":"0","followed":"0","followers":"2","favorites":"1"},"fl":null,"hl":null,"badges":[{"id":"1","title":"Захабренный","alias":"habred","description":"Пользователь с кармой \u003E0","url":null,"is_disabled":false,"is_removable":false}],"contactsLinks":[],"followersCount":2,"ratingScore":4,"registrationDate":"2019-01-17T07:43:32.000Z","invitationDate":"","gender":"0","votes":{"isEnabled":true,"voteState":0},"fullname":"Napoleon IT","imageSrc":"https:\u002F\u002Fhabrastorage.org\u002Fgetpro\u002Fhabr\u002Favatars\u002F5a2\u002F462\u002F845\u002F5a2462845b5b5599bb8a75992e25eaa6.png","paymentsMethods":[],"login":"NapoleonIT"}}},"users":{"profilesList":{"isLoading":false,"items":[],"cache":{"key":null,"page":null,"flowAlias":null,"timestamp":null},"pagesCount":0}},"viewport":{"prevScrollY":{},"scrollY":0,"width":0}}</script><script src="/js/vendors.983b12ad.js" defer></script><script src="/js/app.a99c6101.js" defer></script> | |
<script> | |
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |
</script> | |
<script type="text/javascript" > | |
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; | |
m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) | |
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); | |
ym(24049213, "init", { | |
clickmap:true, | |
trackLinks:true, | |
accurateTrackBounce:true, | |
webvisor:true | |
}); | |
</script> | |
<noscript> | |
<div> | |
<img src="https://mc.yandex.ru/watch/24049213" style="position:absolute; left:-9999px;" alt="" /> | |
</div> | |
</noscript> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment