<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Flask &#8212; ploshadka.net</title>
	<atom:link href="https://ploshadka.net/programming/backend/python/flask/feed/" rel="self" type="application/rss+xml" />
	<link>https://ploshadka.net</link>
	<description>Мир интернет технологий</description>
	<lastBuildDate>Tue, 06 Dec 2022 08:27:28 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.1</generator>
	<item>
		<title>Flask Pedantic</title>
		<link>https://ploshadka.net/flask-pedantic/</link>
					<comments>https://ploshadka.net/flask-pedantic/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Mon, 12 Dec 2022 08:00:00 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[Pedantic]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=8191</guid>

					<description><![CDATA[Примеры кода проверок на Pedantic для Flask. Эта статья будет пополняться примерами. Проверка списка данных Модель проверки: class OperationsUpdateModel&#40;BaseModel&#41;: &#160; &#160; id: int &#160; &#160; confirmed: bool Как проверить валидацию данных в API и отправить ошибку: @operations_bp.route&#40;'/update-operations/', methods=&#91;'PATCH'&#93;&#41; @login_required def...]]></description>
										<content:encoded><![CDATA[<p>Примеры кода проверок на <strong>Pedantic</strong> для <strong>Flask</strong>. <span id="more-8191"></span></p>
<p>Эта статья будет пополняться примерами. </p>
<h2>Проверка списка данных</h2>
<p>Модель проверки:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">class</span> OperationsUpdateModel<span style="color: black;">&#40;</span>BaseModel<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #008000;">id</span>: <span style="color: #008000;">int</span><br />
&nbsp; &nbsp; confirmed: <span style="color: #008000;">bool</span></div></div>
<p>Как проверить валидацию данных в API и отправить ошибку:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>operations_bp.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/update-operations/'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'PATCH'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #ff7700;font-weight:bold;">def</span> update_operations_route<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">try</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; data <span style="color: #66cc66;">=</span> request.<span style="color: black;">get_json</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">try</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list_ <span style="color: #66cc66;">=</span> parse_obj_as<span style="color: black;">&#40;</span>List<span style="color: black;">&#91;</span>OperationsUpdateModel<span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> data<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">Exception</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">&quot;error&quot;</span>: <span style="color: #483d8b;">&quot;Must be a list {id: int, confirmed: bool}&quot;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">400</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> jsonify<span style="color: black;">&#40;</span><span style="color: #483d8b;">'ok'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #008000;">Exception</span> <span style="color: #ff7700;font-weight:bold;">as</span> e:<br />
&nbsp; &nbsp; &nbsp; &nbsp; logger.<span style="color: black;">error</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span>e<span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span> exc_info<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/flask-pedantic/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Минимализм SQLAlchemy &#8212; передача списка и распаковка</title>
		<link>https://ploshadka.net/minimalizm-sqlalchemy-peredacha-spiska-i-raspakovka/</link>
					<comments>https://ploshadka.net/minimalizm-sqlalchemy-peredacha-spiska-i-raspakovka/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Wed, 09 Nov 2022 08:00:00 +0000</pubDate>
				<category><![CDATA[SQLAlchemy]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=8102</guid>

					<description><![CDATA[Как передавать разные параметры в SQLAlchemy и распаковать их. Как одновременно создать объект в SQLAlchemy, а если он создан, то обновить объект. Как передавать любые параметры при этом не создавая много одинаковых функций. Ниже приведены несколько вариантов длинного кода, но...]]></description>
										<content:encoded><![CDATA[<p>Как передавать разные параметры в <strong>SQLAlchemy</strong> и распаковать их. Как одновременно создать объект в SQLAlchemy, а если он создан, то обновить объект. Как передавать любые параметры при этом не создавая много одинаковых функций. <span id="more-8102"></span></p>
<p>Ниже приведены несколько вариантов длинного кода, но сокращение для всех применяется одно и то же.</p>
<h2>Сокращение при создании и обновлении БД</h2>
<p>Вместо увеличения списка параметров:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Result<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> date<span style="color: #66cc66;">=</span>date<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> query:<br />
&nbsp; &nbsp; <span style="color: #dc143c;">new</span> <span style="color: #66cc66;">=</span> Result<span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; rub<span style="color: #66cc66;">=</span>balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'rub'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; usd<span style="color: #66cc66;">=</span>balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'usd'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: #dc143c;">time</span><span style="color: #66cc66;">=</span>balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'time'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Result<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> date<span style="color: #66cc66;">=</span>date<span style="color: black;">&#41;</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'rub'</span>: balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'rub'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'usd'</span>: balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'usd'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'time'</span>: balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'time'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><br />
db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Можно сделать так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> update_results<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">,</span> balance<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'user_id'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> user_id<br />
&nbsp; &nbsp; balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'updated_at'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Result<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> date<span style="color: #66cc66;">=</span>balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'date'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> query:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #dc143c;">new</span> <span style="color: #66cc66;">=</span> Result<span style="color: black;">&#40;</span>**balance<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">new</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> key<span style="color: #66cc66;">,</span> value <span style="color: #ff7700;font-weight:bold;">in</span> balance.<span style="color: black;">items</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">setattr</span><span style="color: black;">&#40;</span>query<span style="color: #66cc66;">,</span> key<span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<h2>Сокращение при создании нового объекта</h2>
<p>Вместо:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> add_new_balance_assets<span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; user_id: <span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> portfolio_id: <span style="color: #008000;">int</span><span style="color: #66cc66;">,</span> instrument_type: <span style="color: #008000;">str</span><span style="color: #66cc66;">,</span> currency: <span style="color: #008000;">str</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; quantity: <span style="color: #008000;">float</span><br />
<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #dc143c;">new</span> <span style="color: #66cc66;">=</span> BalanceAssets<span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; portfolio_id<span style="color: #66cc66;">=</span>portfolio_id<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; instrument_type<span style="color: #66cc66;">=</span>instrument_type<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; currency<span style="color: #66cc66;">=</span>currency<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; quantity<span style="color: #66cc66;">=</span>quantity<br />
&nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">new</span><span style="color: black;">&#41;</span></div></div>
<p>Это:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> add_new_balance_assets<span style="color: black;">&#40;</span>balance<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #dc143c;">new</span> <span style="color: #66cc66;">=</span> BalanceAssets<span style="color: black;">&#40;</span>**balance<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">new</span><span style="color: black;">&#41;</span></div></div>
<h2>Сокращение при обновлении объекта</h2>
<p>Вместо:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> update_balance<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">,</span> id_<span style="color: #66cc66;">,</span> date_time<span style="color: #66cc66;">,</span> description<span style="color: #66cc66;">,</span> sum_in_rub<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> date_time:<br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Balance<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>id_<span style="color: black;">&#41;</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: #483d8b;">'date'</span>: date_time<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> description <span style="color: #ff7700;font-weight:bold;">or</span> description <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Balance<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>id_<span style="color: black;">&#41;</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: #483d8b;">'description'</span>: description<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> sum_in_rub:<br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Balance<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>id_<span style="color: black;">&#41;</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: #483d8b;">'sum_in_rub'</span>: sum_in_rub<span style="color: black;">&#125;</span></div></div>
<p>Так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> update_balance<span style="color: black;">&#40;</span>balance<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Balance<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'user_id'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>balance<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> key<span style="color: #66cc66;">,</span> value <span style="color: #ff7700;font-weight:bold;">in</span> balance.<span style="color: black;">items</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">setattr</span><span style="color: black;">&#40;</span>query<span style="color: #66cc66;">,</span> key<span style="color: #66cc66;">,</span> value<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/minimalizm-sqlalchemy-peredacha-spiska-i-raspakovka/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQLAlchemy &#8212; связи relationship</title>
		<link>https://ploshadka.net/sqlalchemy-svjazi-relationship/</link>
					<comments>https://ploshadka.net/sqlalchemy-svjazi-relationship/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Mon, 24 Oct 2022 10:16:48 +0000</pubDate>
				<category><![CDATA[SQLAlchemy]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=8085</guid>

					<description><![CDATA[Как связать несколько моделей таблиц базы данных PostgreSQL между собой в SQLAlchemy. Пример связей Я уже когда-то писал статью в которой затрагивалась тема связей SQLAlchemy. Это статья будет эволюционным продолжением предыдущий. Возьмём пример из официальной документации: https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html class Association&#40;Base&#41;: &#160;...]]></description>
										<content:encoded><![CDATA[<p>Как связать несколько моделей таблиц базы данных PostgreSQL между собой в SQLAlchemy. <span id="more-8085"></span></p>
<h2>Пример связей</h2>
<p>Я уже когда-то писал статью в которой затрагивалась тема <a href="https://ploshadka.net/sqlalchemy-many-to-many/">связей SQLAlchemy</a>. Это статья будет эволюционным продолжением предыдущий. </p>
<p>Возьмём пример из официальной документации:<br />
<a href="https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html" rel="noopener" target="_blank">https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html</a></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">class</span> Association<span style="color: black;">&#40;</span>Base<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;association_table&quot;</span><br />
&nbsp; &nbsp; left_id <span style="color: #66cc66;">=</span> Column<span style="color: black;">&#40;</span>ForeignKey<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;left_table.id&quot;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; right_id <span style="color: #66cc66;">=</span> Column<span style="color: black;">&#40;</span>ForeignKey<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;right_table.id&quot;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; extra_data <span style="color: #66cc66;">=</span> Column<span style="color: black;">&#40;</span>String<span style="color: black;">&#40;</span><span style="color: #ff4500;">50</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; child <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Child&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;parents&quot;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; parent <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Parent&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;children&quot;</span><span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Parent<span style="color: black;">&#40;</span>Base<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;left_table&quot;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> Column<span style="color: black;">&#40;</span>Integer<span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; children <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Association&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;parent&quot;</span><span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Child<span style="color: black;">&#40;</span>Base<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;right_table&quot;</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> Column<span style="color: black;">&#40;</span>Integer<span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; parents <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Association&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;child&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>В примере выше есть все необходимое. Я лишь обращу внимание на конкретные места, чтобы не было путаницы.</p>
<p>1. Начинаем строить наши связи с таблицы, через которую будет связываться две другие.</p>
<p>2. Обратим внимание на первую модель, на свойство child:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">child <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Child&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;parents&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>Название переменной в единственном роде и оно же ссылается на класс (модель, таблицу) с названием в единственном роде. Принцип построения этой связи легко запоминается по критерию &#8212; единственный род.</p>
<p>3. Второй параметр из примера выше (но для разнообразия возьмем другую связь) <strong>back_populates</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">parent <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Parent&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;children&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>Видим, что <strong>back_populates</strong> ссылается на свойство <strong>children</strong> в классе <strong>Parent</strong>. </p>
<p>4. В обоих моделях (таблицах), которые мы связываем есть отсылки к связанной модели через модель ассоциаций:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">children <span style="color: #66cc66;">=</span> relationship<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Association&quot;</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;parent&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>Т.е. <strong>class Parent(Base)</strong> мы связываем не напрямую через класс <strong>class Child(Base)</strong>, а через класс <strong>class Association(Base)</strong> и свойство в нем <strong>parent</strong>, которое уже ссылается на другой класс <strong>class Child(Base)</strong>.</p>
<p>Важно понимать как строится связи, иначе будут возникать ошибки:</p>
<blockquote><p>SAWarning: relationship &#8216;Task.cat_Task&#8217; will copy column Task.id to column categories_Task.Task_id, which conflicts with relationship(s): &#8216;Category.Task&#8217; (copies Task.id to categories_Task.Task_id), &#8216;Task.cats&#8217; (copies Task.id to categories_Task.Task_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards.   To silence this warning, add the parameter &#8216;overlaps=&#187;cats,Task&#187;&#8216; to the &#8216;Task.cat_Task&#8217; relationship. (Background on this error at: https://sqlalche.me/e/14/qzyx)</p></blockquote>
<h2>Пример доступа к связанным объектам</h2>
<p>Рассмотрим следующий пример. Допустим нам нужно получить объект из одной таблицы, но подтянуть связи из других таблиц. Весь код выглядит так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">class</span> TaskCategory<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'task_categories'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; category <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Category'</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;tasks&quot;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; task <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Task'</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;categories&quot;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> get_task_cats<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task_cat_id'</span>: <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> get_cats<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">category</span>.<span style="color: black;">get_cats</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Task<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'tasks'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; categories <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'TaskCategory'</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;task&quot;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> serialize<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task_id'</span>: <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_cats</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">categories</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task_cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_task_cats</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">categories</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Category<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'categories'</span><br />
&nbsp; &nbsp; tasks <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'TaskCategory'</span><span style="color: #66cc66;">,</span> back_populates<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;category&quot;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> get_cats<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: black;">&#125;</span></div></div>
<p>Я говорю об этом участке:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> serialize<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task_cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_task_cats</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">categories</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_cats</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">categories</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#125;</span></div></div>
<p>В примере выше мы хотим получить по свойству <strong>serialize</strong> данные из таблицы <strong>class Task(db.Model)</strong>, но одновременно запрашиваем обработанные данные из двух других связанных таблиц.</p>
<p>В этом случае:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #483d8b;">'task_cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_task_cats</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">categories</span><span style="color: black;">&#93;</span></div></div>
<p>обращение идет к данным <strong>class TaskCategory(db.Model)</strong>.</p>
<p>В другом случае:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_cats</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">categories</span><span style="color: black;">&#93;</span></div></div>
<p>обращение идет также к классу <strong>class TaskCategory(db.Model)</strong>, но к функции, которая обращается за данными уже к третей таблице.</p>
<p>Таким образом мы берем данные по связи из третей таблицы через вторую (таблица которая связывает обе).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/sqlalchemy-svjazi-relationship/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Как передать пустое значение с фронта на бэкенд</title>
		<link>https://ploshadka.net/kak-peredat-pustoe-znachenie-s-fronta-na-behkend/</link>
					<comments>https://ploshadka.net/kak-peredat-pustoe-znachenie-s-fronta-na-behkend/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Wed, 19 Oct 2022 18:39:17 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[SQLAlchemy]]></category>
		<category><![CDATA[Svelte]]></category>
		<category><![CDATA[pydantic]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=8059</guid>

					<description><![CDATA[Как передать пустое значение с фронта, чтобы на бэкенде оно приходило пустым. Для чего это нужно? Например, для удаление из поля данных. Например, при передаче пустой строки &#34; &#34;, бэкенд может вернуть ошибку, если входящие данные проверяются на int или...]]></description>
										<content:encoded><![CDATA[<p>Как передать пустое значение с фронта, чтобы на бэкенде оно приходило пустым. Для чего это нужно? Например, для удаление из поля данных. <span id="more-8059"></span></p>
<p>Например, при передаче пустой строки <strong>&#34; &#34;</strong>, бэкенд может вернуть ошибку, если входящие данные проверяются на int или на none. </p>
<p>Для передачи пустого значения приведем его к <strong>null</strong></p>
<p>На фронте, если передается пустое значение, то превратим в null:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #339933;">&lt;</span>input <span style="color: #FF0000;">class</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;rub edit&quot;</span> type<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;text&quot;</span> bind<span style="color: #339933;">:</span>value<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;{b.rub}&quot;</span> on<span style="color: #339933;">:</span>change<span style="color: #339933;">=</span><span style="color: #009900;">&#123;</span>saveUsersData<span style="color: #009900;">&#40;</span>b<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">&gt;</span><br />
<br />
<span style="color: #000066; font-weight: bold;">function</span> saveUsersData<span style="color: #009900;">&#40;</span>b<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>b.<span style="color: #660066;">rub</span> <span style="color: #339933;">===</span> <span style="color: #3366CC;">''</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; b.<span style="color: #660066;">rub</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">null</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; let data <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">'rub'</span><span style="color: #339933;">:</span> b.<span style="color: #660066;">rub</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; patchFetch<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'balance/update/'</span><span style="color: #339933;">,</span> data<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>На бэкенде проверим это значение на число через <strong>pydantic</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">sum_in_rub: Optional<span style="color: black;">&#91;</span><span style="color: #008000;">int</span><span style="color: black;">&#93;</span></div></div>
<p>Видим, что пройдет только int и на пустое значение <strong>&#34; &#34;</strong> возвратит ошибку.</p>
<p>А затем сохраним или значение, которое есть или значения, которого нет:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> update_balance<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">,</span> rub<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> rub <span style="color: #ff7700;font-weight:bold;">or</span> rub <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Balance<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> <span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>id_<span style="color: black;">&#41;</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: #483d8b;">'rub'</span>: rub<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#41;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/kak-peredat-pustoe-znachenie-s-fronta-na-behkend/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flask Login Pytest API</title>
		<link>https://ploshadka.net/flask-login-pytest-api/</link>
					<comments>https://ploshadka.net/flask-login-pytest-api/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Mon, 01 Aug 2022 12:46:12 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[Pytest]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=8024</guid>

					<description><![CDATA[Конфигурация PyTest для тестирования Flask API при использовании Flask-Login (Flask-Admin, Flask-Security) при которой тесты обходят авторизацию на сайте. Описание проблемы При тестировании, особенно при тестировании API для прохождения тестов необходимо обойти авторизацию на сайте. Для этого будем использовать встроенные функции....]]></description>
										<content:encoded><![CDATA[<p>Конфигурация <strong>PyTest</strong> для тестирования <strong>Flask API</strong> при использовании <strong>Flask-Login (Flask-Admin, Flask-Security)</strong> при которой тесты обходят авторизацию на сайте. <span id="more-8024"></span></p>
<h2>Описание проблемы</h2>
<p>При тестировании, особенно при тестировании API для прохождения тестов необходимо обойти авторизацию на сайте. Для этого будем использовать встроенные функции. Ниже две конфигурации при которой всё работает.</p>
<p>Код учитывает обход как <strong>@login_required</strong> для путей API, так и авторизирует пользователя при прохождении тестов. В результате <strong>flask_login.current_user.id</strong> видит указанного пользователя.</p>
<h2>Конфигурация 1</h2>
<p>Содержимое файла <strong>conftest.py</strong>. Этот файл используется для инициализации тестов.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> flask_login<br />
<span style="color: #ff7700;font-weight:bold;">import</span> pytest<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> app<span style="color: #66cc66;">,</span> db<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">models</span>.<span style="color: black;">users</span> <span style="color: #ff7700;font-weight:bold;">import</span> User<br />
<br />
<br />
<span style="color: #66cc66;">@</span>pytest.<span style="color: black;">fixture</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> client<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;TESTING&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Обходит @login_required</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;LOGIN_DISABLED&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">with</span> app.<span style="color: black;">test_client</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> client:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">yield</span> client<br />
<br />
<br />
<span style="color: #66cc66;">@</span>pytest.<span style="color: black;">fixture</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> authenticated_request<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Решает проблему с flask_login.current_user.id:</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># AttributeError: 'AnonymousUser' object has no attribute 'id'</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Создает авторизацию пользователя</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">with</span> app.<span style="color: black;">test_request_context</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; test_user <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">yield</span> flask_login.<span style="color: black;">login_user</span><span style="color: black;">&#40;</span>test_user<span style="color: black;">&#41;</span></div></div>
<p>Содержимое файла <strong>test-category.py</strong></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> json<br />
<span style="color: #ff7700;font-weight:bold;">import</span> pytest<br />
<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> db<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">models</span>.<span style="color: black;">categories</span> <span style="color: #ff7700;font-weight:bold;">import</span> Category<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">repositories</span>.<span style="color: black;">category</span> <span style="color: #ff7700;font-weight:bold;">import</span> get_category_by_id<br />
<br />
headers <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">&quot;Content-Type&quot;</span>: <span style="color: #483d8b;">&quot;application/json&quot;</span><span style="color: black;">&#125;</span><br />
<br />
<span style="color: #66cc66;">@</span>pytest.<span style="color: black;">mark</span>.<span style="color: black;">usefixtures</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;authenticated_request&quot;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> test_category_rename<span style="color: black;">&#40;</span>client<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Create test data</span><br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span><br />
&nbsp; &nbsp; cat <span style="color: #66cc66;">=</span> Category<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'Test Category'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span>cat<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Mock</span><br />
&nbsp; &nbsp; mock_name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Test Category New'</span><br />
&nbsp; &nbsp; mock_request_data <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'cat_id'</span>: cat.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'name'</span>: mock_name<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Rename category</span><br />
&nbsp; &nbsp; url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'http://127.0.0.1:5000/categories/update/'</span><br />
&nbsp; &nbsp; client.<span style="color: black;">patch</span><span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> headers<span style="color: #66cc66;">=</span>headers<span style="color: #66cc66;">,</span> data<span style="color: #66cc66;">=</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>mock_request_data<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Check new name</span><br />
&nbsp; &nbsp; category <span style="color: #66cc66;">=</span> get_category_by_id<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">,</span> cat.<span style="color: #008000;">id</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">assert</span> category.<span style="color: black;">name</span> <span style="color: #66cc66;">==</span> mock_name<br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Clear test data</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">delete</span><span style="color: black;">&#40;</span>category<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Ссылка на <a href="https://github.com/ploshadka/flask-login-pytest/tree/main/example-short" rel="noopener" target="_blank">репозиторий в GitHub</a>.</p>
<h2>Конфигурация 2</h2>
<p>Если не нужен обход авторизации во всех тестах можно обходить авторизацию в каждом отдельном тесте.</p>
<p>Содержимое файла <strong>conftest.py</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> pytest<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> app<span style="color: #66cc66;">,</span> db<br />
<br />
<br />
<span style="color: #66cc66;">@</span>pytest.<span style="color: black;">fixture</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> client<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;TESTING&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Bypasses @login_required</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">&quot;LOGIN_DISABLED&quot;</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">with</span> app.<span style="color: black;">test_client</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> client:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">yield</span> client</div></div>
<p>Содержимое файла <strong>test_category.py</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> json<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> db<span style="color: #66cc66;">,</span> app<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">models</span>.<span style="color: black;">categories</span> <span style="color: #ff7700;font-weight:bold;">import</span> Category<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">models</span>.<span style="color: black;">users</span> <span style="color: #ff7700;font-weight:bold;">import</span> User<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">repositories</span>.<span style="color: black;">category</span> <span style="color: #ff7700;font-weight:bold;">import</span> get_category_by_id<br />
<br />
headers <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;Content-Type&quot;</span>: <span style="color: #483d8b;">&quot;application/json&quot;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> test_category_rename<span style="color: black;">&#40;</span>client<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Create test data</span><br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span><br />
&nbsp; &nbsp; cat <span style="color: #66cc66;">=</span> Category<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'Test Category'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span>cat<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Mock</span><br />
&nbsp; &nbsp; mock_name <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'Test Category New'</span><br />
&nbsp; &nbsp; mock_request_data <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'cat_id'</span>: cat.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'name'</span>: mock_name<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">with</span> app.<span style="color: black;">test_client</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> client:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Решает проблему с flask_login.current_user.id:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># AttributeError: 'AnonymousUser' object has no attribute 'id'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; test_user <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>user_id<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">@</span>app.<span style="color: black;">login_manager</span>.<span style="color: black;">request_loader</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> load_user_from_request<span style="color: black;">&#40;</span>request<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> test_user<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Tests starts here</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'http://127.0.0.1:5000/categories/update/'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; client.<span style="color: black;">patch</span><span style="color: black;">&#40;</span>url<span style="color: #66cc66;">,</span> headers<span style="color: #66cc66;">=</span>headers<span style="color: #66cc66;">,</span> data<span style="color: #66cc66;">=</span>json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>mock_request_data<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Check new name</span><br />
&nbsp; &nbsp; category <span style="color: #66cc66;">=</span> get_category_by_id<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">,</span> <span style="color: #ff4500;">576</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">assert</span> category.<span style="color: black;">name</span> <span style="color: #66cc66;">==</span> mock_name<br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Clear test data</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">delete</span><span style="color: black;">&#40;</span>category<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Ссылка на <a href="https://github.com/ploshadka/flask-login-pytest/tree/main/example-long" rel="noopener" target="_blank">репозиторий в GitHub</a>.</p>
<h2>Запуск тестов</h2>
<p>Запуск тестов осуществляется командой:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pytest app/tests/</div></div>
<p>Дополнительные ключи:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"># -s - отображает содержимое print<br />
# -v - отображает полное название тестов<br />
# --disable-pytest-warnings - убирает warnings summary</div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/flask-login-pytest-api/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQLAlchemy &#8212; как получить единичный связанный объект</title>
		<link>https://ploshadka.net/sqlalchemy-kak-poluchit-edinichnyjj-svjazannyjj-obekt/</link>
					<comments>https://ploshadka.net/sqlalchemy-kak-poluchit-edinichnyjj-svjazannyjj-obekt/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 07 Nov 2021 08:00:00 +0000</pubDate>
				<category><![CDATA[SQLAlchemy]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7929</guid>

					<description><![CDATA[Получаем сериализированное свойство в связанном объекте. Как получить единичный объект Для этого нужна функция: &#160; &#160; def __str__&#40;self&#41;: &#160; &#160; &#160; &#160; return self.name_ru Весь пример: class Category&#40;db.Model, RoleMixin&#41;: &#160; &#160; __tablename__ = 'categories' &#160; &#160; id = db.Column&#40;db.Integer, primary_key=True,...]]></description>
										<content:encoded><![CDATA[<p>Получаем сериализированное свойство в связанном объекте. <span id="more-7929"></span></p>
<h2>Как получить единичный объект</h2>
<p>Для этого нужна функция:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">name_ru</span></div></div>
<p>Весь пример:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">class</span> Category<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'categories'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; cat_global_id <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'categories_global.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; parent_global_name <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'CategoryGlobal'</span><span style="color: #66cc66;">,</span> backref<span style="color: #66cc66;">=</span>db.<span style="color: black;">backref</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'categories'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> serialize_for_categories<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'cat_global_name'</span>: <span style="color: #008000;">self</span>.<span style="color: black;">parent_global_name</span>.<span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> CategoryGlobal<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'categories_global'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name_ru <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">name_ru</span></div></div>
<h2>Как получить массив из ID</h2>
<p>В классе моделей:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> serialize_time<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%d'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#125;</span></div></div>
<p>В файле, где нужно получить данные</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Task<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: black;">&#41;</span><br />
query <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize_time</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> query<span style="color: black;">&#93;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># Переделываем множество [{'15'}, {'4'}] в список id -&gt; [15, 4]</span><br />
query <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: #008000;">int</span><span style="color: black;">&#40;</span><span style="color: #008000;">list</span><span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span><span style="color: black;">&#91;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> query<span style="color: black;">&#93;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Отсортируем цифры по увеличению</span><br />
query.<span style="color: black;">sort</span><span style="color: black;">&#40;</span>key<span style="color: #66cc66;">=</span><span style="color: #ff7700;font-weight:bold;">lambda</span> x: x<span style="color: black;">&#41;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/sqlalchemy-kak-poluchit-edinichnyjj-svjazannyjj-obekt/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQLAlchemy &#8212; пример сложной выборки many to many</title>
		<link>https://ploshadka.net/sqlalchemy-primer-slozhnojj-vyborki-many-to-many/</link>
					<comments>https://ploshadka.net/sqlalchemy-primer-slozhnojj-vyborki-many-to-many/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Tue, 19 Oct 2021 08:00:00 +0000</pubDate>
				<category><![CDATA[SQLAlchemy]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7891</guid>

					<description><![CDATA[Пример выборки через SQLAlchemy, когда нужно забрать данные связанные по цепочке с другими таблицами, где в последней из них нужно отобрать данные по определенному значению. Имеем класс CategoryGlobal: class CategoryGlobal&#40;db.Model, RoleMixin&#41;: &#160; &#160; __tablename__ = 'categories_global' &#160; &#160; id =...]]></description>
										<content:encoded><![CDATA[<p>Пример выборки через <strong>SQLAlchemy</strong>, когда нужно забрать данные связанные по цепочке с другими таблицами, где в последней из них нужно отобрать данные по определенному значению. <span id="more-7891"></span></p>
<p>Имеем класс <strong>CategoryGlobal</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">class</span> CategoryGlobal<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'categories_global'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div>
<p>Внутри этого класса есть связь с двумя другими таблицами по методу <strong>many to many</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">items <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'Item'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; secondary<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'categories_item'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; backref<span style="color: #66cc66;">=</span>db.<span style="color: black;">backref</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'categories_global'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span></div></div>
<p>Там же указано свойство:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> serialize_for_favorites<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'name'</span>: <span style="color: #008000;">self</span>.<span style="color: black;">name</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">if</span> x.<span style="color: black;">favorite</span> <span style="color: #66cc66;">==</span> <span style="color: #008000;">False</span> <span style="color: #ff7700;font-weight:bold;">else</span> <span style="color: #483d8b;">''</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">items</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span></div></div>
<p>Рассмотрим последний участок кода:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">if</span> x.<span style="color: black;">favorite</span> <span style="color: #66cc66;">==</span> <span style="color: #008000;">False</span> <span style="color: #ff7700;font-weight:bold;">else</span> <span style="color: #483d8b;">''</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">items</span><span style="color: black;">&#93;</span></div></div>
<p>Обычно это выглядит так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">items</span><span style="color: black;">&#93;</span></div></div>
<p>И в этом случае будут собраны все <strong>items</strong> для всех <strong>categories_global</strong>.</p>
<p>Нам же нужно собрать только избранные записи, в которых у <strong>item</strong> есть значение в колонке <strong>favorite</strong> и оно равно <strong>True</strong>. В противном случае вернуть пустой массив. Поэтому и используется сравнение:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">if</span> x.<span style="color: black;">favorite</span> <span style="color: #66cc66;">==</span> <span style="color: #008000;">False</span> <span style="color: #ff7700;font-weight:bold;">else</span> <span style="color: #483d8b;">''</span></div></div>
<p>Теперь сформируем сам запрос в python, чтобы забрать данные:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>CategoryGlobal<span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
categories_global <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize_for_favorites</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> query<span style="color: black;">&#93;</span></div></div>
<p>Если <strong>item</strong> у <strong>categories_global</strong> существует, но <strong>True</strong> у <strong>favorite</strong> нет:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#123;</span><span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">11</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'корова'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#123;</span><span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span><span style="color: #483d8b;">''</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'бык'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span></div></div>
<p>Из примера выше видно, что там где был <strong>item</strong> появилось <strong>[одинарные кавычки]</strong>, если было бы у item <strong>True</strong>, тогда результат был бы такой:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#123;</span><span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">11</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'корова'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#123;</span><span style="color: #483d8b;">'items'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'hidden'</span>: <span style="color: #008000;">False</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">263</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'слон'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2021-09-11'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'favorite'</span>: <span style="color: #008000;">True</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">283</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'пошел'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'note'</span>: <span style="color: #008000;">None</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'time_passed'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'cat_inner_id'</span>: <span style="color: #ff4500;">263</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #483d8b;">'item_id'</span>: <span style="color: #ff4500;">283</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #483d8b;">'time_passed'</span>: <span style="color: #ff4500;">32426</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'timestamp'</span>: <span style="color: #ff4500;">1631349025.560444</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'weekday'</span>: <span style="color: #483d8b;">'сб'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">4</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'бык'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span></div></div>
<p>Затем можно удалим все где нет значений:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; &nbsp; new_list <span style="color: #66cc66;">=</span> categories_global<span style="color: black;">&#91;</span>:<span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> cat <span style="color: #ff7700;font-weight:bold;">in</span> new_list:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> cat<span style="color: black;">&#91;</span><span style="color: #483d8b;">'items'</span><span style="color: black;">&#93;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; categories_global.<span style="color: black;">remove</span><span style="color: black;">&#40;</span>cat<span style="color: black;">&#41;</span></div></div>
<p>Теперь в <strong>categories_global</strong> только одно значение.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/sqlalchemy-primer-slozhnojj-vyborki-many-to-many/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Как подключить стили в jinja2</title>
		<link>https://ploshadka.net/kak-podkljuchit-stili-v-jinja2/</link>
					<comments>https://ploshadka.net/kak-podkljuchit-stili-v-jinja2/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 10 Apr 2021 08:00:00 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7161</guid>

					<description><![CDATA[По умолчанию стили должны находится в папке static на уровне корневой директории.  Подключение будет таким: &#123;% block head %&#125; &#60;link rel=&#34;stylesheet&#34; href=&#34;{{ url_for('static', filename='styles.css', v='1') }}&#34;&#62; &#123;% endblock %&#125; По умолчанию стили для jinja2 должны находится в корневой директории сайта...]]></description>
										<content:encoded><![CDATA[<p>По умолчанию стили должны находится в папке <strong>static</strong> на уровне корневой директории.  <span id="more-7161"></span></p>
<p>Подключение будет таким:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#123;</span>% block head %<span style="color: black;">&#125;</span><br />
<span style="color: #66cc66;">&lt;</span>link rel<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;stylesheet&quot;</span> href<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;{{ url_for('static', filename='styles.css', v='1') }}&quot;</span><span style="color: #66cc66;">&gt;</span><br />
<span style="color: black;">&#123;</span>% endblock %<span style="color: black;">&#125;</span></div></div>
<p>По умолчанию стили для <strong>jinja2</strong> должны находится в корневой директории сайта в папке <strong>static</strong> иначе будет ошибка. </p>
<p>Директорию &#171;по умолчанию&#187; можно переопределить, но у меня не работали типовые команды во <strong>Flask</strong> для этого, поэтому приводить их не буду.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/kak-podkljuchit-stili-v-jinja2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flask: Celery + RabbitMQ</title>
		<link>https://ploshadka.net/flask-celery-rabbitmq/</link>
					<comments>https://ploshadka.net/flask-celery-rabbitmq/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 03 Jan 2021 18:47:59 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[celery]]></category>
		<category><![CDATA[Homebrew]]></category>
		<category><![CDATA[RabbitMQ]]></category>
		<category><![CDATA[Основательные труды]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7203</guid>

					<description><![CDATA[Полное руководство по настройки очередей задач на Flask через Celery и RabbitMQ. В Интернете сложно найти полноценное описание настройки Celery через RabbitMQ и Flask. В основном попадаются примеры на Redis и Django. В этой статье попробую это исправить и расскажу...]]></description>
										<content:encoded><![CDATA[<p>Полное руководство по настройки очередей задач на <strong>Flask через Celery и RabbitMQ</strong>. <span id="more-7203"></span></p>
<p>В Интернете сложно найти полноценное описание настройки <strong>Celery</strong> через <strong>RabbitMQ</strong> и <strong>Flask</strong>. В основном попадаются примеры на <strong>Redis</strong> и <strong>Django</strong>. В этой статье попробую это исправить и расскажу о полной настройке и запуске <strong>Celery на Flask</strong>.</p>
<h2>Что такое очереди задач?</h2>
<p>Это что-то вроде <strong>cron-задач</strong>, только более продвинутое использование. <strong>Celery</strong> позволяет запускать задачи не перегружая процессор (умеет распределять нагрузку). В отличии от cron задач celery может запускать задачи параллельно. Также имеется обработчики ошибок и легко следить за результатом.</p>
<p>В Celery встроен свой cron-обработчик. С этим мы получаем все преимущества celery и одновременно можем запланировать события по типу cron. </p>
<h2>Зачем нужен RabbitMQ?</h2>
<p>Из-за объема информацию <a href="https://ploshadka.net/rabbitmq-ustanovka-i-upravleniem-brokerom-soobshhenijj/">RabbitMQ вынесен в отдельную статью</a>. </p>
<h2>А нужны ли вам очереди задач?</h2>
<p>Очереди задач довольно замороченная штука. Вы можете увидеть это посмотрев на объем текущей статьи. Я постарался учесть большую часть для того, чтобы все настроить, но описано тут далеко не все.</p>
<p>А что делает в конечном счете <strong>celery</strong>? Если отбросить нюансы, он делает то же самое что и <strong>cron</strong>, но требует гораздо больше дополнительных сущностей, зависимостей и настроек. </p>
<p>Конечно с этой статьей учиться будет быстрее, но никто не гарантирует что вы не столкнетесь с другими проблемами или задачами не описанными здесь. Или допустим у вас будет не RabbitMQ, а Redis или не Flask, а Django. Тогда придется разбираться во многих других нюансах самостоятельно.</p>
<p>Возможно вы сможете <a href="https://ploshadka.net/kak-zapustit-skripty-python-cherez-cron-na-linux/">обойтись cron-ом на питоне</a>.</p>
<p>Или может быть вам подойдет <a href="https://ploshadka.net/python-planirovanie-zadach-cherez-timer/">зацикливание таймером (timer)</a>? Несмотря на простоту таймера есть в нём одно большое преимущество: функция не будет выполнена второй раз прежде чем она завершится. Однако это можно решить с помощью кэша, об этом будет в этой статье.</p>
<h2>Установка Celery на Flask</h2>
<p>Установка:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip <span style="color: #c20cb9; font-weight: bold;">install</span> celery</div></div>
<p>Настройка &#171;очередей задач&#187; будет на той же архитектуре, что рассказывается в цикле основных статей в <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">архитектуре проекта на Flask</a>.</p>
<p>Ниже приведены 2 конфигурации: та к которой я пришел и та которая описана в примерах на Flask.</p>
<h2>Моя конфигурация</h2>
<h3>__init__.py</h3>
<p>Это основной файл инициализации, который запускается вместе с запуском приложения на Flask. Смотрите <a href="https://ploshadka.net/python-delaem-prilozhenie-na-flask-v-lokalnojj-srede/#h2-2">структуру проекта на Flask</a>. В этом же файле <a href="https://ploshadka.net/flask-konfiguracionnye-fajjly/">настраиваются конфигурационные файлы Flask</a>.</p>
<p>Расположение файла</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">app/__init__.py</div></div>
<p>В функцию <strong>create_app</strong> (основная функция создания приложения на Flask) добавим новые конфигурационные файлы:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app <span style="color: #66cc66;">=</span> Flask<span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/private/celery.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/beatschedule.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> app<br />
<br />
app <span style="color: #66cc66;">=</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<h3>beatschedule.py</h3>
<p>Расположение файла:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">configs/beatschedule.py</div></div>
<p>Это конфигурационный файл в котором будут планироваться задачи. Его содержимое:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">imports <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks.updates'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br />
task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka_queue'</span><br />
<br />
BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka_queue'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">30.0</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<h3>celery.py</h3>
<p>Расположение файла:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">configs/private/celery.py</div></div>
<p>Это приватный файл для сельдерея. Локальный конфиг будет отличаться от конфига продакшена. Этот конфиг обязательно должен быть добавлен в исключении файла <strong>.gitignore</strong>.</p>
<p>Его содержимое для <a href="https://ploshadka.net/ustanovka-i-podkljuchenie-postgresql-na-mac-os/">PostgreSQL</a> для локальной среды:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> configs.<span style="color: black;">beatschedule</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
broker_url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pyamqp://guest@localhost//'</span><br />
result_backend <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://localhost/ploshadka_db'</span><br />
beat_schedule <span style="color: #66cc66;">=</span> BEAT_SCHEDULE</div></div>
<p>также будет работать:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">broker_url = 'pyamqp://localhost'</div></div>
<p>Для прода в <strong>result_backend</strong> добавляем пользователя БД и пароль, остальное аналогично тому что выше:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> configs.<span style="color: black;">beatschedule</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
broker_url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pyamqp://guest@localhost//'</span><br />
result_backend <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://&lt;пользователь&gt;:&lt;пароль&gt;@localhost/ploshadka_db'</span><br />
beat_schedule <span style="color: #66cc66;">=</span> BEAT_SCHEDULE</div></div>
<p>В старых инструкциях вместо <strong>result_backend</strong> указана константа <strong>CELERY_RESULT_BACKEND</strong>, но сейчас она считается <strong>deprecated</strong> и <a href="https://ploshadka.net/oshibka-the-celery_result_backend-setting-is-deprecated-and-scheduled-for-removal-in/">будет в последствии удалена</a>.</p>
<p>В результате этой настройки брокер сообщений создаст две таблицы в БД, в которых будет хранить свои данные:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/celery.jpg" rel="lightbox-0"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" class="aligncenter size-full wp-image-7211" src="https://ploshadka.net/wp-content/uploads/7203/celery.jpg" alt="" width="250" height="50" srcset="https://ploshadka.net/wp-content/uploads/7203/celery.jpg 482w, https://ploshadka.net/wp-content/uploads/7203/celery-300x59.jpg 300w" sizes="(max-width: 250px) 100vw, 250px" /></a></p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/celery2.jpg" rel="lightbox-1"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" class="aligncenter size-medium wp-image-7212" src="https://ploshadka.net/wp-content/uploads/7203/celery2-600x81.jpg" alt="" width="600" height="81" srcset="https://ploshadka.net/wp-content/uploads/7203/celery2-600x81.jpg 600w, https://ploshadka.net/wp-content/uploads/7203/celery2-1200x162.jpg 1200w, https://ploshadka.net/wp-content/uploads/7203/celery2-1536x207.jpg 1536w, https://ploshadka.net/wp-content/uploads/7203/celery2-300x40.jpg 300w, https://ploshadka.net/wp-content/uploads/7203/celery2.jpg 1920w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<h3>Задачи</h3>
<p>Расположение задач:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tasks<br />
--__init__.py<br />
--updates.py</div></div>
<p>Внутри файла updates.py:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> celery <span style="color: #ff7700;font-weight:bold;">import</span> Celery<br />
celery <span style="color: #66cc66;">=</span> Celery<span style="color: black;">&#40;</span><span style="color: #483d8b;">'updates'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'task_update'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> task_update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;task_update: success&quot;</span></div></div>
<p><strong>name=&#8217;task_update&#8217;</strong> должно равняться имени <strong>&#8216;task&#8217;: &#8216;task_update&#8217;</strong> в <strong>BEAT_SCHEDULE</strong>.</p>
<h3>Запуск</h3>
<p>Обязательно запускать с опцией конфига. Обо всех этих и других командах (и опциях для них) будет описано подробнее ниже в текущей статье.</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery beat <span style="color: #660033;">--detach</span> <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">-l</span> INFO<br />
<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery worker <span style="color: #660033;">-D</span> <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--purge</span></div></div>
<h2>Классическая конфигурация Flask</h2>
<p>Ближе к официальной доке для Flask, но использую то что выше, а это сохранил на память.</p>
<h3>__init__.py</h3>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> celery <span style="color: #ff7700;font-weight:bold;">import</span> Celery<br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> make_celery<span style="color: black;">&#40;</span>app<span style="color: black;">&#41;</span>:<br />
celery <span style="color: #66cc66;">=</span> Celery<span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">import_name</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; broker<span style="color: #66cc66;">=</span>app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'CELERY_BROKER_URL'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; backend<span style="color: #66cc66;">=</span>app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'RESULT_BACKEND'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span><br />
celery.<span style="color: black;">conf</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span>app.<span style="color: black;">config</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Настройки для cron</span><br />
celery.<span style="color: black;">conf</span>.<span style="color: black;">beat_schedule</span> <span style="color: #66cc66;">=</span> app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'BEAT_SCHEDULE'</span><span style="color: black;">&#93;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">return</span> celery</div></div>
<h3>config.py</h3>
<p>Доступы к серверу сообщений RabbitMQ пропишем внутри файла <strong>config.py</strong></p>
<p><strong>Для локалки подключение к PostgreSQL:</strong></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Celery</span><br />
CELERY_BROKER_URL<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'pyamqp://guest@localhost//'</span><br />
RESULT_BACKEND<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'db+postgresql+psycopg2://localhost/bd_name'</span></div></div>
<p><strong>На удаленном сервере:</strong></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Celery</span><br />
CELERY_BROKER_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pyamqp://localhost'</span><br />
RESULT_BACKEND <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://user_name:password@localhost/bd_name'</span></div></div>
<h3>Cron настройки</h3>
<p>Если мы хотим запускать наши задачи через cron celery, то здесь же укажем настройки:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">imports <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks.updates'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br />
task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka_queue'</span><br />
<br />
BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka_queue'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">30.0</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<p>Вместо <strong>BEAT_SCHEDULE</strong> раньше было <strong>CELERYBEAT_SCHEDULE</strong>, но сейчас является устаревшей.</p>
<h3>Создаём задачи</h3>
<p>Под задачи отведем отдельную директорию, назовем её <strong>tasks</strong>. В ней создадим файл <strong>updates.py</strong>.</p>
<p>Внутри файла. Функция с аргументами:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> make_celery<span style="color: #66cc66;">,</span> app<br />
<br />
celery <span style="color: #66cc66;">=</span> make_celery<span style="color: black;">&#40;</span>app<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> add<span style="color: black;">&#40;</span>a<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> a + b<br />
<br />
add.<span style="color: black;">delay</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span></div></div>
<p>Метод <strong>delay</strong>() отвечает за запуск нашей задачи (функции add). </p>
<p>Функция с именем:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'Update Balance'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> celery_update_balance<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; update_deposit<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'status'</span>: <span style="color: #483d8b;">'Задача завершена!'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'result'</span>: <span style="color: #ff4500;">100</span><span style="color: black;">&#125;</span><br />
<br />
celery_update_balance.<span style="color: black;">delay</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Функция для запуска по крону:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'tasks.update_balance'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> update_balance<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'status'</span>: <span style="color: #483d8b;">'Задача завершена!'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'result'</span>: <span style="color: #ff4500;">100</span><span style="color: black;">&#125;</span></div></div>
<h2>Варианты конфига cron</h2>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> celery.<span style="color: black;">schedules</span> <span style="color: #ff7700;font-weight:bold;">import</span> crontab<br />
<br />
BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka_queue'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">30.0</span><span style="color: #66cc66;">,</span> &nbsp;<span style="color: #808080; font-style: italic;"># Раз в 30 секунд</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># Другие варианты:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># 'schedule': crontab(hour=5, minute=20, day_of_week=1),</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># 'schedule': crontab(*), # Раз в минуту</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># 'args': (16, 16) # Аргументы у функции</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<h2>Запуск задач вне крона (worker)</h2>
<p>а) Мы запустили наше приложении.<br />
б) Находимся внутри виртуального окружения python.</p>
<p>После этого запускаем:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker -l info</div></div>
<p>где:<br />
tasks &#8212; директория<br />
updates &#8212; файл (модуль)<br />
-l info &#8212; вывести мониторинг в консоли</p>
<p>Эта команда запустит <strong>worker</strong> и выполнит функцию с <strong>delay</strong>. Если все настроено было верно в базе данных появится новая запись, а в консоли отобразится информация о ходе работ:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/celery-1.jpg" rel="lightbox-2"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" class="aligncenter size-medium wp-image-7219" src="https://ploshadka.net/wp-content/uploads/7203/celery-1-600x260.jpg" alt="" width="600" height="260" srcset="https://ploshadka.net/wp-content/uploads/7203/celery-1-600x260.jpg 600w, https://ploshadka.net/wp-content/uploads/7203/celery-1-1200x519.jpg 1200w, https://ploshadka.net/wp-content/uploads/7203/celery-1-1536x665.jpg 1536w, https://ploshadka.net/wp-content/uploads/7203/celery-1-300x130.jpg 300w, https://ploshadka.net/wp-content/uploads/7203/celery-1.jpg 1920w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Если в команду выше докинуть атрибут <strong>-B</strong>, то в этом файле одновременно будут запущены все задачи, как с кроном, так и без него.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker -B -l info</div></div>
<p>Предварительно надо запустить beat (об этом будет ниже).</p>
<p>Запуск воркера с -B использовать только для отладки, в противном случае задача может запускаться более одного раза. На боевом сервере команды запуска обычных задач и запланированных надо осуществлять отдельными командами. </p>
<h2>Запуск задач по крону (beat)</h2>
<div class="highlight">Есть важная особенность в работе планировщика <strong>celery beat</strong>, в которой сразу сложно разобраться.</p>
<p><em>Для запуска планировщика нужны две команды <strong>worker и beat</strong>. Важно запускать сначала worker, а потом beat. Иначе задача будет сделана 2 раза.</p>
<p>Задвоение задач можно избежать также, если запускать команды с очисткой очередей <strong>&#8212;purge</strong>.<br />
</em></div>
<p>Запускаем воркер:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker --purge -l info</div></div>
<p>В другой вкладке:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> beat --detach</div></div>
<p>где:<br />
<strong>detach</strong> &#8212; запуск в &#171;тихом&#187; (демон) режиме (без блокировки консоли). Для worker можно использовать вместо <strong>detach</strong> опцию <strong>-D</strong>.</p>
<p>worker тоже можно запускать с detach:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker --purge -D<br />
celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> beat --detach</div></div>
<p>Сначала запускается worker и он начинает мониторинг и обработку очередей задач. Затем туда с помощью beat добавляются задачи, которые подхватываются и выполняются worker-ом.</p>
<h2>Запуск на сервере</h2>
<p>На сервере запуск можно сделать через <a href="https://ploshadka.net/zapusk-celery-kak-daemon/">демонов</a>. Но мне нравится запускать через <strong>detach</strong>. Так больше контроля в одном месте.</p>
<p>Если используется &#171;моя конфигурация&#187;, то запуск осуществляется:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery beat <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">--detach</span><br />
<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-D</span></div></div>
<p>Если используется &#171;классическая конфигурация Flask&#187;, то для beat:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">--detach</span></div></div>
<p>А вот команда worker может не видеть всего что указано в <strong>make_celery</strong> и для этого надо прописать в ней все опции. При этом т.к. мы не создавали конфиг отдельный, то конфиг тоже указываем, но ссылаемся на модуль с задачей. Не знаю почему такие костыли, но только так оно работает в этом случае:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=tasks.updates worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-D</span> <span style="color: #660033;">-Q</span> ploshadka_queue <span style="color: #660033;">--result-backend</span>=db+postgresql+psycopg2:<span style="color: #000000; font-weight: bold;">//</span>localhost<span style="color: #000000; font-weight: bold;">/</span>ploshadka_db</div></div>
<div class="highlight">Для этих целей удобно сделать скрипт и добавить в <a href="https://ploshadka.net/ubuntu-avtozapusk-skriptov-posle-perezagruzki/">автозапуск после перезагрузки сервера</a>.</div>
<h2>Работа с очередями</h2>
<p>Очереди нужны, чтобы не запутаться внутри кролика, если сайтов на сервере несколько или задач очень много, то их лучше поделить на категории.</p>
<p>По умолчанию используется очередь с названием <strong>celery</strong>.</p>
<p>Для того чтобы добавить очередь к задачи надо добавить в конфиг следующее:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka.net'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<p>В случае запуска <strong>worker</strong> с определенной очередью:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">-Q</span> ploshadka.net <span style="color: #660033;">-l</span> info</div></div>
<p>Можно избежать запуск с указанием очереди прописав в файле <strong>__init__.py</strong> (там где мы инициализируем функцию celery):</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery.<span style="color: black;">conf</span>.<span style="color: black;">task_default_queue</span> <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka.net'</span></div></div>
<p>Или указать в конфигурационном файле:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka.net'</span></div></div>
<h2>Проверка очередей</h2>
<p>Предварительно должен быть запущен worker.</p>
<p>Показать какие задачи сейчас в работе:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery inspect registered</div></div>
<p>Для проверки есть ещё такие команды, но они не покажут название текущей рабочей задачи как это сделает команда выше:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery status<br />
celery inspect active<br />
celery inspect scheduled</div></div>
<h2>Логирование в Celery</h2>
<p>Celery перехватывает логирование тех задач, которые она выполняет. Даже если для тех задач <a href="https://ploshadka.net/logirovanie-v-python-logging/">настроено логирование</a>, оно будет записываться в лог celery, а не в тот который был ранее указан в логировании.</p>
<p>Это можно переопределить добавив в конфигурацию создания make_celery такую директиву:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #483d8b;">'worker_hijack_root_logger'</span>: <span style="color: #008000;">False</span></div></div>
<div class="highlight">Но я бы не советовал, потому что такое переопределение может привести к непредсказуемым результатам, да и после небольшого понимания становится удобнее читать все что касается задачи в одном месте &#8212; в логах сельдерея.</div>
<p>Для того чтобы логи записывались мы можем указать их в консольной команде:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker -D --logfile<span style="color: #66cc66;">=</span>logs/celery.<span style="color: black;">log</span> -l INFO</div></div>
<p>Стоит обратить внимание, что одновременно с указанием файла <strong>&#8212;logfile=logs/celery.log</strong> надо указать, что именно будет записываться <strong>-l INFO</strong>. </p>
<p>Вместо info можно записывать дебаг: <strong>-l DEBUG</strong>. Использовать на случай отладки, иначе слишком много дополнительной информации будет записано.</p>
<p>Для beat точно такая же система.</p>
<h2>Задвоения и дубликаты задач</h2>
<p>Как было сказано выше дубли задач celery будут, если сначала добавляются beat, а потом запускать worker или если перед запуском воркера не очищать задачи. </p>
<p>Удалить повторы задач можно командами ниже. Иногда не сразу происходит удаление, нужно подождать около минуты.</p>
<p>Как удалить и очистить задачи ниже.</p>
<h2>Как удалить задачи</h2>
<div class="highlight">Удаление задач не стоит путать с очисткой очередей. <strong>Удаление задач</strong> &#8212; это удаление их из планировщика (beat). Все что ранее добавилось в очередь будет находится в очереди и ждать выполнения. </div>
<p>Показать все планировщики celery:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #c20cb9; font-weight: bold;">ps</span> <span style="color: #660033;">-ef</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> celery</div></div>
<p>Вывод:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; <span style="color: #000000;">501</span> <span style="color: #000000;">82926</span> &nbsp; &nbsp; <span style="color: #000000;">1</span> &nbsp; <span style="color: #000000;">0</span> <span style="color: #000000;">11</span>:51PM ?? &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">0</span>:<span style="color: #000000;">00.23</span> <span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>local<span style="color: #000000; font-weight: bold;">/</span>Cellar<span style="color: #000000; font-weight: bold;">/</span>python<span style="color: #000000; font-weight: bold;">@</span><span style="color: #000000;">3.8</span><span style="color: #000000; font-weight: bold;">/</span>3.8.5<span style="color: #000000; font-weight: bold;">/</span>Frameworks<span style="color: #000000; font-weight: bold;">/</span>Python.framework<span style="color: #000000; font-weight: bold;">/</span>Versions<span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">3.8</span><span style="color: #000000; font-weight: bold;">/</span>Resources<span style="color: #000000; font-weight: bold;">/</span>Python.app<span style="color: #000000; font-weight: bold;">/</span>Contents<span style="color: #000000; font-weight: bold;">/</span>MacOS<span style="color: #000000; font-weight: bold;">/</span>Python <span style="color: #000000; font-weight: bold;">/</span>venv<span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">--detach</span><br />
&nbsp; <span style="color: #000000;">501</span> <span style="color: #000000;">83252</span> <span style="color: #000000;">59866</span> &nbsp; <span style="color: #000000;">0</span> <span style="color: #000000;">11</span>:52PM ttys001 &nbsp; &nbsp;<span style="color: #000000;">0</span>:<span style="color: #000000;">00.00</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> celery</div></div>
<p>Завершить все задачи и планирощик:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill <span style="color: #660033;">-9</span> <span style="color: #660033;">-f</span> tasks.updates.celery</div></div>
<p>Или эту (они должны были бы заменять друг друга, но на деле иногда одна из команд отказывается работать корректно):</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery control shutdown</div></div>
<p>После этой команды возникает надпись:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">Error: No nodes replied within time constraint.</div></div>
<p>Можно не обращать внимание. Все воркеры и планировщики останавливаются. Это можно проверить командой выше (grep). </p>
<h2>Как очистить или удалить очереди</h2>
<p>Если запустить планировщик <strong>beat</strong> без <strong>worker</strong>, он все равно начнет работать. Это значит, что в нужное время будет добавлена задача в очередь. Иными словами, если была запланирована задача, которая каждую минуту что-то делает, то каждую минуту будет добавлена выполнение этой задачи. Очередь задач можно увидеть в RebbitMQ:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/queues.jpg" rel="lightbox-3"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/7203/queues-600x141.jpg" alt="" width="600" height="141" class="aligncenter size-medium wp-image-7363" srcset="https://ploshadka.net/wp-content/uploads/7203/queues-600x141.jpg 600w, https://ploshadka.net/wp-content/uploads/7203/queues-1200x282.jpg 1200w, https://ploshadka.net/wp-content/uploads/7203/queues-1536x361.jpg 1536w, https://ploshadka.net/wp-content/uploads/7203/queues-300x71.jpg 300w, https://ploshadka.net/wp-content/uploads/7203/queues.jpg 1598w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>В тот момент когда будет запущен воркер, все накопленные задачи будут запущены друг за другом без того интервала, который раньше подразумевался (например, 1 минута). Это может привести к неприятным последствиям.</p>
<div class="highlight">Есть несколько способов очистки очереди. Надо понимать, что в случае очистки очереди, если не отключен планировщик beat, задачи будут снова накапливаться.</div>
<h3>1</h3>
<p>Удалить очереди конкретной задачи:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery purge <span style="color: #660033;">-f</span></div></div>
<p>Должно показать что-то вроде:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">Purged <span style="color: #000000;">108</span> messages from <span style="color: #000000;">1</span> known task queue.</div></div>
<h3>2</h3>
<p>Удалить очередь по её названию.</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery amqp queue.delete название_очереди</div></div>
<p>Теперь до запуска нового worker ничего накапливаться не будет, потому что мы вовсе удаляем очередь, а не просто очищаем ее.</p>
<h3>3</h3>
<p>Можно сразу запускать воркер с очищением очереди:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">-l</span> info <span style="color: #660033;">--purge</span></div></div>
<h2>Как не повторять задачу пока она не завершится</h2>
<p>Встроенных инструментов для этого не придумано. Надо самостоятельно делать блокировку внутри функции <a href="https://ploshadka.net/kehshirovanie-na-flask/">с помощью кэша</a>. Можно сделать так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Проверяем есть ли кэш</span><br />
is_running <span style="color: #66cc66;">=</span> cache.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;running&quot;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Если кэш есть, значит предыдущая функция не закончена, выйдем из функции</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> is_running:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Ставим кэш.</span><br />
cache.<span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'1'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Удалим кэш в конце функции</span><br />
cache.<span style="color: black;">delete</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;running&quot;</span><span style="color: black;">&#41;</span></div></div>
<h2>Дебаг</h2>
<p>Если ничего не работает, попробовать найти ошибку через команды с дебагом.</p>
<p>Для worker в консоли или в режиме демона:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> DEBUG<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> DEBUG <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-D</span></div></div>
<p>Для beat:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">-l</span> DEBUG<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">-l</span> DEBUG <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">--detach</span></div></div>
<h2>Celery Flower</h2>
<p>Для просмотра задач воспользуетесь <a href="https://ploshadka.net/celery-flower/">flower</a>. Альтернативные варианты <a href="https://ploshadka.net/celery-vyvod-na-front/">вывести на фронт данные из бд таблицы celery_taskmeta</a> или просматривать данные там напрямую, например, через <a href="https://ploshadka.net/ustanovka-i-podkljuchenie-postgresql-na-mac-os/">DataGrip</a>.</p>
<h2>Настройка конфигов</h2>
<div class="highlight">
Если запускать <strong>worker</strong> в режиме демона с помощью <strong>detach</strong>, он может перестать видеть конфиги. При этом, если запускать его обычным образом с мониторингом в консоли, то все прекрасно работает. Для исправления этой ситуации надо прописывать дополнительные настройки в файле конфигурации и указывать дополнительные параметры в консольной команде.
</div>
<p>При запуске worker в режиме detach может возникнуть такая ошибка:</p>
<blockquote><p>[2020-12-31 11:11:35,802: ERROR/MainProcess] Received unregistered task of type &#8216;task_update&#8217;.<br />
The message has been ignored and discarded.</p>
<p>Did you remember to import the module containing this task?<br />
Or maybe you&#8217;re using relative imports?</p>
<p>Please see<br />
http://docs.celeryq.org/en/latest/internals/protocol.html<br />
for more information.</p>
<p>The full contents of the message body was:<br />
&#8216;[[], {}, {&#171;callbacks&#187;: null, &#171;errbacks&#187;: null, &#171;chain&#187;: null, &#171;chord&#187;: null}]&#8217; (77b)<br />
Traceback (most recent call last):<br />
  File &#171;venv/lib/python3.9/site-packages/celery/worker/consumer/consumer.py&#187;, line 562, in on_task_received<br />
    strategy = strategies[type_]<br />
KeyError: &#8216;task_update&#8217;</p></blockquote>
<p>Дело в том, что worker не видит путь к задачам. Исправить можно двумя вариантами.</p>
<h3>Вариант 1</h3>
<p>Использовать в командной строке указатель на файл конфигурации, в котором описать настройки. Для этого в консольной команде надо указать:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">––config=configs.celery-config</div></div>
<p>где configs это директория в которой находится файл конфигурации <strong>celery-config.py</strong>.</p>
<p> Содержимое этого файла:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># List of modules to import when the Celery worker starts.</span><br />
imports <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks.updates'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span></div></div>
<p>В импорте указываем путь до файла с задачами.</p>
<h3>Вариант 2</h3>
<p>Во втором варианте больше писанины, но так тоже будет работать: можно без указания дополнительных настроек в файле.</p>
<p>В самом <strong>BEAT_SCHEDULE</strong> указать полный путь до задачи:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'every_minute'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'task'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'tasks.updates.task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">59.0</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<p>где <strong>tasks.updates</strong> это директория и файл:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tasks<br />
--__init__.py<br />
--updates.py</div></div>
<p>А <strong>task_update</strong> название функции в файле <strong>updates.py</strong>.</p>
<p>В этом случае в самой задаче тоже обязательно указать полный путь:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'tasks.updates.task_update'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> task_update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;Все ок&quot;</span></div></div>
<p>Также все равно придется добавлять в командную строку опцию, но указывать уже на сам модуль с задачей:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #660033;">--config</span>=tasks.updates</div></div>
<h2>Несколько очередей для разных сайтов на одном сервере</h2>
<p>Добавляем такой атрибут к команде по запуска worker:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #660033;">-n</span> worker1<span style="color: #000000; font-weight: bold;">@%</span>h</div></div>
<p>где каждая цифра под отдельный сайт, т.е. для второго сайта будет 2</p>
<p>Ошибка когда не указан параметр:</p>
<blockquote><p>
celery@domain1 ready.warnings.warn(W_PIDBOX_IN_USE.format(node=self))you give each node a unique node name!Or if you meant to start multiple nodes on the same host please make sure</p>
<p>Maybe you forgot to shutdown the other node or did not do so properly?<br />
2021-01-10 22:38:19,893: venv/lib/python3.8/site-packages/kombu/pidbox.py:72:<br />
UserWarning:<br />
A node named celery@domain1 is already using this process mailbox!<br />
2021-01-10 22:38:19,875: mingle: all alone2021-01-10 22:38:18,816: mingle: searching for neighbors<br />
2021-01-10 22:38:18,802: Connected to amqp://guest:**@127.0.0.1:5672//</p></blockquote>
<h2>Не работает сохранение в БД</h2>
<p>Если через worker detach не работает сохранение в БД, то ошибка аналогично предыдущей. Мы или указываем в файле конфигурации такие настройки:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Using the database to store task state and results.</span><br />
result_backend <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://localhost/ploshadka_net_db'</span></div></div>
<p>Или к консольной команде worker добавляем наш бэкенд:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">--result-backend<span style="color: #66cc66;">=</span>db+postgresql+psycopg2://localhost/ploshadka_net_db</div></div>
<h2>Не запускается очередь</h2>
<p>Ровно такая же ситуация с очередями. Можно указывать в консольной команде:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">-Q название_очереди</div></div>
<p>Или занести в файл конфигурации:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Queue by default</span><br />
task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka_queue'</span></div></div>
<p>Если не указывать очередь по умолчанию в конфигурации и не указывать ее в консольной команде, то в логах можно наблюдать такие надписи:</p>
<blockquote><p>DEBUG/MainProcess] heartbeat_tick : for connection</p></blockquote>
<h2>Другие ошибки в процессе настройки</h2>
<h3>Scheduler: Sending due task every_minute</h3>
<p>Ошибка:</p>
<blockquote><p>LocalTime -> 2020-12-16 10:12:49<br />
Configuration -><br />
    . broker -> amqp://guest:**@localhost:5672//<br />
    . loader -> celery.loaders.app.AppLoader<br />
    . scheduler -> celery.beat.PersistentScheduler<br />
    . db -> celerybeat-schedule<br />
    . logfile -> [stderr]@%INFO<br />
    . maxinterval -> 5.00 minutes (300s)<br />
[2020-12-16 10:12:49,940: INFO/MainProcess] beat: Starting&#8230;<br />
[2020-12-16 10:13:00,017: INFO/MainProcess] Scheduler: Sending due task every_minute (tasks.update_balance)</p></blockquote>
<p>Запущен beat, но не запущен worker.</p>
<p>Ошибка:</p>
<blockquote><p>Error: no such option: &#8212;config</p></blockquote>
<p>В новой версии celery изменилось расположение конфигов. Надо поставить опцию с конфигом перед beat или worker.</p>
<p>Также могут быть такие ошибки:</p>
<ul>
<li><a href="https://ploshadka.net/warning-bottle-installation-failed-building-from-source/">Warning: Bottle installation failed: building from source</a></li>
<li><a href="https://ploshadka.net/attributeerror-flask-object-has-no-attribute-user_options/">AttributeError: ‘Flask’ object has no attribute ‘user_options’</a></li>
<li><a href="https://ploshadka.net/oshibka-consumer-cannot-connect-to-amqp-guest127-0-0-15672/">consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//</a></li>
<li><a href="https://ploshadka.net/oshibka-error-homebrew-core-is-a-shallow-clone/">Error: homebrew-core is a shallow clone</a></li>
<li><a href="https://ploshadka.net/oshibka-the-celery_result_backend-setting-is-deprecated-and-scheduled-for-removal-in/">The ‘CELERY_RESULT_BACKEND’ setting is deprecated and scheduled for removal in</a></li>
<li><a href="https://ploshadka.net/valueerror-invalid-width-2-must-be-0/">https://ploshadka.net/valueerror-invalid-width-2-must-be-0/</a></li>
</ul>
<h2>Сравнение версий Celery 4 и Celery 5</h2>
<p>В четвертой версии Celery при запуске создавал pid файл с номером процесса внутри. Это было и плюсом и минусом. Плюс в том, что нельзя было запустить команду beat или worker дважды, показывало ошибку:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">Stale pidfile exists - Removing it.<br />
Seems we're already running? (pid: 34944)</div></div>
<p>А минус, чтобы убить процесс надо было использовать команду:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill -9 -f celery</div></div>
<p>А она убивала не только процессы одного сайта, но и процессы celery для всех других сайтов. Начиная с версии 5, можно использовать команду:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill -9 -f tasks.updates.celery</div></div>
<p>Еще одно изменение в порядке опций в команде:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.updates.celery --config=configs.private.celery worker -D --logfile=logs/celery.log -l INFO --purge</div></div>
<p>Теперь <strong>config</strong> и некоторые другие опции надо ставить перед worker, как показано выше.</p>
<p>Еще изменились константы. Вместо них сейчас переменные с другим наименованием.</p>
<p>Конечно это не все изменения, остальные можно узнать в официальных источниках.</p>
<h2>Окончательный вариант запуска на сервере</h2>
<p>Запускаем несколько worker:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery --config=configs.private.celery worker -Q update -D --purge -l INFO --logfile=logs/celery.log<br />
celery --config=configs.private.celery worker -Q analyse -D --purge -l INFO --logfile=logs/celery.log</div></div>
<p>И запускаем только один beat:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery --config=configs.private.celery beat --detach -l INFO --logfile=logs/beat.log</div></div>
<p>Импорты указаны в файле <strong>beatschedule</strong> и в команде их дублировать не нужно:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">imports = ('app.tasks.update', )</div></div>
<p>Для перезапуска используем (осторожно, убивает все воркеры и биты):</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill -9 -f celery</div></div>
<p>и повторяем запуск снова.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/flask-celery-rabbitmq/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SqlAlchemy vs psycopg2 &#8212; сравниваем скорость</title>
		<link>https://ploshadka.net/sqlalchemy-vs-psycopg2-sravnivaem-skorost/</link>
					<comments>https://ploshadka.net/sqlalchemy-vs-psycopg2-sravnivaem-skorost/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 31 Dec 2020 08:00:00 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[SQLAlchemy]]></category>
		<category><![CDATA[psycopg2]]></category>
		<category><![CDATA[speed]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=6361</guid>

					<description><![CDATA[Вряд ли найдется спор в том, что &#171;обертка&#187; может быть быстрее &#171;обертываемого&#187;. А на сколько медленнее работает обертка, вот здесь может быть интересно. В этой статье приведу два сценария, делающих одно и тоже в двух вариантах: на SqlAlchemy и чистый...]]></description>
										<content:encoded><![CDATA[<p>Вряд ли найдется спор в том, что &#171;обертка&#187; может быть быстрее &#171;обертываемого&#187;. А на сколько медленнее работает обертка, вот здесь может быть интересно. В этой статье приведу два сценария, делающих одно и тоже в двух вариантах: на <strong>SqlAlchemy</strong> и чистый <strong>psycopg2</strong>. <span id="more-6361"></span></p>
<p><a href="https://ploshadka.net/sqlalchemy-primery-zaprosov-orm/">SqlAlchemy</a> это ОРМ для упрощения взаимодействия с базой данных. В случае с <strong>PostgreSQL</strong>, SqlAlchemy выступает надстройкой над <strong>psycopg2</strong>, который в свою очередь является адаптером базы данных.</p>
<p>В примерах ниже операции абсолютно одинаковые с той лишь разницей, что в одном случае это делается через SqlAlchemy, в другом на psycopg2.</p>
<p><a href="https://ploshadka.net/python-kak-izmerit-skorost/">Как измерить скорость работы скриптов на python</a>.</p>
<h2>SqlAlchemy</h2>
<p><strong>Код:</strong></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> __save_operations<span style="color: black;">&#40;</span>operations<span style="color: #66cc66;">,</span> user_id<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> operation <span style="color: #ff7700;font-weight:bold;">in</span> operations:<br />
&nbsp; &nbsp; &nbsp; &nbsp; exist <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Operation.<span style="color: #008000;">id</span><span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>id_operation<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id_operation'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> exist:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #dc143c;">new</span> <span style="color: #66cc66;">=</span> Operation<span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; id_operation<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id_operation'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currency<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'currency'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; price<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'price'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; quantity<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'quantity'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; payment<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'payment'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commission<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'commission'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation_type<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'operation_type'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; date<span style="color: #66cc66;">=</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'date'</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">new</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log_message<span style="color: black;">&#40;</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'Добавлена новая операция'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p><strong>Выходные данные:</strong></p>
<div class="highlight">
Запускаются обновления<br />
02:41:21 | Cinemark Holdings Inc | Добавлена новая операция<br />
02:41:21 | Macerich | Добавлена новая операция<br />
02:41:21 | Macerich | Добавлена новая операция<br />
02:41:21 | Macerich | Добавлена новая операция<br />
02:41:21 | Macerich | Добавлена новая операция<br />
02:41:21 | American Airlines Group | Добавлена новая операция<br />
02:41:21 | American Airlines Group | Добавлена новая операция<br />
02:41:21 | Spirit Airlines Inc | Добавлена новая операция<br />
02:41:21 | Carnival | Добавлена новая операция<br />
02:41:21 | Carnival | Добавлена новая операция<br />
02:41:21 | Macerich | Добавлена новая операция<br />
02:41:21 | American Airlines Group | Добавлена новая операция<br />
02:41:21 | Spirit Airlines Inc | Добавлена новая операция<br />
02:41:21 | Carnival | Добавлена новая операция<br />
02:41:21 | Carnival | Добавлена новая операция<br />
02:41:21 | Cinemark Holdings Inc | Добавлена новая операция<br />
&#8212; 0.04560375213623047 seconds &#8212;
</div>
<p><strong>Все замеры:</strong></p>
<div class="highlight">
&#8212; 0.04560375213623047 seconds &#8212;<br />
&#8212; 0.044077157974243164 seconds &#8212;<br />
&#8212; 0.044499874114990234 seconds &#8212;
</div>
<h2>psycopg2</h2>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> __save_operations<span style="color: black;">&#40;</span>operations<span style="color: #66cc66;">,</span> user_id<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot; Add shares to DB &quot;&quot;&quot;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> operation <span style="color: #ff7700;font-weight:bold;">in</span> operations:<br />
&nbsp; &nbsp; &nbsp; &nbsp; query <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;&quot;&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; INSERT INTO operations (user_id, id_operation, name, currency, price, quantity, payment, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commission, operation_type, date)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ON CONFLICT DO NOTHING<br />
&nbsp; &nbsp; &nbsp; &nbsp; ;&quot;&quot;&quot;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; db_psycopg.<span style="color: black;">query_update</span><span style="color: black;">&#40;</span>query<span style="color: #66cc66;">,</span> <span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'id_operation'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'currency'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'price'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'quantity'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'payment'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'commission'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'operation_type'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'date'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; log_message<span style="color: black;">&#40;</span>operation<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'Добавлена новая операция'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> query_update<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> query<span style="color: #66cc66;">,</span> arg<span style="color: #66cc66;">,</span> message<span style="color: #66cc66;">=</span><span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot; Обновляет данные в таблице и возвращает сообщение об успешной операции &quot;&quot;&quot;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">try</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; cur <span style="color: #66cc66;">=</span> <span style="color: #008000;">self</span>.<span style="color: black;">conn</span>.<span style="color: black;">cursor</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; cur.<span style="color: black;">execute</span><span style="color: black;">&#40;</span>query<span style="color: #66cc66;">,</span> arg<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> message<br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: black;">&#40;</span><span style="color: #008000;">Exception</span><span style="color: #66cc66;">,</span> psycopg2.<span style="color: black;">Error</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">as</span> error:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.__error<span style="color: black;">&#40;</span>error<span style="color: black;">&#41;</span></div></div>
<p><strong>Все замеры:</strong></p>
<div class="highlight">
&#8212; 0.012418031692504883 seconds &#8212;<br />
&#8212; 0.015745878219604492 seconds &#8212;<br />
<strong>&#8212; 0.009712934494018555 seconds &#8212;</strong>
</div>
<h2>Выводы</h2>
<div class="highlight">Лучший результат в секундах:<br />
0.044 (SQLAlchemy) vs 0.0097 (psycopg2)</div>
<p>С ОРМ SQLAlchemy удобнее и быстрее писать код, но как видим проигрывает он по скорости в 3 раза, а в одном из случаев аж в 5 раз. </p>
<p>В своих проектах, там где тяжелые запросы, использую чистый <strong>psycopg2</strong>. Там где скорость запрос не нужна &#8212; <strong>SQLAlchemy</strong>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/sqlalchemy-vs-psycopg2-sravnivaem-skorost/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Celery &#8212; вывод на фронт</title>
		<link>https://ploshadka.net/celery-vyvod-na-front/</link>
					<comments>https://ploshadka.net/celery-vyvod-na-front/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Tue, 29 Dec 2020 12:15:13 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[Python Errors]]></category>
		<category><![CDATA[celery]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7416</guid>

					<description><![CDATA[Выводим результаты работы периодических задач Celery на фронт. Для этих целей можно установить flower. Но у него есть ряд недостатков: Доступ на сервере настраивается витиевато: либо дополнительно писать конфиги в Nginx либо подключаться через google api. На мой взгляд слишком...]]></description>
										<content:encoded><![CDATA[<p>Выводим результаты работы <a href="https://ploshadka.net/flask-celery-rabbitmq/">периодических задач Celery</a> на фронт. <span id="more-7416"></span></p>
<p>Для этих целей можно установить <a href="https://ploshadka.net/celery-flower/">flower</a>. Но у него есть ряд недостатков:</p>
<ul>
<li>Доступ на сервере настраивается витиевато: либо дополнительно писать конфиги в <strong>Nginx</strong> либо подключаться через <strong>google api</strong>. На мой взгляд слишком далеко от кода все это, потом не вспомнить где это лежит.</li>
<li>Нет мониторинга записанных в БД задач. Мониторинг только в лайв режиме. </li>
</ul>
<p>Все недостатки легко обойти быстро соорудив страницу в админке для просмотра результатов работы периодических задач.</p>
<p>Весь код:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> flask <span style="color: #ff7700;font-weight:bold;">import</span> Blueprint<span style="color: #66cc66;">,</span> json<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_login <span style="color: #ff7700;font-weight:bold;">import</span> login_required<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> db<br />
<br />
celery_bp <span style="color: #66cc66;">=</span> Blueprint<span style="color: black;">&#40;</span><span style="color: #483d8b;">'celery_bp'</span><span style="color: #66cc66;">,</span> __name__<span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> serialize_query<span style="color: black;">&#40;</span>msg<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">&quot;task_id&quot;</span>: msg.<span style="color: black;">task_id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">&quot;status&quot;</span>: msg.<span style="color: black;">status</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">&quot;result&quot;</span>: msg.<span style="color: black;">result</span>.<span style="color: black;">tobytes</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">decode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'utf-8'</span><span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'ignore'</span><span style="color: black;">&#41;</span>.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\x</span>05C<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00?'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">&quot;date_done&quot;</span>: msg.<span style="color: black;">date_done</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%H:%M | %d %B %Y'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><br />
<br />
<br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #66cc66;">@</span>celery_bp.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/get-logs/'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'GET'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> get_logs<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; query <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>serialize_query<span style="color: black;">&#40;</span>x<span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> db.<span style="color: black;">engine</span>.<span style="color: black;">execute</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'select * from celery_taskmeta order by date_done'</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>query<span style="color: black;">&#41;</span></div></div>
<p>Это получение данных с бэкенда. Здесь используются <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">Flask</a>, <a href="https://ploshadka.net/registracija-putejj-cherez-blueprint/">Blueprint</a>, <a href="https://ploshadka.net/sqlalchemy-primery-zaprosov-orm/">SQLAlchemy</a>. Остается только на фронте отправить запрос на указанный урл, распарсить json и вывести на страницу.</p>
<h2>Ошибки</h2>
<h3>TypeError: Object of type memoryview is not JSON serializable</h3>
<p>Из особенностей Celery. Он сохраняет результат в БД в формате <strong>memoryview</strong>. Мы этот формат переводим в байты командой:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tobytes<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Затем декодируем игнорируя ошибки:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">.<span style="color: black;">decode</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'utf-8'</span><span style="color: #66cc66;">,</span> errors<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'ignore'</span><span style="color: black;">&#41;</span></div></div>
<p>Почему-то у меня celery сохраняет <strong>memoryview</strong> с неправильными символами на что собственно и ругается интерпретатор. Дальше эти символы удаляем командой:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">.<span style="color: black;">replace</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'<span style="color: #000099; font-weight: bold;">\x</span>05C<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00<span style="color: #000099; font-weight: bold;">\x</span>00?'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">''</span><span style="color: black;">&#41;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/celery-vyvod-na-front/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Кэширование на Flask</title>
		<link>https://ploshadka.net/kehshirovanie-na-flask/</link>
					<comments>https://ploshadka.net/kehshirovanie-na-flask/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Wed, 23 Dec 2020 11:11:19 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7340</guid>

					<description><![CDATA[Описание кэширования на python в фреймворке Flask. Установка Для кэширования во Flask есть модуль Flask-Caching. Он поддерживает разные виды кэшей и требует дополнительных настроек. Вместо Flask-Caching сразу установим модуль для работы с memcached. Есть много различных модулей для работы с...]]></description>
										<content:encoded><![CDATA[<p>Описание кэширования на python в фреймворке Flask. <span id="more-7340"></span></p>
<h2>Установка</h2>
<p>Для кэширования во Flask есть модуль <strong>Flask-Caching</strong>. Он поддерживает разные виды кэшей и требует дополнительных настроек. Вместо Flask-Caching сразу установим модуль для работы с <strong>memcached</strong>. Есть много различных модулей для работы с ним. Самый популярный это <strong>pylibmc</strong> написанный на C. Я же буду использовать дальше модуль <strong>pymemcache</strong>. </p>
<p>Установим модуль memcached:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip install pymemcache</div></div>
<p>Добавляем установленный модуль в файл зависимостей:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip freeze <span style="color: #000000; font-weight: bold;">&gt;</span> requirements.txt</div></div>
<p>Как установить сервис <a href="https://ploshadka.net/ustanovka-memcached-na-mac-os/">memcached на Mac OS</a> и на <a href="https://ploshadka.net/ustanovka-memcached-na-ubuntu-20-04/">memcached на Ubuntu</a>.</p>
<h2>Интеграция</h2>
<p>В файле __init__.py добавляем:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">from pymemcache.client import base<br />
cache = base.Client(('127.0.0.1', 11211), timeout=60, connect_timeout=60)</div></div>
<h2>Использование</h2>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">from app import cache<br />
cache.set('running', '1', expire=300)<br />
cache_running = cache.get(&quot;running&quot;)<br />
cache.delete(&quot;running&quot;)</div></div>
<p>где <strong>expire</strong> &#8212; необязательный параметр, который означает время жизни кэша по истечении которого он будет удален. По умолчанию 0. Указывается в секундах.</p>
<h2>Разрешим подключение к порту</h2>
<p>Для использования мемкэша на локалхосте (без внешнего подключения к серверу) открывать ничего не требуется. А иначе в случае использования фаервола может потребоваться разрешение.</p>
<p>Добавить правило в <a href="https://ploshadka.net/ufw-firewall-na-ubuntu/">ufw</a> можно так:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">sudo ufw allow 11211/tcp # memcached</div></div>
<p>Если правило больше не нужно, его можно удалить. </p>
<p>Показать по номерам все правила:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">sudo ufw status numbered</div></div>
<p>Удалить конкретное правило:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">sudo ufw delete 4</div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/kehshirovanie-na-flask/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQLAlchemy &#8212; many-to-many</title>
		<link>https://ploshadka.net/sqlalchemy-many-to-many/</link>
					<comments>https://ploshadka.net/sqlalchemy-many-to-many/#comments</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 29 Nov 2020 22:20:35 +0000</pubDate>
				<category><![CDATA[SQLAlchemy]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7114</guid>

					<description><![CDATA[Описание связи many-to-many (многие ко многим) в SQLAlchemy. Пример создания таблиц и сохранения данных. Более новая статья по теме связей relationship SQLAlchemy, более точная, но в чем-то не заменяющая текущую. Описание моделей (классов) Мы создаем два класса: Task и Category,...]]></description>
										<content:encoded><![CDATA[<p>Описание связи many-to-many (многие ко многим) в SQLAlchemy. Пример создания таблиц и сохранения данных.<span id="more-7114"></span></p>
<p>Более новая статья по теме <a href="https://ploshadka.net/sqlalchemy-svjazi-relationship/">связей relationship SQLAlchemy</a>, более точная, но в чем-то не заменяющая текущую.</p>
<h2>Описание моделей (классов)</h2>
<p>Мы создаем два класса: <strong>Task</strong> и <strong>Category</strong>, в одной таблице будут сохранятся задачи, в другой категории. </p>
<p><strong>Таблица задач</strong></p>
<p><a href="https://ploshadka.net/wp-content/uploads/7114/tasks.jpg" rel="lightbox-0"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/7114/tasks-600x95.jpg" alt="" width="600" height="95" class="aligncenter size-medium wp-image-7121" srcset="https://ploshadka.net/wp-content/uploads/7114/tasks-600x95.jpg 600w, https://ploshadka.net/wp-content/uploads/7114/tasks-1200x191.jpg 1200w, https://ploshadka.net/wp-content/uploads/7114/tasks-300x48.jpg 300w, https://ploshadka.net/wp-content/uploads/7114/tasks.jpg 1346w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p><strong>Таблица категорий</strong></p>
<p><a href="https://ploshadka.net/wp-content/uploads/7114/categories.jpg" rel="lightbox-1"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/7114/categories-600x180.jpg" alt="" width="300" height="90" class="aligncenter size-medium wp-image-7120" srcset="https://ploshadka.net/wp-content/uploads/7114/categories-600x180.jpg 600w, https://ploshadka.net/wp-content/uploads/7114/categories-300x90.jpg 300w, https://ploshadka.net/wp-content/uploads/7114/categories.jpg 708w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>У одной задачи может быть несколько категорий. Для этих целей наилучшим образом подойдет сохранение связи между задачей и категорией в отдельной таблице. Это будет третья таблица, которая будет создана с помощью связи <strong>db.relationship</strong>.</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7114/tasks_categories.jpg" rel="lightbox-2"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/7114/tasks_categories.jpg" alt="" width="280" height="95" class="aligncenter size-full wp-image-7119" srcset="https://ploshadka.net/wp-content/uploads/7114/tasks_categories.jpg 566w, https://ploshadka.net/wp-content/uploads/7114/tasks_categories-300x102.jpg 300w" sizes="(max-width: 280px) 100vw, 280px" /></a></p>
<p>Все это делается описанием классов моделей таким образом:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># ploshadka.net</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">datetime</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_login <span style="color: #ff7700;font-weight:bold;">import</span> UserMixin<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_security <span style="color: #ff7700;font-weight:bold;">import</span> RoleMixin<br />
<br />
tasks_categories <span style="color: #66cc66;">=</span> db.<span style="color: black;">Table</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'tasks_categories'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task_id'</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'cat_id'</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'categories.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Task<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'tasks'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'users.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #dc143c;">time</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">DateTime</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Для получения доступа к связанным объектам</span><br />
&nbsp; &nbsp; cats <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Category'</span><span style="color: #66cc66;">,</span> secondary<span style="color: #66cc66;">=</span>tasks_categories<span style="color: #66cc66;">,</span> backref<span style="color: #66cc66;">=</span>db.<span style="color: black;">backref</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks'</span><span style="color: #66cc66;">,</span> lazy<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'dynamic'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Category<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'categories'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'users.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div>
<p>Если требуется в дальнейшем <a href="https://ploshadka.net/sqlalchemy-primery-zaprosov-orm/">удалять одной командой все связанные данные</a>, то в <strong>db.relationship</strong> надо добавить пару дополнительных свойств такого плана:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; &nbsp; cats <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'Category'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; secondary<span style="color: #66cc66;">=</span>tasks_categories<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; backref<span style="color: #66cc66;">=</span>db.<span style="color: black;">backref</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task'</span><span style="color: #66cc66;">,</span> lazy<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'dynamic'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; single_parent<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; cascade<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;all, delete, delete-orphan&quot;</span><br />
&nbsp; &nbsp; <span style="color: black;">&#41;</span></div></div>
<p>Однако в данном примере это не подходит, иначе удалив задачу с привязанной к ней категорией, мы удалим эти категории из всех других задач в том числе.</p>
<h2>Как сохранить данные</h2>
<p>Пример ниже представляет из себя методы с роутом (через <a href="https://ploshadka.net/registracija-putejj-cherez-blueprint/">блюпринт</a>). Она получает <a href="https://ploshadka.net/python-svelte-post-i-get-zaprosy/">данные через метод POST</a>. Затем эти данные сохраняются в БД и возвращаются обратно.</p>
<p>Внутри методов осуществляется сохранение задачи, категорий и связи с ними. Все основные моменты пояснены в первом методе.</p>
<h3>Метод сохранения к задаче одной категории</h3>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># ploshadka.net</span><br />
<span style="color: #66cc66;">@</span>task.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/add-new-item/'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'POST'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #ff7700;font-weight:bold;">def</span> add_new_item<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Данные с фронта</span><br />
&nbsp; &nbsp; data <span style="color: #66cc66;">=</span> request.<span style="color: black;">get_json</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> data<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; category <span style="color: #66cc66;">=</span> data<span style="color: black;">&#91;</span><span style="color: #483d8b;">'category'</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Текущий пользователь</span><br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> flask_login.<span style="color: black;">current_user</span>.<span style="color: #008000;">id</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Определим существование категории</span><br />
&nbsp; &nbsp; c1 <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Category<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span>category<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Если категории нет, создадим её</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> c1 <span style="color: #ff7700;font-weight:bold;">is</span> <span style="color: #008000;">None</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; c1 <span style="color: #66cc66;">=</span> Category<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span>category<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span>c1<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Используем flush, чтобы получить id категории, которая будет добавлена</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Добавим задачу</span><br />
&nbsp; &nbsp; new_task <span style="color: #66cc66;">=</span> Task<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span>name<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">time</span><span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">now</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Добавим связь</span><br />
&nbsp; &nbsp; new_task.<span style="color: black;">cats</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>c1<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span>new_task<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Теперь сохраним все что выше в нашу базу данных</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Вернем обновленные данные обратно на фронт</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> get_items<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<h3>Метод сохранения к задаче несколько категорий</h3>
<p>В этом случае наш метод немного изменится. С фронта мы получаем список категорий через запятую. Формируем из этой строки список категорий. Проверяем каждую категорию на существование в базе данных. Затем или добавляем к задаче её или создаём и опять же добавляем к задаче.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># ploshadka.net</span><br />
<span style="color: #66cc66;">@</span>task_bp.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/add-new-item/'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'POST'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #ff7700;font-weight:bold;">def</span> add_new_item<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> flask_login.<span style="color: black;">current_user</span>.<span style="color: #008000;">id</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Получим данные с фронта</span><br />
&nbsp; &nbsp; data <span style="color: #66cc66;">=</span> request.<span style="color: black;">get_json</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> data<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; categories <span style="color: #66cc66;">=</span> data<span style="color: black;">&#91;</span><span style="color: #483d8b;">'categories'</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Добавим новую задачу</span><br />
&nbsp; &nbsp; new_task <span style="color: #66cc66;">=</span> Task<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span>name<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">time</span><span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">now</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Сделаем лист из нескольких категорий (разделим их по запятым)</span><br />
&nbsp; &nbsp; categories <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">strip</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> categories.<span style="color: black;">split</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">','</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Для каждой категории</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> category_name <span style="color: #ff7700;font-weight:bold;">in</span> categories:<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Определим существует ли категория</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; category_in_db <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Category<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span>category_name<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> category_in_db:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_task.<span style="color: black;">cats</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>category_in_db<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c <span style="color: #66cc66;">=</span> Category<span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">=</span>category_name<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span>c<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">flush</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_task.<span style="color: black;">cats</span>.<span style="color: black;">append</span><span style="color: black;">&#40;</span>c<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Сохраним</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span>new_task<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Вернем обновленные данные</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> get_items<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<h2>Как получить задачи по категориям</h2>
<p>Мы должны в класс категорий:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">class</span> Category<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'categories'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; user_id <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'users.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div>
<p>добавить связь:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tasks <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'Task'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; secondary<span style="color: #66cc66;">=</span>categories_task<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; backref<span style="color: #66cc66;">=</span>db.<span style="color: black;">backref</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks'</span><span style="color: #66cc66;">,</span> lazy<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'dynamic'</span><span style="color: black;">&#41;</span><br />
<span style="color: black;">&#41;</span></div></div>
<p>Теперь мы можем получать задачи по id категории так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">response <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Category<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">45</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">tasks</span></div></div>
<p>Ответ:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#91;</span><span style="color: #66cc66;">&lt;</span>task <span style="color: #ff4500;">69</span><span style="color: #66cc66;">&gt;,</span> <span style="color: #66cc66;">&lt;</span>task <span style="color: #ff4500;">70</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#93;</span></div></div>
<h3>Получение сериализованных данных</h3>
<p>Также эти данные можно получить сериализованными (обработанными).</p>
<div class="highlight">Больше о серилизации данных можно узнать в статьях <a href="https://ploshadka.net/sqlalchemy-kak-poluchit-dannye-v-vide-spiska-slovarejj/">получение списков из SQLAlchemy</a> и <a href="https://ploshadka.net/sqlalchemy-primery-zaprosov-orm/">в примерах запросов на SQLAlchemy</a>.</div>
<p>Запросим обработанные данные:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">response <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Category<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span><span style="color: #ff4500;">45</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">tasks</span><span style="color: black;">&#93;</span></div></div>
<p>Для этого в метод <strong>Task</strong> (не перепутайте) надо добавить обработку:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> serialize<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #008000;">self</span>.<span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%H:%M'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'time_min'</span>: <span style="color: #008000;">self</span>.<span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%M'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'time_hour'</span>: <span style="color: #008000;">self</span>.<span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%H'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'date'</span>: <span style="color: #008000;">self</span>.<span style="color: #dc143c;">time</span>.<span style="color: black;">strftime</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'%Y-%m-%d'</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'name'</span>: <span style="color: #008000;">self</span>.<span style="color: black;">name</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span>x.<span style="color: black;">get_name</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">cats</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span></div></div>
<p>Ответ будет таким:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">45</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'солнце'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2021-02-19'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">69</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'тест 1'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'12:27'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time_hour'</span>: <span style="color: #483d8b;">'12'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time_min'</span>: <span style="color: #483d8b;">'27'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp;<span style="color: black;">&#123;</span><span style="color: #483d8b;">'cats'</span>: <span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">45</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'солнце'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2021-02-19'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'id'</span>: <span style="color: #ff4500;">70</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'name'</span>: <span style="color: #483d8b;">'тест 2'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'12:28'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time_hour'</span>: <span style="color: #483d8b;">'12'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time_min'</span>: <span style="color: #483d8b;">'28'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span></div></div>
<h2>Еще один пример выборки many to many</h2>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">categories_task <span style="color: #66cc66;">=</span> db.<span style="color: black;">Table</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'categories_task'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task_id'</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'task.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'cat_id'</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'categories.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<span style="color: black;">&#41;</span></div></div>
<p>Вот как можно отобрать task-и из категорий:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> get_tasks_by_category<span style="color: black;">&#40;</span>cat_id<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp;<br />
&nbsp;<span style="color: #808080; font-style: italic;"># Получим все task присоединенные к категориям</span><br />
&nbsp; &nbsp; response <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>categories_task<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>cat_id<span style="color: #66cc66;">=</span>cat_id<span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Затем по категориям отберем task</span><br />
&nbsp; &nbsp; tasks <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> response:<br />
&nbsp; &nbsp; &nbsp; &nbsp; task <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Task<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>x.<span style="color: black;">task_id</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">serialize</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; tasks.<span style="color: black;">append</span><span style="color: black;">&#40;</span>task<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>tasks<span style="color: black;">&#41;</span></div></div>
<p>Из участка выше, это:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tasks <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> response:<br />
&nbsp; &nbsp; &nbsp; &nbsp; task <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Task<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>x.<span style="color: black;">task_id</span><span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">serialize</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; tasks.<span style="color: black;">append</span><span style="color: black;">&#40;</span>task<span style="color: black;">&#41;</span></div></div>
<p>Можно также записать так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Сформируем основной запрос по tasks</span><br />
query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Task<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: black;">&#41;</span>.<span style="color: black;">order_by</span><span style="color: black;">&#40;</span>db.<span style="color: black;">desc</span><span style="color: black;">&#40;</span>Task.<span style="color: #dc143c;">time</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Заберем все tasks с учетом параметров пагинации</span><br />
tasks <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> query.<span style="color: black;">offset</span><span style="color: black;">&#40;</span>offset<span style="color: black;">&#41;</span>.<span style="color: black;">limit</span><span style="color: black;">&#40;</span>limit<span style="color: black;">&#41;</span><span style="color: black;">&#93;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/sqlalchemy-many-to-many/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Python + Svelte &#8212; post и get запросы</title>
		<link>https://ploshadka.net/python-svelte-post-i-get-zaprosy/</link>
					<comments>https://ploshadka.net/python-svelte-post-i-get-zaprosy/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 28 Nov 2020 21:52:09 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Svelte]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7046</guid>

					<description><![CDATA[Описание взаимодействия передачи информации от фронта к бэку через GET и POST на Svelte и Python. GET JavaScript Функция получает домен в зависимости от окружения (локальный или лайв). Она пригодится нам в дальнейшем. export function getDomain&#40;&#41; &#123; &#160; &#160; let...]]></description>
										<content:encoded><![CDATA[<p>Описание взаимодействия передачи информации от фронта к бэку через GET и POST на Svelte и Python. <span id="more-7046"></span></p>
<h2>GET</h2>
<h3>JavaScript</h3>
<p>Функция получает домен в зависимости от окружения (локальный или лайв). Она пригодится нам в дальнейшем.</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #FF0000;">export</span> <span style="color: #000066; font-weight: bold;">function</span> getDomain<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; let url <span style="color: #339933;">=</span> window.<span style="color: #660066;">location</span>.<span style="color: #660066;">href</span>.<span style="color: #660066;">split</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;/&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; let domain <span style="color: #339933;">=</span> url<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">&quot;//&quot;</span> <span style="color: #339933;">+</span> url<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">2</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/'</span><span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>url<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">2</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">===</span> <span style="color: #3366CC;">'localhost'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> domain <span style="color: #339933;">+</span> url<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">3</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> domain<span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>Основная функция для отправки запроса get. В ней же мы используем функцию выше для подстановки домена.</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #FF0000;">export</span> async <span style="color: #000066; font-weight: bold;">function</span> getFetch<span style="color: #009900;">&#40;</span>url<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; let response <span style="color: #339933;">=</span> await fetch<span style="color: #009900;">&#40;</span>getDomain<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> url<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> response.<span style="color: #660066;">json</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>Положим эти функции в файл <strong>main.js</strong>. Это общие функции, которые будут использоваться во всём проекте.</p>
<h3>Svelte</h3>
<p>На стороне Svelte мы импортируем функцию для запроса get из файла main:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #FF0000;">import</span> <span style="color: #009900;">&#123;</span>getFetch<span style="color: #009900;">&#125;</span> from <span style="color: #3366CC;">'../main'</span><span style="color: #339933;">;</span></div></div>
<p>Дальше мы будем обращаться к этой функции, чтобы передать нужные нам значения. Например, таким образом:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #000066; font-weight: bold;">function</span> saveUsersData<span style="color: #009900;">&#40;</span>deposit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; getFetch<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'deposit/save-user-data/'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #339933;">+</span> deposit.<span style="color: #660066;">user_id_time</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #339933;">+</span> deposit.<span style="color: #660066;">time</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #339933;">+</span> deposit.<span style="color: #660066;">date</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'/'</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #339933;">+</span> deposit.<span style="color: #660066;">deposit</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>На том как сюда передаются значения останавливаться не будем. Эти данные передаются в функцию через события &#8212; об этом можно узнать в статье о <a href="https://ploshadka.net/?p=7035">событиях на Svelte</a>.</p>
<h3>Python</h3>
<p>На стороне питона мы используем <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">Flask</a> и <a href="https://ploshadka.net/registracija-putejj-cherez-blueprint/">Blueprint</a> и получаем данные так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>deposit_bp.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/save-user-data/&lt;user_id_time&gt;/&lt;time&gt;/&lt;date&gt;/&lt;deposit&gt;'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'GET'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #ff7700;font-weight:bold;">def</span> save_user_data<span style="color: black;">&#40;</span>user_id_time<span style="color: #66cc66;">,</span> <span style="color: #dc143c;">time</span><span style="color: #66cc66;">,</span> date<span style="color: #66cc66;">,</span> deposit<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp;<span style="color: #ff7700;font-weight:bold;">pass</span></div></div>
<p>Внутри этой функции полученные данные обрабатываете на своё усмотрение.</p>
<h2>POST</h2>
<h3>JavaScript</h3>
<p>Добавим в наш файл main.js функцию для отправки Post запросов:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #FF0000;">export</span> async <span style="color: #000066; font-weight: bold;">function</span> postFetch<span style="color: #009900;">&#40;</span>url<span style="color: #339933;">,</span> data<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; let response <span style="color: #339933;">=</span> await fetch<span style="color: #009900;">&#40;</span>getDomain<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> url<span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; method<span style="color: #339933;">:</span> <span style="color: #3366CC;">'POST'</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers<span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span> <span style="color: #3366CC;">&quot;Content-Type&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;application/json&quot;</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body<span style="color: #339933;">:</span> JSON.<span style="color: #660066;">stringify</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> response.<span style="color: #660066;">json</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<h3>Svelte</h3>
<p>Точно также импортируем функцию:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #FF0000;">import</span> <span style="color: #009900;">&#123;</span>postFetch<span style="color: #009900;">&#125;</span> from <span style="color: #3366CC;">'../main'</span><span style="color: #339933;">;</span></div></div>
<p>Дальше мы её вызываем и передаём туда свои данные:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #000066; font-weight: bold;">function</span> saveUsersData<span style="color: #009900;">&#40;</span>deposit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; let data <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">'user_id_time'</span><span style="color: #339933;">:</span> deposit.<span style="color: #660066;">user_id_time</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">'time'</span><span style="color: #339933;">:</span> deposit.<span style="color: #660066;">time</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">'date'</span><span style="color: #339933;">:</span> deposit.<span style="color: #660066;">date</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">'deposit'</span><span style="color: #339933;">:</span> deposit.<span style="color: #660066;">deposit</span><span style="color: #339933;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #3366CC;">'description'</span><span style="color: #339933;">:</span> deposit.<span style="color: #660066;">description</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; postFetch<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'deposit/save-user-data/'</span><span style="color: #339933;">,</span> data<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<h3>Python</h3>
<p>На python мы получаем эти данные так:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #339933;">@</span>deposit_bp.<span style="color: #660066;">route</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'/save-user-data/'</span><span style="color: #339933;">,</span> methods<span style="color: #339933;">=</span><span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'POST'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #339933;">@</span>login_required<br />
def save_user_data<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span><br />
&nbsp; &nbsp; data <span style="color: #339933;">=</span> request.<span style="color: #660066;">get_json</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; description <span style="color: #339933;">=</span> data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'description'</span><span style="color: #009900;">&#93;</span><br />
&nbsp; &nbsp; user_id_time <span style="color: #339933;">=</span> data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'user_id_time'</span><span style="color: #009900;">&#93;</span><br />
&nbsp; &nbsp; time <span style="color: #339933;">=</span> data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'time'</span><span style="color: #009900;">&#93;</span><br />
&nbsp; &nbsp; date <span style="color: #339933;">=</span> data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'date'</span><span style="color: #009900;">&#93;</span><br />
&nbsp; &nbsp; deposit <span style="color: #339933;">=</span> data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'deposit'</span><span style="color: #009900;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> json.<span style="color: #660066;">dumps</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'ok'</span><span style="color: #009900;">&#41;</span></div></div>
<p>Что-то делаем с ними и если нужно возвращаем обратно. В данном пример возвращаем всегда, потому что наша JS функция после отправки всегда ждет ответа.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/python-svelte-post-i-get-zaprosy/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQLAlchemy &#8212; о проблемах с сессиями</title>
		<link>https://ploshadka.net/sqlalchemy-o-problemakh-s-sessijami/</link>
					<comments>https://ploshadka.net/sqlalchemy-o-problemakh-s-sessijami/#comments</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 26 Nov 2020 08:00:00 +0000</pubDate>
				<category><![CDATA[SQLAlchemy]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=6261</guid>

					<description><![CDATA[SQLAlchemy, как и многие другие ОРМ работает с базой данных через сессии. Это отличается от работы на прямую с базой данных. Не понимания принципа работы сессий может приводить к надоедливым ошибкам. Ошибки sqlalchemy.orm.exc.DetachedInstanceError: Instance is not bound to a Session;...]]></description>
										<content:encoded><![CDATA[<p>SQLAlchemy, как и многие другие ОРМ работает с базой данных через сессии. Это отличается от работы на прямую с базой данных. Не понимания принципа работы сессий может приводить к надоедливым ошибкам. <span id="more-6261"></span></p>
<p>Ошибки</p>
<blockquote><p>sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x11a478e50> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)</p>
<p>&#171;attribute refresh operation cannot proceed&#187; % (state_str(state))<br />
sqlalchemy.orm.exc.DetachedInstanceError: Instance <UserSetting at 0x10ca7ffd0> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)</p></blockquote>
<p>Как избежать ошибок на примере кода ниже, где все работает корректно:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> ploshadka_function<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Положим в переменную все ID пользователей (чтобы потом работать с другой сессией)</span><br />
&nbsp; &nbsp; user_ids <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: #008000;">id</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>User.<span style="color: #008000;">id</span><span style="color: black;">&#41;</span>.<span style="color: black;">distinct</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> x_id <span style="color: #ff7700;font-weight:bold;">in</span> user_ids:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Теперь мы можем работать с новой сессией</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #dc143c;">user</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span><span style="color: #66cc66;">=</span>x_id<span style="color: black;">&#41;</span>.<span style="color: black;">first</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> setting <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">user</span>.<span style="color: black;">settings</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; row <span style="color: #66cc66;">=</span> <span style="color: #008000;">vars</span><span style="color: black;">&#40;</span>setting<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> row<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'market_direction'</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">send_market_direction</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">user</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">elif</span> row<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">'change_percent'</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">send_change_percent</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">user</span><span style="color: #66cc66;">,</span> row<span style="color: black;">&#91;</span><span style="color: #483d8b;">'percent'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Обновляем дату</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>UserSetting<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span>row<span style="color: black;">&#91;</span><span style="color: #483d8b;">'name'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'key'</span>: value<span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Коммитим все изменения после обработки одного пользователя</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; db.<span style="color: black;">session</span>.<span style="color: black;">commit</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>В этом коде могло быть 2 ошибки в пересечениях сессий.</p>
<p>1.<br />
Если бы мы использовали <strong>db.session.commit()</strong> внутри <strong>второго</strong> цикла <strong>for</strong>, то <strong>SQLAlchemy</strong> закрыл бы сессию, посчитав, что задача выполнена. </p>
<p>Вместо этого мы обновляем методом <strong>update</strong> наш экземпляр класса <strong>User</strong>, созданный выше. А в самом конце делаем <strong>commit</strong>.</p>
<p>2.<br />
Но <strong>db.session.commit()</strong> все еще внутри первого цикла и мы не можем вынести его за него. Поэтому мы кладем наверху ID пользователей в переменную <strong>user_ids</strong>. ID собраны отдельно и при закрытии сессии нам не страшно, что закроется сеанс.</p>
<p>Например, такой код при взаимодействии выше, вызвал бы ошибки с сессиями:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">users <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">for</span> <span style="color: #dc143c;">user</span> <span style="color: #ff7700;font-weight:bold;">in</span> users:<br />
&nbsp; &nbsp;settings <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>UserSetting<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span><span style="color: #dc143c;">user</span>.<span style="color: #008000;">id</span><span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Это произошло бы из-за <strong>db.session.commit()</strong> внутри первого цикла <strong>for</strong>. После первого прохода в переменной <strong>users</strong> ничего бы не осталось. И во втором проходе возникла бы ошибка <strong>&#171;Instance <User at 0x11a478e50> is not bound to a Session&#187;</strong>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/sqlalchemy-o-problemakh-s-sessijami/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Python + Svelte &#8212; таблицы &#8212; как сравнить сумму с предыдущей и проставить цвета</title>
		<link>https://ploshadka.net/python-svelte-kak-naznachit-cveta/</link>
					<comments>https://ploshadka.net/python-svelte-kak-naznachit-cveta/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 21 Nov 2020 22:40:01 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[SQLAlchemy]]></category>
		<category><![CDATA[Svelte]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7013</guid>

					<description><![CDATA[У нас есть некая таблица с данными. В ней одна из колонок состоит из цифр. Мы хотим чтобы цвет следующего значения подкрашивался зеленым цветом, если оно больше предыдущего. И наоборот &#8212; красным, если меньше. Как это сделать ниже. Введение Пример...]]></description>
										<content:encoded><![CDATA[<p>У нас есть некая таблица с данными. В ней одна из колонок состоит из цифр. Мы хотим чтобы цвет следующего значения подкрашивался зеленым цветом, если оно больше предыдущего. И наоборот &#8212; красным, если меньше. Как это сделать ниже. <span id="more-7013"></span></p>
<h2>Введение</h2>
<p>Пример участка готовой таблицы:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7013/img1.jpg" rel="lightbox-0"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/7013/img1-600x383.jpg" alt="" width="400" height="240" class="aligncenter size-medium wp-image-7015" /></a></p>
<p>Все было бы очень просто, если бы список формировался от последнего к первому. В таком случае следующий элемент будет знать значение предыдущего. </p>
<p>У нас же наоборот &#8212; последний элемент по дате формируется первым в списке. И он ничего не знает о следующей строке, ведь она еще не создана.</p>
<p>Для решение этой проблемы на бэке мы позже добавим одну дополнительную колонку, в которой будет сохранено предыдущее значение.</p>
<h2>Svelte</h2>
<p>Функция, которая присваивает цвет:</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">let colour<span style="color: #339933;">;</span><br />
<span style="color: #000066; font-weight: bold;">function</span> compare<span style="color: #009900;">&#40;</span>previousDeposit<span style="color: #339933;">,</span> currentDeposit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>currentDeposit <span style="color: #339933;">&gt;</span> previousDeposit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; colour <span style="color: #339933;">=</span> <span style="color: #3366CC;">'green'</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; colour <span style="color: #339933;">=</span> <span style="color: #3366CC;">'red'</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> colour<span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></div>
<p>В отдельной статье можно узнать <a href="https://ploshadka.net/svelte-dva-varianta-razbora-massivov/">как создать таблицу из данных массива на Svelte</a>.</p>
<p>В одном из участков формировании таблицы, там где формируется сумма мы вызываем нашу функцию сравнения и передаем ей данные предыдущего и следующего значения, а возвращаем класс с цветом.</p>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #339933;">&lt;</span>div <span style="color: #FF0000;">class</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;cell deposit edit {compare(deposit.previous_deposit, deposit.deposit)}&quot;</span><br />
&nbsp; &nbsp; &nbsp;contenteditable<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;true&quot;</span><br />
&nbsp; &nbsp; &nbsp;bind<span style="color: #339933;">:</span>innerHTML<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;{deposit.deposit}&quot;</span><br />
&nbsp; &nbsp; &nbsp;on<span style="color: #339933;">:</span>input<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;{saveUsersData(deposit)}&quot;</span><br />
<span style="color: #339933;">&gt;&lt;/</span>div<span style="color: #339933;">&gt;</span></div></div>
<p>Про <strong>conteneditable</strong> (в этом задаче он не имеет значения) можно <a href="https://ploshadka.net/svelte-gruppirovka-i-sokhranenie-dannykh-v-each-block/">прочитать тут</a>.</p>
<h2>Python</h2>
<p>На стороне бэкэнда мы забираем из БД данные отсортированные так, чтобы последняя запись по дате была первой. </p>
<p>В примере данные берутся с помощью <a href="https://ploshadka.net/sqlalchemy-primery-zaprosov-orm/">SQLAlchemy</a>. Останавливаться подробнее не будем. Нас интересуют в этой задаче другие участки.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> get_deposits<span style="color: black;">&#40;</span>user_id<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; response <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Budget<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: black;">&#41;</span>.<span style="color: black;">order_by</span><span style="color: black;">&#40;</span>db.<span style="color: black;">desc</span><span style="color: black;">&#40;</span>Budget.<span style="color: #dc143c;">time</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Заберем из списка тюплов данные из одной колонки</span><br />
&nbsp; &nbsp; deposits <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>i<span style="color: black;">&#91;</span><span style="color: #483d8b;">'deposit'</span><span style="color: black;">&#93;</span> <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> response<span style="color: black;">&#93;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Узнаем кол-во элементов чтобы пройтись меньше на 1 раз</span><br />
&nbsp; &nbsp; length <span style="color: #66cc66;">=</span> <span style="color: #008000;">len</span><span style="color: black;">&#40;</span>deposits<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Удалим первый элемент, он нам не нужен</span><br />
&nbsp; &nbsp; deposits.<span style="color: black;">pop</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Добавим в наш список новую колонку</span><br />
&nbsp; &nbsp; i <span style="color: #66cc66;">=</span> <span style="color: #ff4500;">0</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> response:<br />
&nbsp; &nbsp; &nbsp; &nbsp; i +<span style="color: #66cc66;">=</span> <span style="color: #ff4500;">1</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> length <span style="color: #66cc66;">&gt;</span> i:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Заберем первый элемент и удалим его из списка</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x<span style="color: black;">&#91;</span><span style="color: #483d8b;">'previous_deposit'</span><span style="color: black;">&#93;</span> <span style="color: #66cc66;">=</span> deposits.<span style="color: black;">pop</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> response</div></div>
<p>Разберем подробнее.</p>
<p>Сначала мы получаем из БД данные такого формата:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2020-11-14'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'deposit'</span>: <span style="color: #ff4500;">71721</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'00:58'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'user_id_time'</span>: <span style="color: #483d8b;">'1_1605986079'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp;<span style="color: black;">&#123;</span><span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2020-10-30'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'deposit'</span>: <span style="color: #ff4500;">15890</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'10:05'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'user_id_time'</span>: <span style="color: #483d8b;">'1_1603985712'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span></div></div>
<p>Затем мы делаем ряд преобразований (в комментариях кода подробно все расписано) и получаем такой список тюплов:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#91;</span><span style="color: black;">&#123;</span><span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2020-11-14'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'deposit'</span>: <span style="color: #ff4500;">71721</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'00:58'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'previous_deposit'</span>: <span style="color: #ff4500;">15890</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'user_id_time'</span>: <span style="color: #483d8b;">'1_1605986079'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp;<span style="color: black;">&#123;</span><span style="color: #483d8b;">'date'</span>: <span style="color: #483d8b;">'2020-10-30'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'deposit'</span>: <span style="color: #ff4500;">15890</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'time'</span>: <span style="color: #483d8b;">'10:05'</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'previous_deposit'</span>: <span style="color: #ff4500;">17518</span><span style="color: #66cc66;">,</span><br />
&nbsp; <span style="color: #483d8b;">'user_id_time'</span>: <span style="color: #483d8b;">'1_1603985712'</span><span style="color: black;">&#125;</span><span style="color: black;">&#93;</span></div></div>
<p>Вот и все.</p>
<h2>Простой случай</h2>
<p>Если колонка отсортирована от первого к последнему.</p>
<h3>Svelte</h3>
<div class="codecolorer-container javascript dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #339933;">&lt;</span>script<span style="color: #339933;">&gt;</span><br />
let colour<span style="color: #339933;">;</span><br />
let previousDeposit <span style="color: #339933;">=</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">;</span><br />
<span style="color: #000066; font-weight: bold;">function</span> compare<span style="color: #009900;">&#40;</span>currentDeposit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>currentDeposit <span style="color: #339933;">&gt;</span> previousDeposit<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; colour <span style="color: #339933;">=</span> <span style="color: #3366CC;">'green'</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span> <span style="color: #000066; font-weight: bold;">else</span> <span style="color: #009900;">&#123;</span> &nbsp; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; colour <span style="color: #339933;">=</span> <span style="color: #3366CC;">'red'</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<br />
&nbsp; &nbsp; previousDeposit <span style="color: #339933;">=</span> currentDeposit<span style="color: #339933;">;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #000066; font-weight: bold;">return</span> colour<span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #339933;">&lt;/</span>script<span style="color: #339933;">&gt;</span><br />
<br />
<span style="color: #339933;">&lt;</span>div <span style="color: #FF0000;">class</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;cell deposit edit {compare(deposit.deposit)}&quot;</span><br />
&nbsp; &nbsp; &nbsp;contenteditable<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;true&quot;</span><br />
&nbsp; &nbsp; &nbsp;bind<span style="color: #339933;">:</span>innerHTML<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;{deposit.deposit}&quot;</span><br />
&nbsp; &nbsp; &nbsp;on<span style="color: #339933;">:</span>input<span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;{saveUsersData(deposit)}&quot;</span><br />
<span style="color: #339933;">&gt;&lt;/</span>div<span style="color: #339933;">&gt;</span></div></div>
<h3>Python</h3>
<p>А на питоне делать никаких лишних преобразований не нужно, кроме получения данные через <strong>asc</strong>.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">response <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">serialize</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Budget<span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>user_id<span style="color: black;">&#41;</span>.<span style="color: black;">order_by</span><span style="color: black;">&#40;</span>db.<span style="color: black;">asc</span><span style="color: black;">&#40;</span>Budget.<span style="color: #dc143c;">time</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/python-svelte-kak-naznachit-cveta/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Варианты запуска скрипта на сервере для python</title>
		<link>https://ploshadka.net/varianty-zapuska-skripta-na-servere-dlja-python/</link>
					<comments>https://ploshadka.net/varianty-zapuska-skripta-na-servere-dlja-python/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 15 Nov 2020 20:02:52 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=6963</guid>

					<description><![CDATA[Отдельный запуск команд с cli flask python. Этот материал из цикла статей по разработке сайтов на python: от локальной разработки до развертывания на удаленном сервере. Варианты запуска скипта на Flask Предположим у нас есть такой код, который что-то обновляет: #...]]></description>
										<content:encoded><![CDATA[<p>Отдельный запуск команд с cli flask python. <span id="more-6963"></span></p>
<p>Этот материал из цикла статей по разработке сайтов на python: <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">от локальной разработки до развертывания на удаленном сервере</a>.</p>
<h2>Варианты запуска скипта на Flask</h2>
<p>Предположим у нас есть такой код, который что-то обновляет:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Flask CLI</span><br />
<span style="color: #66cc66;">@</span>app.<span style="color: black;">cli</span>.<span style="color: black;">command</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">updates</span>.<span style="color: black;">updates</span> <span style="color: #ff7700;font-weight:bold;">import</span> start_updating<br />
&nbsp; &nbsp; start_updating<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Для его запуска в командной строке ввести:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">flask update</div></div>
<p>Эта команда запустит скрипт и выведет мониторинг этого процесса в командной строке:<br />
<a href="https://ploshadka.net/wp-content/uploads/6963/monitoring1.jpg" rel="lightbox-0"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6963/monitoring1-600x197.jpg" alt="" width="600" height="197" class="aligncenter size-medium wp-image-6965" srcset="https://ploshadka.net/wp-content/uploads/6963/monitoring1-600x197.jpg 600w, https://ploshadka.net/wp-content/uploads/6963/monitoring1-1200x394.jpg 1200w, https://ploshadka.net/wp-content/uploads/6963/monitoring1-300x98.jpg 300w, https://ploshadka.net/wp-content/uploads/6963/monitoring1.jpg 1256w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Запустит в &#171;тихом&#187; режиме:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">flask update <span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null</div></div>
<p>При запуске скрипта, покажет его <strong>PID</strong>, а также освободит командную строку для ввода следующей команды:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">flask update <span style="color: #000000; font-weight: bold;">&gt;/</span>dev<span style="color: #000000; font-weight: bold;">/</span>null <span style="color: #000000; font-weight: bold;">&amp;</span></div></div>
<p>Покажет:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #7a0874; font-weight: bold;">&#91;</span><span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#93;</span> <span style="color: #000000;">37000</span></div></div>
<p>Цифра выше это номер процесса. Удобно его знать, если позже нужно завершить работу этого процесса вручную.</p>
<h2>Варианты остановки скрипта на Flask</h2>
<p>Как найти в системе запущенный скрипт python:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #c20cb9; font-weight: bold;">ps</span> <span style="color: #660033;">-ef</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> python</div></div>
<p><a href="https://ploshadka.net/wp-content/uploads/6963/grep1.jpg" rel="lightbox-1"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6963/grep1-600x95.jpg" alt="" width="600" height="95" class="aligncenter size-medium wp-image-6967" srcset="https://ploshadka.net/wp-content/uploads/6963/grep1-600x95.jpg 600w, https://ploshadka.net/wp-content/uploads/6963/grep1-300x47.jpg 300w, https://ploshadka.net/wp-content/uploads/6963/grep1.jpg 1100w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Как остановить скрипт</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #c20cb9; font-weight: bold;">kill</span> <span style="color: #000000;">37000</span></div></div>
<p>Остановит все скрипты с названием <strong>script_name_or_file_name</strong>:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill <span style="color: #660033;">-9</span> <span style="color: #660033;">-f</span> script_name_or_file_name</div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/varianty-zapuska-skripta-na-servere-dlja-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Регистрация путей через Blueprint</title>
		<link>https://ploshadka.net/registracija-putejj-cherez-blueprint/</link>
					<comments>https://ploshadka.net/registracija-putejj-cherez-blueprint/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 15 Nov 2020 20:01:33 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=6927</guid>

					<description><![CDATA[Blueprint &#8212; упрощает организацию путей на Flask. Этот материал из цикла статей по разработке сайтов на python: от локальной разработки до развертывания на удаленном сервере. Упрощенный вид кода для работы сайта на Flask: Стандартный Flask from flask import Flask app...]]></description>
										<content:encoded><![CDATA[<p>Blueprint &#8212; упрощает организацию путей на Flask. <span id="more-6927"></span></p>
<p>Этот материал из цикла статей по разработке сайтов на python: <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">от локальной разработки до развертывания на удаленном сервере</a>.</p>
<p>Упрощенный вид кода для работы сайта на Flask:</p>
<h2>Стандартный Flask</h2>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> flask <span style="color: #ff7700;font-weight:bold;">import</span> Flask<br />
app <span style="color: #66cc66;">=</span> Flask<span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span><br />
app.<span style="color: black;">config</span>.<span style="color: black;">from_object</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'config.DevConfig'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Website routes</span><br />
<span style="color: #66cc66;">@</span>app.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> home<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;Home&quot;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Admin pages</span><br />
<span style="color: #66cc66;">@</span>app.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/portfolio'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> portfolio_home<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;portfolio Home&quot;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ <span style="color: #66cc66;">==</span> <span style="color: #483d8b;">&quot;__main__&quot;</span>:<br />
&nbsp; &nbsp; app.<span style="color: black;">run</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Отсюда нам интересны только <strong>роуты (route)</strong>. Мы можем эти роуты вынести в отдельный файл. Это нормальная практика даже для небольшого проекта. Однако что делать, если роутов слишком много?</p>
<p>Для этих целей подходит распределение роутов через <strong>Blueprint</strong>.</p>
<h2>Роуты через Blueprint</h2>
<p>В файле <strong>app/__init__.py</strong> подключаем несколько файлов <strong>routes.py</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Регистрация путей Blueprint</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">routes</span> <span style="color: #ff7700;font-weight:bold;">import</span> main_bp<br />
app.<span style="color: black;">register_blueprint</span><span style="color: black;">&#40;</span>main_bp<span style="color: #66cc66;">,</span> url_prefix<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;/&quot;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">admin</span>.<span style="color: black;">routes</span> <span style="color: #ff7700;font-weight:bold;">import</span> admin_bp<br />
app.<span style="color: black;">register_blueprint</span><span style="color: black;">&#40;</span>admin_bp<span style="color: #66cc66;">,</span> url_prefix<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;/admin&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>Как видно выше, у нас два файла <strong>routes.py</strong> в двух директориях. Для главной страницы это файл <strong>app/routes.py</strong>. Для страницы админки это файл <strong>app/admin/routes.py</strong>.</p>
<h3>app/routes.py</h3>
<p>Содержимое файла:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> flask<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask <span style="color: #ff7700;font-weight:bold;">import</span> Blueprint<span style="color: #66cc66;">,</span> render_template<br />
<br />
main_bp <span style="color: #66cc66;">=</span> Blueprint<span style="color: black;">&#40;</span><span style="color: #483d8b;">'main_blueprint'</span><span style="color: #66cc66;">,</span> __name__<span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># Include main page</span><br />
<span style="color: #808080; font-style: italic;"># Подключение главной страницы</span><br />
<span style="color: #66cc66;">@</span>main_bp.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> portfolio_home_route<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># return 'Сайт работает.'</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> flask.<span style="color: black;">redirect</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;/admin/&quot;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> render_template<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/admin/index.html'</span><span style="color: black;">&#41;</span></div></div>
<h3>app/portfolio/routes.py</h3>
<p>Содержимое файла:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">portfolio_bp <span style="color: #66cc66;">=</span> Blueprint<span style="color: black;">&#40;</span><span style="color: #483d8b;">'portfolio'</span><span style="color: #66cc66;">,</span> __name__<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Получение списка всех названий когда-либо купленных акций</span><br />
<span style="color: #66cc66;">@</span>portfolio_bp.<span style="color: black;">route</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'/get-user/'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'GET'</span><span style="color: black;">&#93;</span><span style="color: black;">&#41;</span><br />
<span style="color: #66cc66;">@</span>login_required<br />
<span style="color: #ff7700;font-weight:bold;">def</span> get_all_user_shares_route<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; query <span style="color: #66cc66;">=</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Portfolio.<span style="color: black;">name</span><span style="color: black;">&#41;</span>.<span style="color: black;">filter_by</span><span style="color: black;">&#40;</span>user_id<span style="color: #66cc66;">=</span>flask_login.<span style="color: black;">current_user</span>.<span style="color: #008000;">id</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; shares <span style="color: #66cc66;">=</span> <span style="color: black;">&#91;</span>x.<span style="color: black;">name</span> <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> query.<span style="color: #008000;">all</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#93;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> json.<span style="color: black;">dumps</span><span style="color: black;">&#40;</span>shares<span style="color: black;">&#41;</span></div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/registracija-putejj-cherez-blueprint/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flask &#8212; делаем авторизацию на сайте</title>
		<link>https://ploshadka.net/flask-delaem-avtorizaciju-na-sajjte/</link>
					<comments>https://ploshadka.net/flask-delaem-avtorizaciju-na-sajjte/#comments</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 10 Oct 2020 12:25:57 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[Основательные труды]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=6831</guid>

					<description><![CDATA[Есть много способов построить авторизацию и административную часть (личный кабинет) на Flask. Некоторые предполагают самостоятельно все строить, другие имеют уже готовые концепции. Статья входит в цикл статей по разработке на python. Перед тем как приступить к этой статье следует сделать...]]></description>
										<content:encoded><![CDATA[<p>Есть много способов построить авторизацию и административную часть (личный кабинет) на <strong>Flask</strong>. Некоторые предполагают самостоятельно все строить, другие имеют уже готовые концепции. <span id="more-6831"></span></p>
<p>Статья входит в <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">цикл статей по разработке на python</a>.</p>
<p>Перед тем как приступить к этой статье следует сделать всё что описано в статье <a href="https://ploshadka.net/python-delaem-prilozhenie-na-flask-v-lokalnojj-srede/">делаем приложение на Flask в локальной среде</a>.</p>
<h2>Введение</h2>
<p>Нет особого смысла делать с нуля то, что уже было сделано и оттестировано другими. Мы пойдем по проторенной дорожке и выберем готовые ингредиенты, которые останется только правильно смешать.</p>
<p><strong>Flask</strong> &#8212; это микрофреймворк, что предполагает создание проекта по модулям. </p>
<p>Ниже мы построим личный кабинет с авторизацией. </p>
<h2>Установка расширений</h2>
<p><strong>Flask-Login</strong> &#8212; расширение, которое обеспечит управление пользовательскими сеансами во <strong>Flask</strong>. Благодаря этому мы сможем входить и выходить из системы, ограничивать страницы авторизацией.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip install Flask-Login</div></div>
<p><strong>Flask-Admin</strong> &#8212; с помощью этого расширения мы сможем создать личный кабинет на подобии того, что есть в <strong>Django</strong>.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip install Flask-Admin</div></div>
<p><strong>Flask-WTF</strong> &#8212; расширение для Flask, являющееся оберткой <strong>WTForms</strong>, с помощью которого можно строить безопасные формы. </p>
<p>Также установим здесь <strong>email_validator</strong>, библиотеку для проверки эмейлов.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip install Flask-WTF<br />
pip install email_validator</div></div>
<p><strong>Flask-Security</strong> &#8212; разделение пользователей на роли.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip install Flask-Security</div></div>
<h2>Создаем модели для базы данных</h2>
<p>Создаем файл:<br />
<strong>app/models.py</strong></p>
<p>Внутри которого пишем:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:400px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">datetime</span> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">datetime</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_login <span style="color: #ff7700;font-weight:bold;">import</span> UserMixin<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_security <span style="color: #ff7700;font-weight:bold;">import</span> RoleMixin<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> db<span style="color: #66cc66;">,</span> login_manager<br />
<span style="color: #ff7700;font-weight:bold;">from</span> werkzeug.<span style="color: black;">security</span> <span style="color: #ff7700;font-weight:bold;">import</span> generate_password_hash<span style="color: #66cc66;">,</span> check_password_hash<br />
<br />
<br />
roles_users <span style="color: #66cc66;">=</span> db.<span style="color: black;">Table</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'roles_users'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'user_id'</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'users.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'role_id'</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> db.<span style="color: black;">ForeignKey</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'roles.id'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Role<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> RoleMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'roles'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">80</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; description <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__str__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">name</span><br />
<br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> User<span style="color: black;">&#40;</span>db.<span style="color: black;">Model</span><span style="color: #66cc66;">,</span> UserMixin<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; __tablename__ <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'users'</span><br />
&nbsp; &nbsp; <span style="color: #008000;">id</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Integer</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: #66cc66;">,</span> primary_key<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; name <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; username <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #dc143c;">email</span> <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: #66cc66;">,</span> unique<span style="color: #66cc66;">=</span><span style="color: #008000;">True</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; password <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">String</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; created_on <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">DateTime</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> default<span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; updated_on <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">DateTime</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> default<span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: #66cc66;">,</span> onupdate<span style="color: #66cc66;">=</span><span style="color: #dc143c;">datetime</span>.<span style="color: black;">utcnow</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Нужен для security!</span><br />
&nbsp; &nbsp; active <span style="color: #66cc66;">=</span> db.<span style="color: black;">Column</span><span style="color: black;">&#40;</span>db.<span style="color: black;">Boolean</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Для получения доступа к связанным объектам</span><br />
&nbsp; &nbsp; roles <span style="color: #66cc66;">=</span> db.<span style="color: black;">relationship</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'Role'</span><span style="color: #66cc66;">,</span> secondary<span style="color: #66cc66;">=</span>roles_users<span style="color: #66cc66;">,</span> backref<span style="color: #66cc66;">=</span>db.<span style="color: black;">backref</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'users'</span><span style="color: #66cc66;">,</span> lazy<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'dynamic'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Flask - Login</span><br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> is_authenticated<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> is_active<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">True</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span><span style="color: #008000;">property</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> is_anonymous<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">False</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Flask-Security</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> has_role<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> *args<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">set</span><span style="color: black;">&#40;</span>args<span style="color: black;">&#41;</span>.<span style="color: black;">issubset</span><span style="color: black;">&#40;</span><span style="color: black;">&#123;</span>role.<span style="color: black;">name</span> <span style="color: #ff7700;font-weight:bold;">for</span> role <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">self</span>.<span style="color: black;">roles</span><span style="color: black;">&#125;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> get_id<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: #008000;">id</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Required for administrative interface</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__unicode__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">username</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> set_password<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> password<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #008000;">self</span>.<span style="color: black;">password</span> <span style="color: #66cc66;">=</span> generate_password_hash<span style="color: black;">&#40;</span>password<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> check_password<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> password<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> check_password_hash<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">password</span><span style="color: #66cc66;">,</span> password<span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># Отвечает за сессию пользователей. Запрещает доступ к роутам, перед которыми указано @login_required</span><br />
<span style="color: #66cc66;">@</span>login_manager.<span style="color: black;">user_loader</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> load_user<span style="color: black;">&#40;</span>user_id<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> db.<span style="color: black;">session</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>User<span style="color: black;">&#41;</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span>user_id<span style="color: black;">&#41;</span></div></div>
<p>Разберем код. </p>
<p><strong>class Role</strong> &#8212; класс ролей связанная с таблицей <strong>roles</strong>.<br />
<strong>class User</strong> &#8212; класс пользователей связанный с таблицей <strong>users</strong>.<br />
<strong>roles_users = db.Table</strong> &#8212; создание связей между этими двумя таблицами, значение которых будет записываться в новую таблицу <strong>roles_users</strong>.</p>
<p><strong>@property</strong> в виде <strong>is_authenticated</strong>, <strong>is_active</strong>, <strong>is_anonymous</strong> нужны будут для определения авторизованных, активных и анонимных пользователей.</p>
<p><strong>@login_required</strong> &#8212; пригодится позже для разделения показа страниц на авторизованных и не авторизованных пользователей.</p>
<p>Другие дополнительные участки кода тоже пригодятся или могут пригодиться в дальнейшем.</p>
<p>Запускаем миграцию этих таблиц:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">flask db init<br />
flask db migrate -m <span style="color: #483d8b;">&quot;Initial migration.&quot;</span><br />
flask db upgrade</div></div>
<p>На этом этапе скорее всего покажет ошибку, что бд нет. В этом случае команды миграции можно пропустить. Мы их запустим когда будем создавать базу данных. </p>
<p>Подробнее о <a href="https://ploshadka.net/sqlalchemy-v-svjazke-s-flask/">миграции таблиц в SQLAlchemy</a>.</p>
<h2>Добавляем модуль admin</h2>
<p>Создаём директорию <strong>admin</strong> в директории <strong>app</strong> со следующими файлами:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/admin-module.jpg" rel="lightbox-0"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/admin-module.jpg" alt="" width="150" height="80" class="aligncenter size-full wp-image-6845" /></a></p>
<p>Содержимое файла <strong>__init__.py</strong>:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:400px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> db<span style="color: #66cc66;">,</span> app<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask <span style="color: #ff7700;font-weight:bold;">import</span> url_for<span style="color: #66cc66;">,</span> redirect<span style="color: #66cc66;">,</span> request<span style="color: #66cc66;">,</span> abort<br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> User<span style="color: #66cc66;">,</span> Role<br />
<br />
<span style="color: #808080; font-style: italic;"># flask-login</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_login <span style="color: #ff7700;font-weight:bold;">import</span> current_user<br />
<span style="color: #ff7700;font-weight:bold;">import</span> flask_login <span style="color: #ff7700;font-weight:bold;">as</span> login<br />
<br />
<span style="color: #808080; font-style: italic;"># flask-security</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_security <span style="color: #ff7700;font-weight:bold;">import</span> SQLAlchemyUserDatastore<span style="color: #66cc66;">,</span> Security<br />
<br />
<span style="color: #808080; font-style: italic;"># flask-admin</span><br />
<span style="color: #ff7700;font-weight:bold;">import</span> flask_admin<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_admin <span style="color: #ff7700;font-weight:bold;">import</span> helpers<span style="color: #66cc66;">,</span> expose<br />
<span style="color: #ff7700;font-weight:bold;">from</span> flask_admin.<span style="color: black;">contrib</span> <span style="color: #ff7700;font-weight:bold;">import</span> sqla<br />
<br />
<span style="color: #808080; font-style: italic;"># Setup Flask-Security</span><br />
user_datastore <span style="color: #66cc66;">=</span> SQLAlchemyUserDatastore<span style="color: black;">&#40;</span>db<span style="color: #66cc66;">,</span> User<span style="color: #66cc66;">,</span> Role<span style="color: black;">&#41;</span><br />
security <span style="color: #66cc66;">=</span> Security<span style="color: black;">&#40;</span>app<span style="color: #66cc66;">,</span> user_datastore<span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># Create customized model view class</span><br />
<span style="color: #ff7700;font-weight:bold;">class</span> MyModelView<span style="color: black;">&#40;</span>sqla.<span style="color: black;">ModelView</span><span style="color: black;">&#41;</span>:<br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> is_accessible<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#40;</span>current_user.<span style="color: black;">is_active</span> <span style="color: #ff7700;font-weight:bold;">and</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current_user.<span style="color: black;">is_authenticated</span> <span style="color: #ff7700;font-weight:bold;">and</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; current_user.<span style="color: black;">has_role</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'admin'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> _handle_view<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: #66cc66;">,</span> name<span style="color: #66cc66;">,</span> **kwargs<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">&quot;&quot;&quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; Override builtin _handle_view in order to redirect users when a view is not accessible.<br />
&nbsp; &nbsp; &nbsp; &nbsp; &quot;&quot;&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>.<span style="color: black;">is_accessible</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> current_user.<span style="color: black;">is_authenticated</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># permission denied</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; abort<span style="color: black;">&#40;</span><span style="color: #ff4500;">403</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">else</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> redirect<span style="color: black;">&#40;</span>url_for<span style="color: black;">&#40;</span><span style="color: #483d8b;">'security.login'</span><span style="color: #66cc66;">,</span> next<span style="color: #66cc66;">=</span>request.<span style="color: black;">url</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># Переадресация страниц (используется в шаблонах)</span><br />
<span style="color: #ff7700;font-weight:bold;">class</span> MyAdminIndexView<span style="color: black;">&#40;</span>flask_admin.<span style="color: black;">AdminIndexView</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span>expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> index<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> current_user.<span style="color: black;">is_authenticated</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> redirect<span style="color: black;">&#40;</span>url_for<span style="color: black;">&#40;</span><span style="color: #483d8b;">'.login_page'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>MyAdminIndexView<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">index</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span>expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/login/'</span><span style="color: #66cc66;">,</span> methods<span style="color: #66cc66;">=</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'GET'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'POST'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> login_page<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">if</span> current_user.<span style="color: black;">is_authenticated</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> redirect<span style="color: black;">&#40;</span>url_for<span style="color: black;">&#40;</span><span style="color: #483d8b;">'.index'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>MyAdminIndexView<span style="color: #66cc66;">,</span> <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: black;">index</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span>expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/logout/'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> logout_page<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; login.<span style="color: black;">logout_user</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> redirect<span style="color: black;">&#40;</span>url_for<span style="color: black;">&#40;</span><span style="color: #483d8b;">'.index'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">@</span>expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">'/reset/'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">def</span> reset_page<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> redirect<span style="color: black;">&#40;</span>url_for<span style="color: black;">&#40;</span><span style="color: #483d8b;">'.index'</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># Create admin</span><br />
admin <span style="color: #66cc66;">=</span> flask_admin.<span style="color: black;">Admin</span><span style="color: black;">&#40;</span>app<span style="color: #66cc66;">,</span> index_view<span style="color: #66cc66;">=</span>MyAdminIndexView<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">,</span> base_template<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'admin/master-extended.html'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Add view</span><br />
admin.<span style="color: black;">add_view</span><span style="color: black;">&#40;</span>MyModelView<span style="color: black;">&#40;</span>User<span style="color: #66cc66;">,</span> db.<span style="color: black;">session</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;"># define a context processor for merging flask-admin's template context into the</span><br />
<span style="color: #808080; font-style: italic;"># flask-security views.</span><br />
<span style="color: #66cc66;">@</span>security.<span style="color: black;">context_processor</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> security_context_processor<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; admin_base_template<span style="color: #66cc66;">=</span>admin.<span style="color: black;">base_template</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; admin_view<span style="color: #66cc66;">=</span>admin.<span style="color: black;">index_view</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; h<span style="color: #66cc66;">=</span>helpers<span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; get_url<span style="color: #66cc66;">=</span>url_for<br />
&nbsp; &nbsp; <span style="color: black;">&#41;</span></div></div>
<h2>Создаем шаблоны для административной части</h2>
<h3>templates/admin</h3>
<p>Создаем внутри директории <strong>templates</strong> директорию <strong>admin</strong>. В этой директории добавляем файлы.</p>
<h4>master-extended.html</h4>
<p>Файлом <strong>master-extended.html</strong> мы расширяем админ панель. В конкретно данном случае добавляем кнопку выхода на админ панель справа:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/admin.jpg" rel="lightbox-1"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/admin-600x46.jpg" alt="" width="600" height="46" class="aligncenter size-medium wp-image-6841" srcset="https://ploshadka.net/wp-content/uploads/6831/admin-600x46.jpg 600w, https://ploshadka.net/wp-content/uploads/6831/admin-1200x92.jpg 1200w, https://ploshadka.net/wp-content/uploads/6831/admin-300x23.jpg 300w, https://ploshadka.net/wp-content/uploads/6831/admin.jpg 1490w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Содержимое файла:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#123;</span>% extends <span style="color: #483d8b;">'admin/base.html'</span> %<span style="color: black;">&#125;</span><br />
<span style="color: black;">&#123;</span>% block access_control %<span style="color: black;">&#125;</span><br />
<br />
<span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">if</span> current_user.<span style="color: black;">is_authenticated</span> %<span style="color: black;">&#125;</span><br />
<span style="color: #66cc66;">&lt;</span>div <span style="color: #ff7700;font-weight:bold;">class</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;btn-group pull-right&quot;</span><span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>a <span style="color: #ff7700;font-weight:bold;">class</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;btn&quot;</span> href<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;{{ url_for('admin.logout_page') }}&quot;</span><span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> current_user.<span style="color: black;">username</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span> - Log out<span style="color: #66cc66;">&lt;</span>/a<span style="color: #66cc66;">&gt;</span><br />
<span style="color: #66cc66;">&lt;</span>/div<span style="color: #66cc66;">&gt;</span><br />
<span style="color: black;">&#123;</span>% endif %<span style="color: black;">&#125;</span><br />
<span style="color: black;">&#123;</span>% endblock %<span style="color: black;">&#125;</span></div></div>
<h4>index.html</h4>
<p>Файл <strong>index.html</strong> у нас выступит формой входа:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/file-index.jpg" rel="lightbox-2"><img decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/file-index-465x400.jpg" alt="" width="465" height="400" class="aligncenter size-medium wp-image-6843" srcset="https://ploshadka.net/wp-content/uploads/6831/file-index-465x400.jpg 465w, https://ploshadka.net/wp-content/uploads/6831/file-index-929x800.jpg 929w, https://ploshadka.net/wp-content/uploads/6831/file-index-232x200.jpg 232w, https://ploshadka.net/wp-content/uploads/6831/file-index.jpg 1034w" sizes="(max-width: 465px) 100vw, 465px" /></a></p>
<p>Содержимое файла может быть таким:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: black;">&#123;</span>% extends <span style="color: #483d8b;">'admin/master.html'</span> %<span style="color: black;">&#125;</span><br />
<span style="color: black;">&#123;</span>% block body %<span style="color: black;">&#125;</span><br />
<span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> <span style="color: #008000;">super</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><br />
<span style="color: #66cc66;">&lt;</span>div <span style="color: #ff7700;font-weight:bold;">class</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;row-fluid&quot;</span><span style="color: #66cc66;">&gt;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>div<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">if</span> current_user.<span style="color: black;">is_authenticated</span> %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>div<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>h2<span style="color: #66cc66;">&gt;</span>Logged <span style="color: #ff7700;font-weight:bold;">in</span> User details<span style="color: #66cc66;">&lt;</span>/h2<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>ul<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>li<span style="color: #66cc66;">&gt;</span>Username: <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> current_user.<span style="color: black;">username</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/li<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>li<span style="color: #66cc66;">&gt;</span>Email: <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> current_user.<span style="color: #dc143c;">email</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/li<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>li<span style="color: #66cc66;">&gt;</span>Created on: <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> current_user.<span style="color: black;">created_on</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/li<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>li<span style="color: #66cc66;">&gt;</span>Updated on: <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> current_user.<span style="color: black;">updated_on</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/li<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>/ul<span style="color: #66cc66;">&gt;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>h2<span style="color: #66cc66;">&gt;</span>Основные ваши настройки<span style="color: #66cc66;">&lt;</span>/h2<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>p <span style="color: #ff7700;font-weight:bold;">class</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;lead&quot;</span><span style="color: #66cc66;">&gt;</span>Вы можете:<span style="color: #66cc66;">&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>p<span style="color: #66cc66;">&gt;&lt;</span>a href<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;/admin/change/&quot;</span><span style="color: #66cc66;">&gt;</span>Изменить пароль<span style="color: #66cc66;">&lt;</span>/a<span style="color: #66cc66;">&gt;&lt;</span>/p<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>/div<span style="color: #66cc66;">&gt;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">else</span> %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>form method<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;POST&quot;</span> action<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;&quot;</span><span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> form.<span style="color: black;">hidden_tag</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">if</span> form.<span style="color: black;">hidden_tag</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">for</span> f <span style="color: #ff7700;font-weight:bold;">in</span> form <span style="color: #ff7700;font-weight:bold;">if</span> f.<span style="color: #008000;">type</span> <span style="color: #66cc66;">!=</span> <span style="color: #483d8b;">'CSRFTokenField'</span> %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>div<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> f.<span style="color: black;">label</span> <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> f <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">if</span> f.<span style="color: black;">errors</span> %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>ul<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% <span style="color: #ff7700;font-weight:bold;">for</span> e <span style="color: #ff7700;font-weight:bold;">in</span> f.<span style="color: black;">errors</span> %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>li<span style="color: #66cc66;">&gt;</span><span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> e <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">&lt;</span>/li<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% endfor %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>/ul<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% endif %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>/div<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% endfor %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>button <span style="color: #ff7700;font-weight:bold;">class</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;btn&quot;</span> <span style="color: #008000;">type</span><span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;submit&quot;</span><span style="color: #66cc66;">&gt;</span>Submit<span style="color: #66cc66;">&lt;</span>/button<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>/form<span style="color: #66cc66;">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span><span style="color: black;">&#123;</span> link | safe <span style="color: black;">&#125;</span><span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: black;">&#123;</span>% endif %<span style="color: black;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #66cc66;">&lt;</span>/div<span style="color: #66cc66;">&gt;</span><br />
<br />
<span style="color: #66cc66;">&lt;</span>/div<span style="color: #66cc66;">&gt;</span><br />
<span style="color: black;">&#123;</span>% endblock body %<span style="color: black;">&#125;</span></div></div>
<h3>templates/security</h3>
<p>Когда было установлено расширение <strong>Flask-Security</strong> в директории <strong>templates</strong> появилась директория <strong>security</strong>, а в ней следующие файлы:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/directory-security.jpg" rel="lightbox-3"><img decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/directory-security-387x400.jpg" alt="" width="387" height="400" class="aligncenter size-medium wp-image-6848" srcset="https://ploshadka.net/wp-content/uploads/6831/directory-security-387x400.jpg 387w, https://ploshadka.net/wp-content/uploads/6831/directory-security-193x200.jpg 193w, https://ploshadka.net/wp-content/uploads/6831/directory-security.jpg 472w" sizes="(max-width: 387px) 100vw, 387px" /></a></p>
<div class="highlight">Файлы могли и не появиться после установки. Тогда их можно перенести вручную. Путь указан ниже.</div>
<p>Подробно на них останавливаться не будем. Скажу лишь, что эти файлы переопределяют шаблоны расширения Flask-Security. </p>
<p>Шаблоны, которые они расширяют находятся здесь:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">/venv/lib/python3.8/site-packages/flask_security/templates/security</div></div>
<p>Если какой-то из шаблонов хочется поменять, его нужно скопировать оттуда к себе в директорию security. И дальше можно его менять.</p>
<h2>Config</h2>
<p>Для того, чтобы все работало нам нужно добавить данные в файл конфига <strong>config-extended</strong>. К этому моменты вы должны были ознакомиться с <a href="https://ploshadka.net/flask-konfiguracionnye-fajjly/">конфигурационными файлами Flask</a>.</p>
<p>Содержимое конфига:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;">################</span><br />
<span style="color: #808080; font-style: italic;"># Flask-Security</span><br />
<span style="color: #808080; font-style: italic;">################</span><br />
<br />
<span style="color: #808080; font-style: italic;"># URLs</span><br />
SECURITY_URL_PREFIX <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin&quot;</span><br />
SECURITY_LOGIN_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/login/&quot;</span><br />
SECURITY_LOGOUT_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/logout/&quot;</span><br />
SECURITY_POST_LOGIN_VIEW <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin/&quot;</span><br />
SECURITY_POST_LOGOUT_VIEW <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin/&quot;</span><br />
SECURITY_POST_REGISTER_VIEW <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin/&quot;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Включает регистрацию</span><br />
SECURITY_REGISTERABLE <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
SECURITY_REGISTER_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/register/&quot;</span><br />
SECURITY_SEND_REGISTER_EMAIL <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Включет сброс пароля</span><br />
SECURITY_RECOVERABLE <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
SECURITY_RESET_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/reset/&quot;</span><br />
SECURITY_SEND_PASSWORD_RESET_EMAIL <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Включает изменение пароля</span><br />
SECURITY_CHANGEABLE <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
SECURITY_CHANGE_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/change/&quot;</span><br />
SECURITY_SEND_PASSWORD_CHANGE_EMAIL <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span></div></div>
<p>С другими директивами можно ознакомиться <a href="https://pythonhosted.org/Flask-Security/configuration.html" rel="noopener noreferrer" target="_blank">в официальном источнике</a>.</p>
<h2>Blueprint</h2>
<p>Последнее что нам нужно сделать для работы админки добавить регистрацию путей через <strong>Blueprint</strong>. <a href="https://ploshadka.net/registracija-putejj-cherez-blueprint/">Зачем нужен Blueprint и его настройка</a>.</p>
<p>В файл <strong>app/__init__.py</strong> нужно добавить:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Регистрация путей Blueprint</span><br />
<span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">admin</span>.<span style="color: black;">routes</span> <span style="color: #ff7700;font-weight:bold;">import</span> admin_bp<br />
app.<span style="color: black;">register_blueprint</span><span style="color: black;">&#40;</span>admin_bp<span style="color: #66cc66;">,</span> url_prefix<span style="color: #66cc66;">=</span><span style="color: #483d8b;">&quot;/admin&quot;</span><span style="color: black;">&#41;</span></div></div>
<p>Может быть вы обратили внимание, что на скрине выше в модуле admin есть файл <strong>routes.py</strong>.</p>
<p>Если мы строим большой и расширяемый проект, то у нас в каждом модуле, где требуются роуты, будет свой файл routes.py с путями.</p>
<p>В файл routes.py надо добавить:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> flask <span style="color: #ff7700;font-weight:bold;">import</span> Blueprint<br />
admin_bp <span style="color: #66cc66;">=</span> Blueprint<span style="color: black;">&#40;</span><span style="color: #483d8b;">'admin_blueprint'</span><span style="color: #66cc66;">,</span> __name__<span style="color: black;">&#41;</span></div></div>
<p>В этом файле на текущий момент больше ничего не нужно. Все наши роуты для админки прописаны в файле <strong>admin/__init__.py</strong> там где класс <strong>class MyAdminIndexView</strong>, но именно потому что они идут немного в другом формате и переплетены с функционалом. Чтобы все не усложнять мы оставим как есть, но практику применения роутов добавим.</p>
<h2>Вход в админку</h2>
<p>Теперь у нас есть админка. Заходим по адресу:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">http:<span style="color: #000000; font-weight: bold;">//</span>127.0.0.1:<span style="color: #000000;">5000</span><span style="color: #000000; font-weight: bold;">/</span>admin<span style="color: #000000; font-weight: bold;">/</span>login<span style="color: #000000; font-weight: bold;">/</span></div></div>
<p>Регистрируемся на свой эмейл и заходим внутрь. Наша админка выглядит так (пока видны не все вкладки, мы дадим доступ к ним ниже):</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/admin-page2.jpg" rel="lightbox-4"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/admin-page2-600x355.jpg" alt="" width="600" height="355" class="aligncenter size-medium wp-image-6855" srcset="https://ploshadka.net/wp-content/uploads/6831/admin-page2-600x355.jpg 600w, https://ploshadka.net/wp-content/uploads/6831/admin-page2-1200x711.jpg 1200w, https://ploshadka.net/wp-content/uploads/6831/admin-page2-300x178.jpg 300w, https://ploshadka.net/wp-content/uploads/6831/admin-page2.jpg 1492w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Отдельные вкладки для неё мы можем добавлять в файле __init__.py. Например, вот как мы можем добавить новую вкладку для работы с таблицей UserSetting в базе данных (предварительно для неё должна быть создана модель в файле models.py, по инструкции выше):</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Add view</span><br />
admin.<span style="color: black;">add_view</span><span style="color: black;">&#40;</span>MyModelView<span style="color: black;">&#40;</span>User<span style="color: #66cc66;">,</span> db.<span style="color: black;">session</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span><br />
admin.<span style="color: black;">add_view</span><span style="color: black;">&#40;</span>MyModelView<span style="color: black;">&#40;</span>UserSetting<span style="color: #66cc66;">,</span> db.<span style="color: black;">session</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></div></div>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/admin-page1.jpg" rel="lightbox-5"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/admin-page1-600x305.jpg" alt="" width="600" height="305" class="aligncenter size-medium wp-image-6856" srcset="https://ploshadka.net/wp-content/uploads/6831/admin-page1-600x305.jpg 600w, https://ploshadka.net/wp-content/uploads/6831/admin-page1-1200x610.jpg 1200w, https://ploshadka.net/wp-content/uploads/6831/admin-page1-300x153.jpg 300w, https://ploshadka.net/wp-content/uploads/6831/admin-page1.jpg 1506w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Добавим роли для админки. Заходим к себе в БД. Как зайти в <a href="https://ploshadka.net/ustanovka-i-podkljuchenie-postgresql-na-mac-os/">локальную ДБ через DataGrip</a> или <a href="https://ploshadka.net/postgresql-v-ubuntu-vneshnee-podkljuchenie-k-baze-dannykh/">соединяемся удаленно с базой данных</a>. </p>
<p>В таблице <strong>role</strong> добавим данные:<br />
<a href="https://ploshadka.net/wp-content/uploads/6831/role.jpg" rel="lightbox-6"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/role-600x103.jpg" alt="" width="600" height="103" class="aligncenter size-medium wp-image-6872" srcset="https://ploshadka.net/wp-content/uploads/6831/role-600x103.jpg 600w, https://ploshadka.net/wp-content/uploads/6831/role-300x52.jpg 300w, https://ploshadka.net/wp-content/uploads/6831/role.jpg 790w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>И добавим нашему пользователю роль администратора в таблице <strong>roles_users</strong>:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6831/role-add.jpg" rel="lightbox-7"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6831/role-add.jpg" alt="" width="576" height="96" class="aligncenter size-full wp-image-6873" srcset="https://ploshadka.net/wp-content/uploads/6831/role-add.jpg 576w, https://ploshadka.net/wp-content/uploads/6831/role-add-300x50.jpg 300w" sizes="(max-width: 576px) 100vw, 576px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/flask-delaem-avtorizaciju-na-sajjte/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Flask &#8212; конфигурационные файлы</title>
		<link>https://ploshadka.net/flask-konfiguracionnye-fajjly/</link>
					<comments>https://ploshadka.net/flask-konfiguracionnye-fajjly/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sat, 10 Oct 2020 07:57:38 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=6812</guid>

					<description><![CDATA[Несколько способов использования различных конфигураций настроек сервера для Flask. Вариант 1: как в официальной доке С советами в официальной части можно ознакомиться здесь. В официальной инструкции, помимо прочего, дают рекомендации делать через смену конфигураций: def create_app&#40;&#41;: &#160; &#160; app =...]]></description>
										<content:encoded><![CDATA[<p>Несколько способов использования различных конфигураций настроек сервера для Flask. <span id="more-6812"></span></p>
<h2>Вариант 1: как в официальной доке</h2>
<p>С советами в официальной части можно ознакомиться <a href="https://flask.palletsprojects.com/en/1.1.x/config/">здесь</a>.</p>
<p>В официальной инструкции, помимо прочего, дают рекомендации делать через смену конфигураций:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app <span style="color: #66cc66;">=</span> Flask<span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># app.config.from_object('config.DevelopmentConfig')</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_object</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'config.ProductionConfig'</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> app</div></div>
<p>Минус такого подхода в заливке. Перед отправкой изменений на сервер придется постоянно комментировать/раскомментировать код.</p>
<h2>Вариант 2: делим конфиги на две части</h2>
<p>Разделить конфиги на 2 части:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app <span style="color: #66cc66;">=</span> Flask<span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_object</span><span style="color: black;">&#40;</span>Config<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../config-extended.py'</span><span style="color: black;">&#41;</span><br />
<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> app</div></div>
<p>ploshadka.net/<strong>config.py</strong> &#8212; этот файл нужно создать самостоятельно на сервере, потому что там важная секретная информация. И такой же создаем у себя на локальном компьютере. А если нужно, также и на тестовом.</p>
<p>ploshadka.net/<strong>config-extended.py</strong> &#8212; этот файл будет у нас отправляться в git и здесь ничего важного с точки зрения безопасности.</p>
<p>Теперь в зависимости от нахождения кода будет подтягиваться разная конфигурация сервера.</p>
<p><a href="https://ploshadka.net/wp-content/uploads/6812/migrations.jpg" rel="lightbox-0"><img decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/6812/migrations-285x400.jpg" alt="" width="285" height="400" class="aligncenter size-medium wp-image-6815" srcset="https://ploshadka.net/wp-content/uploads/6812/migrations-285x400.jpg 285w, https://ploshadka.net/wp-content/uploads/6812/migrations-143x200.jpg 143w, https://ploshadka.net/wp-content/uploads/6812/migrations.jpg 374w" sizes="(max-width: 285px) 100vw, 285px" /></a></p>
<p>Содержимое файла <strong>config.py</strong> может быть таким:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Config<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; DEBUG <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
&nbsp; &nbsp; SECRET_KEY <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">environ</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'SECRET_KEY'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #483d8b;">'fsdkfd32r234fsdf'</span><br />
&nbsp; &nbsp; SQLALCHEMY_DATABASE_URI <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'postgresql://localhost/db_name'</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">################</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Flask-Security</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">################</span><br />
<br />
&nbsp; &nbsp; SECURITY_PASSWORD_HASH <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;pbkdf2_sha512&quot;</span><br />
&nbsp; &nbsp; SECURITY_PASSWORD_SALT <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;fsdfdfsdfdfsdafds&quot;</span></div></div>
<p>На сервере тот же файл:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">class</span> Config<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; DEBUG <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><br />
&nbsp; &nbsp; SECRET_KEY <span style="color: #66cc66;">=</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">environ</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'SECRET_KEY'</span><span style="color: black;">&#41;</span> <span style="color: #ff7700;font-weight:bold;">or</span> <span style="color: #483d8b;">'fsDFKsmf923jnagd'</span><br />
&nbsp; &nbsp; SQLALCHEMY_DATABASE_URI <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'postgresql://user:pass/db_name'</span><br />
&nbsp; &nbsp; SQLALCHEMY_TRACK_MODIFICATIONS <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><br />
<br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">################</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;"># Flask-Security</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">################</span><br />
<br />
&nbsp; &nbsp; SECURITY_PASSWORD_HASH <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;pbkdf2_sha512&quot;</span><br />
&nbsp; &nbsp; SECURITY_PASSWORD_SALT <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;fsdfk3fsdDFFdfsr0fsa&quot;</span></div></div>
<p>Следует отметить, что такие данные как <strong>SECRET_KEY</strong>, <strong>SECURITY_PASSWORD_SALT</strong> или другие подобные можно еще более безопасно хранить в переменных окружения. Но это тема отдельной статьи и тут разбираться не будет.</p>
<p>Содержимое файла <strong>config-extended.py</strong> может быть таким:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;">##########</span><br />
<span style="color: #808080; font-style: italic;"># Features</span><br />
<span style="color: #808080; font-style: italic;">##########</span><br />
<br />
SQLALCHEMY_TRACK_MODIFICATIONS <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><br />
<br />
<br />
<span style="color: #808080; font-style: italic;">################</span><br />
<span style="color: #808080; font-style: italic;"># Flask-Security</span><br />
<span style="color: #808080; font-style: italic;"># https://pythonhosted.org/Flask-Security/configuration.html</span><br />
<span style="color: #808080; font-style: italic;">################</span><br />
<br />
<span style="color: #808080; font-style: italic;"># URLs</span><br />
SECURITY_URL_PREFIX <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin&quot;</span><br />
SECURITY_LOGIN_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/login/&quot;</span><br />
SECURITY_LOGOUT_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/logout/&quot;</span><br />
SECURITY_POST_LOGIN_VIEW <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin/&quot;</span><br />
SECURITY_POST_LOGOUT_VIEW <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin/&quot;</span><br />
SECURITY_POST_REGISTER_VIEW <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/admin/&quot;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Включает регистрацию</span><br />
SECURITY_REGISTERABLE <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
SECURITY_REGISTER_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/register/&quot;</span><br />
SECURITY_SEND_REGISTER_EMAIL <span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Включет сброс пароля</span><br />
SECURITY_RECOVERABLE <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
SECURITY_RESET_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/reset/&quot;</span><br />
SECURITY_SEND_PASSWORD_RESET_EMAIL <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Включает изменение пароля</span><br />
SECURITY_CHANGEABLE <span style="color: #66cc66;">=</span> <span style="color: #008000;">True</span><br />
SECURITY_CHANGE_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">&quot;/change/&quot;</span><br />
SECURITY_SEND_PASSWORD_CHANGE_EMAIL &nbsp;<span style="color: #66cc66;">=</span> <span style="color: #008000;">False</span><br />
<br />
<span style="color: #808080; font-style: italic;"># todo не работает отправка email</span><br />
<span style="color: #808080; font-style: italic;"># AttributeError: 'NoneType' object has no attribute 'send'</span></div></div>
<h2>Вариант 3: разделяем конфиги на много частей</h2>
<p>С расширением проекта увеличивается кол-во конфигов. Для порядка выносим все конфиги в отдельную директорию <strong>configs</strong>. В поддиректории private храним конфиги в которых содержимое отличается от продакшена и локальной разработки. Эти конфиги добавляем в файл <a href="https://ploshadka.net/bystraja-integracija-git/">.gitignore</a> и вручную создаем на сервере.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app <span style="color: #66cc66;">=</span> Flask<span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/private/main.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/private/celery.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/sqlalchemy.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/flask-security.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/beatschedule.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> app</div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/flask-konfiguracionnye-fajjly/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
