<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Danil Poletavkin</title>
    <description>The latest articles on Forem by Danil Poletavkin (@danilpoletavkin).</description>
    <link>https://forem.com/danilpoletavkin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F692936%2F4fa95c74-2e2c-4b93-b59b-0f5e2e0c4bac.png</url>
      <title>Forem: Danil Poletavkin</title>
      <link>https://forem.com/danilpoletavkin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danilpoletavkin"/>
    <language>en</language>
    <item>
      <title>Создание своего собственного оператора в RxJS</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Wed, 12 Apr 2023 03:41:49 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/sozdaniie-svoiegho-sobstviennogho-opieratora-v-rxjs-3dje</link>
      <guid>https://forem.com/danilpoletavkin/sozdaniie-svoiegho-sobstviennogho-opieratora-v-rxjs-3dje</guid>
      <description>&lt;p&gt;Это вольный перевод очень хорошей статьи&lt;br&gt;
&lt;a href="https://netbasal.com/creating-custom-operators-in-rxjs-32f052d69457"&gt;https://netbasal.com/creating-custom-operators-in-rxjs-32f052d69457&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;В этом материале вместо слова &lt;em&gt;наблюдаемый (Observable)&lt;/em&gt; используется слово &lt;em&gt;издатель&lt;/em&gt;, а вместо слова &lt;em&gt;наблюдатель (Observer)&lt;/em&gt; - слово &lt;em&gt;подписчик.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Что такое оператор? Это издатель, который принимает на вход другого&lt;br&gt;
издателя. Простейший оператор будет выглядеть так:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function myOperator&amp;lt;T&amp;gt;(source:Observable&amp;lt;T&amp;gt;){
return source;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Этот оператор бесполезен, так как возвращает (или передаёт далее по&lt;br&gt;
цепочке) того же издателя, что и получает, но тем не менее это&lt;br&gt;
оператор!&lt;/p&gt;

&lt;p&gt;Теперь давайте попробуем вернуть какого-нибудь другого издателя:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function myOperator&amp;lt;T&amp;gt;(source: Observable&amp;lt;T&amp;gt;){
return new Observable(subscriber=&amp;gt;{
subscriber.next(1);
subscriber.complete();
})
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Этот оператор никак не использует входного издателя, а просто возвращает единицу.&lt;br&gt;&lt;br&gt;
Теперь попробуем создать оператор, который будет отфильтровывать&lt;br&gt;
значения &lt;code&gt;null&lt;/code&gt; и &lt;code&gt;undefined&lt;/code&gt; и не будет пускать их дальше по цепи.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function filterNil() {
    return function &amp;lt;T&amp;gt;(sourse: Observable&amp;lt;T&amp;gt;) {
        return new Observable(s =&amp;gt; {
            sourse.subscribe(
                {
                    next: (v) =&amp;gt; {
                        if (v!==undefined&amp;amp;&amp;amp;v!==null) {
                            s.next(v);
                        }
                    },
                    error: (error) =&amp;gt; {
                        s.error();
                    },
                    complete: () =&amp;gt; {
                        s.complete();
                    }
                }
            )
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Почему мы обернули оператор в ещё одну функцию? Это для того, чтобы было&lt;br&gt;
удобно передавать оператору какие-то аргументы. Теперь эта функция&lt;br&gt;
возвращает оператор и вызов функции равносилен применению оператора.&lt;/p&gt;

&lt;p&gt;Но что-то не так - мы только что создали утечку памяти. Каждый&lt;br&gt;
издатель должен возвращать функцию &lt;code&gt;unsubscribe()&lt;/code&gt;, которая выполняет все&lt;br&gt;
необходимые по очистке памяти действия, а издатель в нашем операторе&lt;br&gt;
такую функцию не возвращает. Для того, чтобы всё заработало как надо&lt;br&gt;
добавим ещё одну строку&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function filterNil() {
    return function &amp;lt;T&amp;gt;(sourse: Observable&amp;lt;T&amp;gt;) {
        return new Observable(s =&amp;gt; {
            sourse.subscribe(
                {
                    next: (v) =&amp;gt; {
                        if (v!==undefined&amp;amp;&amp;amp;v!==null) {
                            s.next(v);
                        }
                    },
                    error: (error) =&amp;gt; {
                        s.error();
                    },
                    complete: () =&amp;gt; {
                        s.complete();
                    }
                }
            )
            return () =&amp;gt; s.unsubscribe(); &amp;lt;===
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Конструкция &lt;code&gt;Observable.subscribe()&lt;/code&gt; возвращает объект &lt;code&gt;Subscription&lt;/code&gt;,&lt;br&gt;
поэтому можно сократить код, показанный выше просто возвратив сам объект&lt;br&gt;
подписки. Теперь при вызове &lt;code&gt;unsubscribe()&lt;/code&gt; будет вызываться метод&lt;br&gt;
&lt;code&gt;unsubscribe()&lt;/code&gt; подписки, которую мы возвращаем.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function filterNil() {
    return function &amp;lt;T&amp;gt;(sourse: Observable&amp;lt;T&amp;gt;) {
        return new Observable(s =&amp;gt; {
            return sourse.subscribe(
                {
                    next: (v) =&amp;gt; {
                        if (v!==undefined&amp;amp;&amp;amp;v!==null) {
                            s.next(v);
                        }
                    },
                    error: (error) =&amp;gt; {
                        s.error();
                    },
                    complete: () =&amp;gt; {
                        s.complete();
                    }
                }
            )
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Рекомендуется создавать свои операторы на основе существующих и вот как&lt;br&gt;
будет выглядеть оператор &lt;code&gt;filterNil&lt;/code&gt;, созданный при помощи оператора&lt;br&gt;
&lt;code&gt;filter&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function filterNil() {
    return function &amp;lt;T&amp;gt;(source: Observable&amp;lt;T&amp;gt;) {
        return source.pipe(filter(v=&amp;gt;v!==undefined &amp;amp;&amp;amp; v!=null));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;А так как операторы функции блока &lt;code&gt;pipe&lt;/code&gt; сами по себе уже возвращают функции, то&lt;br&gt;
есть оператор &lt;code&gt;filter&lt;/code&gt; уже вернёт нужную нам функцию, принимающую и&lt;br&gt;
возвращающую издателя, то можно сократить код до&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function filterNil() {
return filter(v=&amp;gt;v!==undefined&amp;amp;&amp;amp;v!=null);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Строки и строки байтов в Python</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Tue, 07 Mar 2023 03:48:59 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/stroki-i-stroki-baitov-v-python-35jp</link>
      <guid>https://forem.com/danilpoletavkin/stroki-i-stroki-baitov-v-python-35jp</guid>
      <description>&lt;p&gt;Строка - это набор символов, а строка байтов - это именно это и есть - строка байтов. Обычная строка перед тем, как станет возможным сохранить её на компьютере должна быть преобразована (&lt;strong&gt;encoding&lt;/strong&gt;) в строку байтов.&lt;/p&gt;

&lt;p&gt;Обычные строки - это наборы символов на каком-то человеческом языке, они читаются и понимаются, строки байтов - могут быть представлены в &lt;em&gt;двоичном&lt;/em&gt; или &lt;em&gt;шестнадцатеричном&lt;/em&gt; (или каком-то другом) формате и людьми обычно не понимаются.&lt;/p&gt;

&lt;p&gt;Тем не менее в &lt;em&gt;Python&lt;/em&gt; если какой-то переменной присвоить строку байтов &lt;strong&gt;b'string'&lt;/strong&gt; и вывести в консоль, то &lt;em&gt;Python&lt;/em&gt; представит строку байтов как строку доступную для чтения.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s=b'string'
print(s)
# Вывод: b'string'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Здесь дело в том, что перед выводом на консоль команда &lt;strong&gt;print&lt;/strong&gt; преобразовывает строку байтов в формат, удобный для чтения, то есть в обычную строку. На самом деле это будет по-прежнему строка байтов, а не набор символов как каком-то из международных языков. К примеру можно представить набор символов строки &lt;strong&gt;s&lt;/strong&gt; в шестнадцатеричном формате&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c=s.hex()
print(s)
# Вывод: 737472696e67
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Использован ответ пользователя stackoverflow.com&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/31322359/12613870"&gt;https://stackoverflow.com/a/31322359/12613870&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Answer: What is the difference between a string and a byte string?</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Mon, 06 Mar 2023 05:00:19 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/answer-what-is-the-difference-between-a-string-and-a-byte-string-140n</link>
      <guid>https://forem.com/danilpoletavkin/answer-what-is-the-difference-between-a-string-and-a-byte-string-140n</guid>
      <description>&lt;div class="ltag__stackexchange--container"&gt;
  &lt;div class="ltag__stackexchange--title-container"&gt;
    
      &lt;div class="ltag__stackexchange--title"&gt;
        &lt;div class="ltag__stackexchange--header"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Gn-iPj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackoverflow-logo-b42691ae545e4810b105ee957979a853a696085e67e43ee14c5699cf3e890fb4.svg" alt=""&gt;
          &lt;a href="https://stackoverflow.com/questions/6224052/what-is-the-difference-between-a-string-and-a-byte-string/31322359#31322359" rel="noopener noreferrer"&gt;
            &lt;span class="title-flare"&gt;answer&lt;/span&gt; re: What is the difference between a string and a byte string?
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="ltag__stackexchange--post-metadata"&gt;
          &lt;span&gt;Jul  9 '15&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;a class="ltag__stackexchange--score-container" href="https://stackoverflow.com/questions/6224052/what-is-the-difference-between-a-string-and-a-byte-string/31322359#31322359" rel="noopener noreferrer"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9mJpuJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackexchange-arrow-up-eff2e2849e67d156181d258e38802c0b57fa011f74164a7f97675ca3b6ab756b.svg" alt=""&gt;
        &lt;div class="ltag__stackexchange--score-number"&gt;
          679
        &lt;/div&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wif5Zq3z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackexchange-arrow-down-4349fac0dd932d284fab7e4dd9846f19a3710558efde0d2dfd05897f3eeb9aba.svg" alt=""&gt;
      &lt;/a&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--body"&gt;
    
&lt;p&gt;The only thing that a computer can store is bytes.&lt;/p&gt;
&lt;p&gt;To store anything in a computer, you must first &lt;em&gt;encode&lt;/em&gt; it, i.e. convert it to bytes. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want to store music, you must first &lt;em&gt;encode&lt;/em&gt; it using &lt;a href="https://en.wikipedia.org/wiki/MP3" rel="noreferrer"&gt;MP3&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/WAV" rel="noreferrer"&gt;WAV&lt;/a&gt;, etc.&lt;/li&gt;
&lt;li&gt;If you want to store…&lt;/li&gt;
&lt;/ul&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--btn--container"&gt;
    &lt;a href="https://stackoverflow.com/questions/6224052/what-is-the-difference-between-a-string-and-a-byte-string/31322359#31322359" class="ltag__stackexchange--btn" rel="noopener noreferrer"&gt;Open Full Answer&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Django. Работа через WebSocket. Библиотека Channels</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Thu, 16 Feb 2023 05:30:18 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/django-rabota-chieriez-websocket-bibliotieka-channels-2pcg</link>
      <guid>https://forem.com/danilpoletavkin/django-rabota-chieriez-websocket-bibliotieka-channels-2pcg</guid>
      <description>&lt;p&gt;Ссылка на документацию &lt;a href="https://channels.readthedocs.io/" rel="noopener noreferrer"&gt;https://channels.readthedocs.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ссылка на проект github &lt;a href="https://github.com/django/channels" rel="noopener noreferrer"&gt;https://github.com/django/channels&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Библиотека позволяет выполнять соединение с клиентской стороной как через http, так и через websocket. Подключая библиотеку к Django соединения будут выполняться через сервер-стандарт asgi, так как библиотека переопределит стандартное поведение Django. В остальном же функциональность остаётся та же самая, как обычно, через http можно вызывать контроллеры из файла views.py.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Классы-Потребители (Consumers)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Это специальный класс — потребитель сообщений. Принято располагать эти классы в модуле consumers.py.&lt;/p&gt;

&lt;p&gt;Эти классы обрабатывают сообщения по протоколам отличным от http, например для работы с websocket класс создаётся следующим образом:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from channels.generic.websocket import WebsocketConsumer

class SimpleWsConsumer(WebsocketConsumer):
    pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В отличие от стандартной конфигурации при работе через http, когда маршруты располагаются в модуле urls.py при работе через websocket маршруты принято располагать в модуле routing.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.urls import re_path
from . import consumers

websocket_urlpatterns=[
    re_path(r’ws/chat/(?P&amp;lt;room_name&amp;gt;\w+)/$’, consumers.SimpleWsConsumer.as_asgi()),
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Как видим, у класса-контроллера вместо обычного вызова as_view() вызывается метод as_asgi(), в остальном модуль маршрутов похож на стандартный urls.py&lt;/p&gt;

&lt;p&gt;Подключаются же такие маршруты в модуле asgi.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
from chat import routing # Импортируется модуль routing.py, где как раз и настроены маршруты websocket

os.environ.setdefault('DJANGO_SETTINGS_MODULE','mysite.settings')
application=ProtocolTypeRouter({
    "http":get_asgi_application(),
    "websocket":AllowedHostsOriginValidator(AuthMiddlewareStack(URLRouter(routing.websockets_urlpatterns)) # Передаётся список маршрутов из модуля routing.py)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Подключение коммуникационной системы потребителей. Слой Каналов (Channel layer)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Канал — это как путь, через который направляются сообщения. Для каждой вкладки веб-обозревателя создаётся свой канал. Каждому потребителю по умолчанию назначается уникальное имя канала channel_name, то есть если создать 10 вкладок (или приложение используют 10 разных пользователей), то будет создано 10 каналов. Наверное можно думать о канале как о канале соединения websocket. Каналы могут объединяться в группы, и тогда каналы, принадлежащие одной группе могут общаться между собой&lt;/p&gt;

&lt;p&gt;Группа — это группа связанных каланов. Имея имя группы можно отправлять сообщения через все каналы этой группы. И, насколько понимаю, для каждого потребителя создаётся свой собственный канал с уникальным именем. Получается, что каналы объединяются в группы вместе со своими потребителями.&lt;/p&gt;

&lt;p&gt;То есть можно сказать, что канал — это путь к потребителю, по которому идут сообщения, а потребитель — конечная точка для этих сообщений. Потребитель может принять сообщение, обработать его и отправить ответ.&lt;/p&gt;

&lt;p&gt;Коммуникационная система работает через redis — нужно запустить redis и добавить пакет channels_redis&lt;/p&gt;

&lt;p&gt;pip install channels_redis&lt;/p&gt;

&lt;p&gt;И в модуле настроек settings.py коммуникационную систему нужно сконфигурировать&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ASGI_APPLICATION='mysite.asgi.application'

CHANNEL_LAYERS={
    'default':{
        'BACKEND':'channels_redis.core.RedisChannelLayer',
        'CONFIG':{
            'hosts':[
                ('127.0.0.1',6379)
            ]
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Добавление потребителя в группу&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Потребителя можно добавить в группу методом channel_layer.group_add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer

class SimpleWsConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name=self.scope['url_route'][^1]['kwargs']['room_name']
        self.room_group_name='chat_%s' % self.room_name
        # Добавление потребителя в группу
        async_to_sync(self.channel_layer.group_add)[^2](
            self.room_group_name, self.channel_name # Параметры метода channel_layer.group_add
        )
    self.accept() # Принять соединение

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>discuss</category>
      <category>intranet</category>
      <category>workplace</category>
    </item>
    <item>
      <title>Git Как изменить любой коммит</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Thu, 22 Dec 2022 03:05:53 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/kak-izmienit-liuboi-kommit-45o8</link>
      <guid>https://forem.com/danilpoletavkin/kak-izmienit-liuboi-kommit-45o8</guid>
      <description>&lt;p&gt;Для того, чтобы изменить любой коммит, нужно использовать команду &lt;code&gt;git rebase&lt;/code&gt;. Важно, что при этом все изменения, которые планируется внести в коммит должны быть скрыты командой &lt;code&gt;stash&lt;/code&gt;, то есть вывод команды &lt;code&gt;git status&lt;/code&gt; должен быть пустой:&lt;br&gt;
1.Допустим в области изменений git есть какие-то файлы, которые мы хотим добавить к коммиту командой &lt;code&gt;git rebase&lt;/code&gt;, и есть файлы, которые добавлять не планируется.&lt;br&gt;
В таком случае нужно скрыть изменения поочерёдно командой &lt;code&gt;git stash push [относительный путь к файлу]&lt;/code&gt;.&lt;br&gt;
Показать список скрытых файлов можно командой &lt;code&gt;git stash list&lt;/code&gt;.&lt;br&gt;
Команда &lt;code&gt;git stash&lt;/code&gt; сохраняет записи по методу стэка, то есть последние добавленные записи имеют меньший индекс, а самая последняя добавленная запись имеет индекс 0.&lt;br&gt;
Хорошая статья про команду &lt;code&gt;git stash&lt;/code&gt; &lt;a href="https://tangenttechnologies.ca/blog/git-stash-single-file/"&gt;https://tangenttechnologies.ca/blog/git-stash-single-file/&lt;/a&gt;&lt;br&gt;
2.Выполнить команду&lt;br&gt;
&lt;code&gt;git rebase —interactive [номер коммита^]&lt;/code&gt;&lt;br&gt;
обратить внимание на символ ^ - это означает, что мы перемещаемся в состояние «до» этого коммита&lt;br&gt;
3.В открывшемся файле или консольном текстовом редакторе (в зависимости от того, какой редактор настроен для работой с git) с коммитами указать, какой коммит мы хотим изменить — вместо &lt;code&gt;pick&lt;/code&gt; написать &lt;code&gt;edit&lt;/code&gt;&lt;br&gt;
4.Выполнить изменения, например добавить файлы к коммиту. Если изменения находятся в состоянии stash, выполнить команду&lt;br&gt;
&lt;code&gt;git stash pop&lt;/code&gt;&lt;br&gt;
или&lt;br&gt;
&lt;code&gt;git stash pop [номер изменения]&lt;/code&gt;&lt;br&gt;
5.Выполнить команду&lt;br&gt;
&lt;code&gt;git commit —amend&lt;/code&gt;&lt;br&gt;
6.Выполнить команду&lt;br&gt;
&lt;code&gt;git rebase —continue&lt;/code&gt;&lt;br&gt;
7.При необходимости перезаписать изменения на сервере командой&lt;br&gt;
&lt;code&gt;git push --force&lt;/code&gt;&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Выполнение интеграционных тестов ASP.NET Core в пайплайне GitLab</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Thu, 15 Dec 2022 05:50:36 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/vypolnieniie-intieghratsionnykh-tiestov-aspnet-core-v-paiplainie-gitlab-4nm8</link>
      <guid>https://forem.com/danilpoletavkin/vypolnieniie-intieghratsionnykh-tiestov-aspnet-core-v-paiplainie-gitlab-4nm8</guid>
      <description>&lt;p&gt;Для того, чтобы запустить в пайплайне интеграционные тесты ASP.NET Core, нужно&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Создать отдельный файл &lt;strong&gt;docker-compose-test.yml&lt;/strong&gt; с конфигурацией развёртывания базы данных. Этот файл не будет использоваться при основном развёртывании, а только при выполнении тестов. &lt;/li&gt;
&lt;li&gt;Запустить контейнер &lt;strong&gt;dotnet/sdk&lt;/strong&gt;, связать директорию в контейнере с директорией приложения (вероятно это текущая директория), назначить в качестве рабочей директории докера &lt;strong&gt;workdir&lt;/strong&gt; директорию с файлами тестов и выполнить команду &lt;strong&gt;dotnet test&lt;/strong&gt;. При этом переопределить строку подключения к базе данных, так как по умолчанию используется строка подключения из файла &lt;strong&gt;appsettings.json&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Выполнить команду &lt;strong&gt;docker-compose down&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;На что нужно обратить внимание:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;В интеграционных тестах класс с методами тестов обычно наследуется от &lt;strong&gt;WebApplicationFactory&lt;/strong&gt; или &lt;strong&gt;WebApplicationFactory&lt;/strong&gt;, и при создании класса с тестами выполняются методы класса &lt;strong&gt;Startup (Program)&lt;/strong&gt;. Если классов с тестами несколько, то методы класса Startup будут выполняться каждый раз, и если в нём определены операции миграции базы данных, будет возникать ошибка. Причина ошибки в том, что каждый класс с тестами запускается как бы отдельно и «приложение не знает», какие миграции уже были применены к базе данных до этого. Получается, что все миграции применяются каждый раз. 
Чтобы этого избежать, можно поместить все тесты в один класс, так, чтобы создавался только один класс &lt;strong&gt;WebApplicationFactory&lt;/strong&gt; и соответственно методы класса &lt;strong&gt;Startup&lt;/strong&gt; выполнялись только один раз &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Примеры кода
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Интеграционный тест
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.AspNetCore.Mvc.Testing;

public class IntegrationTests:IClassFixture&amp;lt;WebApplicationFactory&amp;lt;Startup&amp;gt;&amp;gt;{
    private readonly WebApplicationFactory&amp;lt;Startup&amp;gt; _fixture;
    public IntegrationTests(WebApplicationFactory&amp;lt;Startup&amp;gt; fixture)
    {
        _fixture = fixture;
    }
    [Fact]
    public void Test1(){}
    [Fact]
    public void Test2(){}
    [Fact]
    public void Test3(){}
    [Fact]
    public void Test4(){}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Файл docker-compose-test.yml с образом postgres
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.4'
networks: 
  network_name:
    driver: bridge
services:
  db:
   image: postgres:14
   ports:
    - "5432:5432"
   environment:
    PGUSER: postgres
    POSTGRES_PASSWORD: postgres
    POSTGRES_DB: postgres
   networks:
    - network_name
   restart: always
   healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 20s
      timeout: 10s
      retries: 3
      start_period: 10s  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Джоб в конфигурации .gitlab-ci.yml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stages:
  - test
  - build
  - deploy

docker-test:
  image: docker:latest
  stage: test
  when: on_success
  services:
    - docker:dind
  script:
# развернуть контейнер с базой данных 
# -d - отменить вывод журнала в консоль для того, чтобы остальные команды могли выполниться
    - docker-compose --file docker-compose-test.yml up -d --build
# --name - задать имя контейнеру (необязательно)
# --network - задать имя сети, с которой будет соединяться контейнер. При развёртывании контейнера с базой данных в предыдущем шаге создаётся сеть с именем network_name. При развёртывании контейнера с базой данных к этому имени добавится имя текущей директории, из которой была выполнена команда docker-compose, так что окончательное имя сети будет не таким, как указано в файле docker-compose-test, а с префиксом из имени текущей директории и нижним подчёркиванием: [имя текущей директории]_network_name. Нужно иметь это ввиду при запуске команды.
# --rm - удалить образ, который используется при развёртывании контейнера а также удалить связанные volume
# -w - задать рабочую директорию для контейнера, то есть ту директорию, в которой будут выполняться дальнейшие команды. Это то же самое, что команда WORKDIR в файлах Dockerfile. Здесь мы в качестве рабочей директории задаём директорию Tests, так как именно в ней нужно будет выполнить команду dotnet test
# dotnet test - команда запуска тестов
# -e задать переменную текущей среды выполнения. Здесь мы задаём строку соединения с базой данных.
# в строке соединения к базе данных:
# __ - двойное подчёркивание используется вместо двоеточия :, так как двоеточие не всегда срабатывает
# Server=db - здесь db - это имя службы из участка кода
# services:
# db:
# image: postgres:14          
    - docker run --name app_name --network [имя текущей директории]_network_name --rm -v $(pwd):/app -w /app/Tests mcr.microsoft.com/dotnet/sdk:7.0 dotnet test -e "ConnectionStrings__DefaultConnection=Server=db;Port=5432;Database=postgres;Username=postgres;Password=postgres"
    - docker-compose --file docker-compose-test.yml down -v

docker-build:
  stage: build
# …
docker-deploy:
  stage: deploy
# …
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>gitlab</category>
      <category>aspnetcore</category>
      <category>testing</category>
    </item>
    <item>
      <title>Docker Volume Backup &amp; Restore</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Tue, 04 Oct 2022 14:28:14 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/docker-volume-backup-restore-2bde</link>
      <guid>https://forem.com/danilpoletavkin/docker-volume-backup-restore-2bde</guid>
      <description>&lt;h1&gt;
  
  
  Backup
&lt;/h1&gt;

&lt;p&gt;Перейти в папку, в которой мы ожидаем появления архива и выполнить следующую команду&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm --volume [имя volume, который мы хотим архивировать]:/data --volume $(pwd):/backup ubuntu tar cvf /backup/backup.tar /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Пояснение
&lt;/h3&gt;

&lt;p&gt;1) Запустить контейнер&lt;br&gt;
&lt;code&gt;docker run&lt;/code&gt;&lt;br&gt;
2) &lt;strong&gt;image&lt;/strong&gt; будет удалён после запуска контейнера&lt;br&gt;
&lt;code&gt;--rm&lt;/code&gt; &lt;br&gt;
3) монтировать нужный нам &lt;strong&gt;volume&lt;/strong&gt; в папку &lt;strong&gt;data&lt;/strong&gt; внутри создаваемого контейнера&lt;br&gt;
имя этой папки может быть любым&lt;br&gt;
&lt;code&gt;--volume [имя volume, который мы хотим архивировать]:/data&lt;/code&gt;&lt;br&gt;
4) связать текущую папку &lt;strong&gt;$(pwd)&lt;/strong&gt; с папкой &lt;strong&gt;backup&lt;/strong&gt; внутри создаваемого контейнера&lt;br&gt;
текущая папка - это место, где мы ожидаем появления архива &lt;strong&gt;backup.tar&lt;/strong&gt;&lt;br&gt;
имя папки &lt;strong&gt;backup&lt;/strong&gt; может быть любым&lt;br&gt;
то есть данные из папки &lt;strong&gt;backup&lt;/strong&gt;, расположенной внутри контейнера будут появляться в текущей папке&lt;br&gt;
&lt;code&gt;--volume $(pwd):/backup&lt;/code&gt;&lt;br&gt;
5) имя &lt;strong&gt;image&lt;/strong&gt; контейнера, который создаётся&lt;br&gt;
не обязательно использовать именно &lt;strong&gt;ubuntu&lt;/strong&gt;, это может быть любой образ&lt;br&gt;
нам всего лишь нужно скопировать данные из существующего &lt;strong&gt;volume&lt;/strong&gt;, где располагаются наши данные в новый &lt;strong&gt;volume&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ubuntu&lt;/code&gt;&lt;br&gt;
6) архивировать то, что находится в папке &lt;strong&gt;data&lt;/strong&gt; в архив c именем &lt;strong&gt;backup.tar&lt;/strong&gt;&lt;br&gt;
архив &lt;strong&gt;backup.tar&lt;/strong&gt; появляется в папке &lt;strong&gt;backup&lt;/strong&gt; внутри контейнера, и, так как мы связали в команде 4 папку &lt;strong&gt;backup&lt;/strong&gt; внутри контейнера с текущей директорией, файл &lt;strong&gt;backup.tar&lt;/strong&gt; появится в текущей директории&lt;br&gt;
&lt;code&gt;tar cvf /backup/backup.tar /data&lt;/code&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Restore
&lt;/h1&gt;

&lt;p&gt;Перейти в папку, в которой находится файл &lt;strong&gt;.tar&lt;/strong&gt; с резервной копией базы данных и выполнить следующую команду&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --rm --volume some_docker_project-backup:/extracted_backup --volume $(pwd):/data ubuntu tar xvf /data/backup.tar -C /extracted_backup --strip 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Пояснение
&lt;/h3&gt;

&lt;p&gt;1) Запустить контейнер&lt;br&gt;
&lt;code&gt;docker run&lt;/code&gt;&lt;br&gt;
2) &lt;strong&gt;image&lt;/strong&gt; будет удалён после запуска контейнера&lt;br&gt;
&lt;code&gt;--rm&lt;/code&gt; &lt;br&gt;
3) связать новый &lt;strong&gt;volume&lt;/strong&gt; &lt;strong&gt;some_docker_project-backup&lt;/strong&gt; с папкой &lt;strong&gt;extracted_backup&lt;/strong&gt; внутри контейнера&lt;br&gt;
новый &lt;strong&gt;volume&lt;/strong&gt; - это тот, который потом планируется использовать в качестве источника данных в файле &lt;strong&gt;docker-compose&lt;/strong&gt;. В файле &lt;strong&gt;docker-compose&lt;/strong&gt; имя &lt;strong&gt;volume&lt;/strong&gt; указывается без префикса &lt;a href="https://dev.to%D0%B2%20%D1%8D%D1%82%D0%BE%D0%BC%20%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D0%B5%20%D1%8D%D1%82%D0%BE%20**some_docker_project**"&gt;имя проекта&lt;/a&gt;, а имя проекта добавляется в процессе выполнения команды &lt;strong&gt;docker-compose up&lt;/strong&gt; (для смены имени проекта по-умолчанию нужно использовать параметр &lt;strong&gt;--project-name&lt;/strong&gt;)&lt;br&gt;
поэтому здесь имя нового volume включает в себя префикс &lt;strong&gt;some_docker_project&lt;/strong&gt; - это как раз имя проекта &lt;strong&gt;docker-compose&lt;/strong&gt;&lt;br&gt;
папка &lt;strong&gt;extracted_backup&lt;/strong&gt; - это папка внутри контейнера, имя папки может быть любым&lt;br&gt;
&lt;code&gt;--volume some_docker_project-backup:/extracted_backup&lt;/code&gt;&lt;br&gt;
4) связать текущую папку с папкой &lt;strong&gt;data&lt;/strong&gt; внутри контейнера.&lt;br&gt;
текущая папка - это та папка, в которой находится архив &lt;strong&gt;.tar&lt;/strong&gt; с резервной копией базы данных&lt;br&gt;
&lt;code&gt;--volume $(pwd):/data&lt;/code&gt;&lt;br&gt;
5) имя запускаемого образа&lt;br&gt;
не обязательно использовать именно &lt;strong&gt;ubuntu&lt;/strong&gt;, это может быть любой образ&lt;br&gt;
&lt;code&gt;ubuntu&lt;/code&gt;&lt;br&gt;
6) разархивировать файл &lt;strong&gt;.tar&lt;/strong&gt; в папку &lt;strong&gt;extracted_backup&lt;/strong&gt; внутри контейнера, которую ранее мы связывали с новым &lt;strong&gt;volume&lt;/strong&gt;. В результате появится новый &lt;strong&gt;volume&lt;/strong&gt; с данными из архива, которые были извлечены в папку &lt;strong&gt;extracted_backup&lt;/strong&gt; внутри контейнера&lt;br&gt;
&lt;code&gt;tar xvf /data/backup.tar -C /extracted_backup --strip 1&lt;/code&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>volumes</category>
    </item>
    <item>
      <title>Инструкция по развёртыванию приложения Django Python3 на веб сервере Apache для операционной системы CentOS 7</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Tue, 26 Jul 2022 05:03:00 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/instruktsiia-po-razviortyvaniiu-prilozhieniia-django-python3-na-vieb-siervierie-apache-dlia-opieratsionnoi-sistiemy-centos-7-28m2</link>
      <guid>https://forem.com/danilpoletavkin/instruktsiia-po-razviortyvaniiu-prilozhieniia-django-python3-na-vieb-siervierie-apache-dlia-opieratsionnoi-sistiemy-centos-7-28m2</guid>
      <description>&lt;p&gt;Инструкция написана начинающим пользователем систем &lt;em&gt;Linux&lt;/em&gt; и неспециалистом по программам &lt;em&gt;Apache&lt;/em&gt; и &lt;em&gt;Django&lt;/em&gt;&lt;br&gt;
В данной инструкции использовались следующие версии пакетов:&lt;br&gt;
python3-pip-9.0.3-8.el7&lt;br&gt;
httpd-2.4.6-97.el7.centos.5&lt;br&gt;
python3-mod_wsgi-4.7.1-2.el7&lt;br&gt;
django 2.1.13&lt;/p&gt;

&lt;h2&gt;
  
  
  Замечания
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Изменить уровень служебных сообщений журнала в файле &lt;em&gt;httpd.conf&lt;/em&gt; на &lt;em&gt;LogLevel info&lt;/em&gt; чтобы видеть ошибки&lt;/li&gt;
&lt;li&gt;Важно, чтобы у всех необходимых для &lt;em&gt;apache&lt;/em&gt; директорий был доступ &lt;em&gt;o+rx&lt;/em&gt;. Раз доступ есть у пользователей не относящихся к группе, добавлять пользователя &lt;em&gt;apache&lt;/em&gt; в какую-либо группу не нужно (например в руководстве &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-serve-django-applications-with-apache-and-mod_wsgi-on-centos-7"&gt;How To Serve Django Applications with Apache and mod_wsgi on CentOS 7 | DigitalOcean&lt;/a&gt; излишне указано, что пользователь &lt;em&gt;apache&lt;/em&gt; должен быть добавлен в группу пользователя системы)&lt;/li&gt;
&lt;li&gt;Служба &lt;em&gt;SELinux&lt;/em&gt; может блокировать доступ &lt;em&gt;apache&lt;/em&gt; к необходимым файлам. Один из вариантов отключить &lt;em&gt;SELinux&lt;/em&gt;, а лучше настроить её для работы с &lt;em&gt;apache&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Внимательно подойти к созданию файла конфигурации &lt;em&gt;[имя файла].conf.&lt;/em&gt; Рабочий пример с комментариями &lt;a href="https://yadi.sk/d/XnMw3Xfv1ra59A"&gt;django.conf&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Убедиться, что версия &lt;em&gt;mod_wsgi&lt;/em&gt; соответствует версии &lt;em&gt;python&lt;/em&gt; при помощи которой создаётся проект &lt;em&gt;django&lt;/em&gt; и виртуальная среда. Например для версии &lt;em&gt;Python3.x&lt;/em&gt; это будет &lt;em&gt;mod_wsgi.4.x.x&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Порядок действий
&lt;/h2&gt;

&lt;p&gt;Все команды рекомендуется запускать из-под учётной записи пользователя, а не администратора. Далее все адреса к директориям и файлам указаны относительно домашней директории пользователя&lt;/p&gt;

&lt;h1&gt;
  
  
  Django
&lt;/h1&gt;

&lt;p&gt;1. Установить &lt;em&gt;python3-pip&lt;/em&gt;. Пакеты &lt;em&gt;python&lt;/em&gt; относятся к &lt;em&gt;python версии 2&lt;/em&gt;, а пакеты &lt;em&gt;python3&lt;/em&gt; - к &lt;em&gt;python версии 3&lt;/em&gt;&lt;br&gt;
&lt;code&gt;sudo yum install python3-pip&lt;/code&gt;&lt;br&gt;
 2. Установить пакет для создания виртуальных сред &lt;em&gt;virtualenv&lt;/em&gt;.&lt;br&gt;
&lt;code&gt;sudo pip3 install virtualenv&lt;/code&gt;&lt;br&gt;
 3. Находясь в директории пользователя создать виртуальную среду&lt;br&gt;
&lt;code&gt;virutalenv [имя среды, например 'venv']&lt;/code&gt;&lt;br&gt;
    Будет создана директория &lt;em&gt;venv&lt;/em&gt;&lt;br&gt;
 4. Активировать виртуальную среду&lt;br&gt;
&lt;code&gt;source venv/bin/activate&lt;/code&gt;&lt;br&gt;
 5. Установить &lt;em&gt;django&lt;/em&gt;. Так как это общий пример, будем использовать провайдера базы данных по умолчанию, это &lt;em&gt;sqlite&lt;/em&gt;. На момент написания инструкции при установке &lt;em&gt;django&lt;/em&gt; последней версии возникает ошибка о том, что установлена устаревшая версия &lt;em&gt;sqlite&lt;/em&gt; и требуется версия не ниже &lt;em&gt;3.8.x&lt;/em&gt;. Так как это ознакомительная инструкция, будем использовать версию &lt;em&gt;django 2.1.x&lt;/em&gt;, например &lt;em&gt;2.1.13&lt;/em&gt;. В реальном приложении вероятно, что будет использоваться другой провайдер базы данных.&lt;br&gt;
&lt;code&gt;pip3 install django==2.1.13&lt;/code&gt;&lt;br&gt;
 6. Создать новый проект django&lt;br&gt;
&lt;code&gt;django-admin startproject [имя проекта, например 'myproject']&lt;/code&gt;&lt;br&gt;
 7. В файле &lt;em&gt;myproject/myproject/settings.py&lt;/em&gt; указать, с каких адресов будет разрешён вызов приложения &lt;em&gt;django&lt;/em&gt;. Для примера разрешим вызов со всех адресов&lt;br&gt;
&lt;code&gt;ALLOWED_HOSTS = ['*']&lt;/code&gt;&lt;br&gt;
 8. Создадим и применим миграции базы данных&lt;br&gt;
&lt;code&gt;myproject/manage.py makemigrations&lt;/code&gt;&lt;br&gt;
&lt;code&gt;myproject/manage.py migrate&lt;/code&gt;&lt;br&gt;
 9. Запустим приложение, используя &lt;em&gt;порт 8000&lt;/em&gt;&lt;br&gt;
&lt;code&gt;myproject/manage.py runserver 0.0.0.0:8000&lt;/code&gt;&lt;br&gt;
10. Так как система без графического интерфейса, для того, чтобы посмотреть веб-страницу нашего &lt;em&gt;django-проекта&lt;/em&gt; используем программу &lt;em&gt;lynx&lt;/em&gt; с другого окна терминала&lt;br&gt;
&lt;code&gt;sudo yum install lynx&lt;/code&gt;&lt;br&gt;
&lt;code&gt;lynx&lt;/code&gt;&lt;a href="http://0.0.0.0:8000"&gt;&lt;code&gt;http://0.0.0.0:8000&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
11. Остановим приложение&lt;br&gt;
12. Этот шаг понадобится для доступа к приложению через &lt;em&gt;apache&lt;/em&gt;. Для того, чтобы &lt;em&gt;apache&lt;/em&gt; мог запустить наше приложение, понадобятся статические файлы, которые необходимо собрать с отдельную папку командой &lt;em&gt;collectstatic&lt;/em&gt;. Прежде, чем это сделать, нужно указать &lt;em&gt;django&lt;/em&gt; в какую именно папку собирать статические файлы. Для этого нужно добавить следующую строку в файл &lt;em&gt;myproject/myproject/settings.py&lt;/em&gt; &lt;code&gt;STATIC_ROOT=os.path.join(BASE_DIR,'static/')&lt;/code&gt;&lt;br&gt;
13. Теперь соберём статические файлы&lt;br&gt;
&lt;code&gt;myproject/manage.py collectstatic&lt;/code&gt;&lt;br&gt;
Важно запускать эту команду из активной виртуальной среды После выполнения команды в директории &lt;span&gt;&lt;em&gt;~&lt;/em&gt;&lt;/span&gt;&lt;em&gt;/myproject/&lt;/em&gt; появится новая папка &lt;em&gt;static&lt;/em&gt;&lt;br&gt;
14. Можно деактивировать виртуальную среду&lt;br&gt;
&lt;code&gt;deactivate&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Apache
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;В &lt;em&gt;CentOS&lt;/em&gt; главная служба &lt;em&gt;Apache&lt;/em&gt; называется &lt;em&gt;httpd&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;При установке по умолчанию главный файл конфигурации размещается по адресу &lt;em&gt;/etc/httpd/conf/httpd.conf&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Дополнительные файлы конфигурации, которые создаются для веб-приложений размещаются по адресу &lt;em&gt;/etc/httpd/conf.d/&lt;/em&gt;. Команды из этих файлов переопределяют команды главного файла &lt;em&gt;/etc/httpd/conf/httpd.conf&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Для того, чтобы видеть подробные сообщения об ошибках, в файле &lt;em&gt;httpd.conf&lt;/em&gt; нужно изменить уровень сообщений журнала с &lt;em&gt;LogLevel warn&lt;/em&gt; на &lt;em&gt;LogLevel info&lt;/em&gt;. В этом же файле указано, по какому адресу находится журнал с сообщениями об ошибках, по умолчанию это файл &lt;em&gt;/etc/httpd/logs/error_log&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1. Установить пакет apache&lt;br&gt;
&lt;code&gt;sudo yum install httpd&lt;/code&gt;&lt;br&gt;
 2. Проверим работу службы httpd.&lt;br&gt;
    Для этого сначала откроем &lt;em&gt;порт 80&lt;/em&gt; для внешних соединений по протоколу &lt;em&gt;tcp&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo firewall-cmd --permanent --add-port=80/tcp&lt;/code&gt;&lt;br&gt;
    включим службу &lt;em&gt;http файервола&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo firewall-cmd --permanent --add-service=http&lt;/code&gt;&lt;br&gt;
    перезапустим &lt;em&gt;файерволл&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo firewall-cmd --reload&lt;/code&gt;&lt;br&gt;
    теперь запустим &lt;em&gt;apache&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo systemctl start httpd&lt;/code&gt;&lt;br&gt;
    После этого при переходе по адресу сервера должна отобразиться приветственная страница&lt;br&gt;
 3. Установить пакет &lt;em&gt;mod_wsgi&lt;/em&gt;. &lt;em&gt;WSGI&lt;/em&gt; - это модуль, который позволяет &lt;em&gt;apache&lt;/em&gt; обрабатывать приложения &lt;em&gt;python&lt;/em&gt;. Как сказано в замечаниях, версия модуля должна соответствовать версии &lt;em&gt;python&lt;/em&gt;, при помощи которой создавался проект &lt;em&gt;django&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo yum install python3-mod_wsgi&lt;/code&gt;&lt;br&gt;
    Если появляется сообщение, что пакет не найден, то установить пакет &lt;em&gt;rh-python36-mod_wsgi&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo yum install rh-python36-mod_wsgi&lt;/code&gt;&lt;br&gt;
 4. Проверим, что модуль &lt;em&gt;mod_wsgi&lt;/em&gt; установился в папку модулей &lt;em&gt;apache&lt;/em&gt;.&lt;br&gt;
    &lt;code&gt;ls -l /usr/lib64/httpd/modules&lt;/code&gt;&lt;br&gt;
    В списке должен присутствовать файл &lt;em&gt;mod_wsgi_python3.so.&lt;/em&gt; Этот модуль &lt;em&gt;apache&lt;/em&gt; будет использовать при обработке конфигурационного файла для приложения &lt;em&gt;django&lt;/em&gt;, который мы создадим далее&lt;br&gt;
 5. Откроем доступ сторонним пользователям к папкам проекта, для того, чтобы &lt;em&gt;apache&lt;/em&gt; был способен использовать все необходимые файлы (реальный проект рекомендуется создавать в отдельной папке вне домашней директории пользователя)&lt;br&gt;
    &lt;code&gt;sudo chmod -R o+rx /home/[пользователь]/myproject/myproject/wsgi.py&lt;/code&gt;&lt;br&gt;
    &lt;code&gt;sudo chmod -R o+rx /home/[пользователь]/venv/lib/python3.6/site-packages&lt;/code&gt;&lt;br&gt;
    Чтобы проверить права доступа к папкам можно использовать команды&lt;br&gt;
    &lt;code&gt;namei -l /home/[пользователь]/myproject/myproject/wsgi.py&lt;/code&gt;&lt;br&gt;
    &lt;code&gt;namei -l /home/[пользователь]/venv/lib/python3.6/site-packages&lt;/code&gt;&lt;br&gt;
    Эта команда отобразит все папки по пути к конечному пункту. У всех директорий права доступа других пользователей должны быть &lt;em&gt;r-x&lt;/em&gt;. Если команда &lt;em&gt;chmod -R&lt;/em&gt; не сработала для директории &lt;em&gt;/home/[пользователь]&lt;/em&gt; то изменить права доступа отдельно для этой директории. Также проверить права доступа у файла &lt;a href="http://wsgi.py"&gt;&lt;em&gt;wsgi.py&lt;/em&gt;&lt;/a&gt;&lt;br&gt;
 6. Отключить &lt;em&gt;selinux&lt;/em&gt; (если знаете как, то лучше не отключать, а настроить для работы с &lt;em&gt;apache&lt;/em&gt;). Конфигурация &lt;em&gt;SELinux&lt;/em&gt; располагается в файле &lt;em&gt;/etc/selinux/config&lt;/em&gt;, в комментариях этого файла указано как отключить службу. Статус службы можно посмотреть командой&lt;br&gt;
    &lt;code&gt;sestatus&lt;/code&gt;&lt;br&gt;
 7. Перезагрузить компьютер чтобы изменения &lt;em&gt;SELinux&lt;/em&gt; вступили в силу&lt;br&gt;
    &lt;code&gt;sudo reboot&lt;/code&gt;&lt;br&gt;
    После перезагрузки команда &lt;em&gt;sestatus&lt;/em&gt; должна показывать &lt;em&gt;disabled&lt;/em&gt;&lt;br&gt;
 8. Очистить (удалить содержимое) файл &lt;em&gt;/etc/httpd/conf.d/welcome.conf&lt;/em&gt;. Этот файл используется &lt;em&gt;apache&lt;/em&gt; для отображения приветственной страницы&lt;br&gt;
 9. Создать файл &lt;em&gt;/etc/httpd/conf.d/[имя файла, например 'django'].conf&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo touch /etc/httpd/conf.d/django.conf&lt;/code&gt;&lt;br&gt;
    и вставить в него содержимое файла &lt;a href="https://yadi.sk/d/XnMw3Xfv1ra59A"&gt;django.conf&lt;/a&gt;. Заменить &lt;em&gt;[имя пользователя]&lt;/em&gt; на текущее[django.conf]&lt;br&gt;
10. Запустить службу &lt;em&gt;httpd&lt;/em&gt;&lt;br&gt;
    &lt;code&gt;sudo systemctl restart httpd&lt;/code&gt;&lt;br&gt;
После выполненных действий по адресу сервера должна отображаться приветственная страница &lt;em&gt;django&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Answer: ASP.NET Identity change password</title>
      <dc:creator>Danil Poletavkin</dc:creator>
      <pubDate>Wed, 25 Aug 2021 03:58:45 +0000</pubDate>
      <link>https://forem.com/danilpoletavkin/answer-asp-net-identity-change-password-1cf6</link>
      <guid>https://forem.com/danilpoletavkin/answer-asp-net-identity-change-password-1cf6</guid>
      <description>&lt;div class="ltag__stackexchange--container"&gt;
  &lt;div class="ltag__stackexchange--title-container"&gt;
    
      &lt;div class="ltag__stackexchange--title"&gt;
        &lt;div class="ltag__stackexchange--header"&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Gn-iPj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackoverflow-logo-b42691ae545e4810b105ee957979a853a696085e67e43ee14c5699cf3e890fb4.svg" alt=""&gt;
          &lt;a href="https://stackoverflow.com/questions/29291366/asp-net-identity-change-password/29291561#29291561" rel="noopener noreferrer"&gt;
            &lt;span class="title-flare"&gt;answer&lt;/span&gt; re: ASP.NET Identity change password
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="ltag__stackexchange--post-metadata"&gt;
          &lt;span&gt;Mar 27 '15&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;a class="ltag__stackexchange--score-container" href="https://stackoverflow.com/questions/29291366/asp-net-identity-change-password/29291561#29291561" rel="noopener noreferrer"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9mJpuJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackexchange-arrow-up-eff2e2849e67d156181d258e38802c0b57fa011f74164a7f97675ca3b6ab756b.svg" alt=""&gt;
        &lt;div class="ltag__stackexchange--score-number"&gt;
          44
        &lt;/div&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wif5Zq3z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackexchange-arrow-down-4349fac0dd932d284fab7e4dd9846f19a3710558efde0d2dfd05897f3eeb9aba.svg" alt=""&gt;
      &lt;/a&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--body"&gt;
    
&lt;p&gt;&lt;code&gt;ApplicationUserManager&lt;/code&gt; is the  class generated by the ASP.NET Template. &lt;/p&gt;
&lt;p&gt;Which means, you can edit it and add any functionality it doesn't have yet. The UserManager class has a protected property named &lt;code&gt;Store&lt;/code&gt; which stores a reference to the &lt;code&gt;UserStore&lt;/code&gt; class (or any subclass of it, depending on how you configured…&lt;/p&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--btn--container"&gt;
    &lt;a href="https://stackoverflow.com/questions/29291366/asp-net-identity-change-password/29291561#29291561" class="ltag__stackexchange--btn" rel="noopener noreferrer"&gt;Open Full Answer&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
  </channel>
</rss>
