Django 31. Форма авторизации и кнопка выхода
Продолжаем развивать проект.
В этом и нескольких следующих постах, мы добавим возможность пользователям регистрироваться, авторизовываться на сайте и выходить с него, сбрасывать свой пароль, а также реализуем страницу с профилем пользователя.
И начнём мы с создания нового приложения.
Приложение user_app.
В прошлом посте я упоминал о важности разделения разных логических блоков на Django-приложения. Поэтому для пользователей сделаем отдельное приложение
Для создания нового приложения выполните в терминале следующую команду:
Создастся новая директория с основными файлами.
Сразу откроем файл
Продолжаем развивать проект.
В этом и нескольких следующих постах, мы добавим возможность пользователям регистрироваться, авторизовываться на сайте и выходить с него, сбрасывать свой пароль, а также реализуем страницу с профилем пользователя.
И начнём мы с создания нового приложения.
Приложение user_app.
В прошлом посте я упоминал о важности разделения разных логических блоков на Django-приложения. Поэтому для пользователей сделаем отдельное приложение
user_app.Для создания нового приложения выполните в терминале следующую команду:
python manage.py startapp user_app
Создастся новая директория с основными файлами.
Сразу откроем файл
settings.py и добавим новое приложение в список INSTALLED_APPS - 'user_app.apps.UserAppConfig',Авторизация.
Форма авторизации.
Создадим форму авторизации.
На самом деле, делать этого не обязательно.
Можно воспользоваться встроенной формой, собственно от которой будем наследоваться, однако тут встаёт вопрос с её отображением. У стандартной формы отсутствуют стили.
Можно конечно их прописать с помощью CSS-селекторов, но у меня в данный момент используется Bootstrap5.
Для того чтобы воспользоваться его возможностями, необходимо форме определить конкретные стили, а для этого нужно её переопределить.
В директории приложения
Создадим класс
Затем создадим два поля:
Первое будет
Первым атрибутом будет максимальная длина имени пользователя. По умолчанию в модели пользователя максимальная длина составляет 150 символов, укажем столько же или можете по своему усмотрению указать меньше.
Вторым будет название поля.
Третьим атрибутом будет виджет, в котором определим текстовое поле ввода. Внутри в качестве атрибутов присвоим полю класс, а также укажем текст внутри поля.
Второе поле
Первый атрибут такой же - максимальная длина, которая равна 128 символам.
Второй - название поля.
Третьим атрибутом определим виджет, в этот раз это будет поле ввода пароля. Точно также передаём в атрибутах класс и текст внутри поля.
Ниже создаём подкласс
Первым полем будет используемая модель пользователя. Поскольку мы пока используем стандартную модель, её и указываем. Если же вы используете расширенную модель пользователя, то указывайте её.
Вторым полем мы указываем поля формы. В нашем случае их всего два, имя пользователя и пароль.
Код:
Представление страницы авторизации.
Откроем файл
Напишем класс
В классе пропишем всего три поля:
-
-
-
Также переопределим метод
Код:
Форма авторизации.
Создадим форму авторизации.
На самом деле, делать этого не обязательно.
Можно воспользоваться встроенной формой, собственно от которой будем наследоваться, однако тут встаёт вопрос с её отображением. У стандартной формы отсутствуют стили.
Можно конечно их прописать с помощью CSS-селекторов, но у меня в данный момент используется Bootstrap5.
Для того чтобы воспользоваться его возможностями, необходимо форме определить конкретные стили, а для этого нужно её переопределить.
В директории приложения
user_app создадим новый файл forms.py.Создадим класс
LoginForm, унаследованный от AuthenticationForm.Затем создадим два поля:
Первое будет
username. В нём определим поле символов. Первым атрибутом будет максимальная длина имени пользователя. По умолчанию в модели пользователя максимальная длина составляет 150 символов, укажем столько же или можете по своему усмотрению указать меньше.
Вторым будет название поля.
Третьим атрибутом будет виджет, в котором определим текстовое поле ввода. Внутри в качестве атрибутов присвоим полю класс, а также укажем текст внутри поля.
Второе поле
password. Также является полем символов. Первый атрибут такой же - максимальная длина, которая равна 128 символам.
Второй - название поля.
Третьим атрибутом определим виджет, в этот раз это будет поле ввода пароля. Точно также передаём в атрибутах класс и текст внутри поля.
Ниже создаём подкласс
Meta.Первым полем будет используемая модель пользователя. Поскольку мы пока используем стандартную модель, её и указываем. Если же вы используете расширенную модель пользователя, то указывайте её.
Вторым полем мы указываем поля формы. В нашем случае их всего два, имя пользователя и пароль.
Код:
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User
class LoginForm(AuthenticationForm):
username = forms.CharField(
max_length=150,
label='Имя пользователя',
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Введите имя пользователя'
})
)
password = forms.CharField(
max_length=128,
label='Пароль',
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Введите пароль'
})
)
class Meta:
model = User
fields = ['username', 'password']
Представление страницы авторизации.
Откроем файл
views.py.Напишем класс
CustomLoginView, унаследованный от LoginView.В классе пропишем всего три поля:
-
authentication_form - в этом поле указываем класс нашей переопределённой формы.-
template_name - в этом поле указываем HTML-файл шаблона.-
extra_context - в этом поле указываем дополнительные переменные, передаваемые в шаблон. Также переопределим метод
get_success_url. В этом методе, воспользовавшись функцией reverse_lazy указываем на какую страницу перейдёт пользователь после авторизации. Пока у нас нет страницы профиля, будем перенаправлять пользователя на главную страницу.Код:
from django.contrib.auth.views import LoginView
from django.urls import reverse_lazy
from user_app.forms import LoginForm
class CustomLoginView(LoginView):
authentication_form = LoginForm
template_name = 'user_app/login.html'
extra_context = {'title': 'Авторизация на сайте'}
def get_success_url(self):
return reverse_lazy('blog:index')
👍1
Шаблон страницы авторизации.
В директории
В этом файле прописываем тег формы.
Внутри прописываем:
- Тег
- Проверку на наличие не связанных с полями ошибок. Если ошибки есть, выводим их.
- В цикле
- Кнопку для отправки формы.
Код:
URL-паттерн страницы входа.
Создадим новый файл
Указываем имя приложения и в списке
Код:
Теперь необходимо добавить приложение в основной файл. Откроем файл
Все страницы связанные с пользователем будут начинаться на
Например:
-
-
-
- И так далее....
Выход.
Выход с сайта - очень простой функционал, но есть два варианта его исполнения:
1. Если вам не нужна промежуточная страница.
2. Если вам нужна промежуточная страница.
Второй вариант, это продолжение первого. В первом случае при выходе пользователя с сайта, он будет переправлен на указанную вами страницу, например, главную. Во втором же варианте, после выхода пользователя с сайта, он попадёт на специальную страницу на которой, например, будет кнопка повторной авторизации или ещё что-то, что вам необходимо.
Рассмотрим оба варианта.
Откроем файл
Для выхода используем встроенное в Django представление. Определяем URL и название.
На этом месте начинаются различия между первым и вторым вариантом.
Для первого варианта достаточно открыть файл
Пример для главной страницы:
Для второго варианта необходимо в URL-паттерне внутри представления добавить путь к шаблону:
Затем создать этот шаблон с необходимым вам содержимым, например:
Готово.
Кнопка входа и выхода в шапке сайта.
Остался последний "штрих" - добавить кнопку для входа и выхода в шапку сайта.
При этом, кнопка входа должна быть видна только неавторизованному пользователю, а кнопка выхода авторизованному соответственно.
В директории
templates создадим новую директорию user_app, а в ней файл login.html.В этом файле прописываем тег формы.
Внутри прописываем:
- Тег
csrf_token. Данный тег обеспечивает защиту формы.- Проверку на наличие не связанных с полями ошибок. Если ошибки есть, выводим их.
- В цикле
for перебираем поля формы. Выводим название поля и само поле.- Кнопку для отправки формы.
Код:
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% for field in form %}
<p class="form-label">{{ field.label }}</p>
<p>{{ field }}</p>
{% endfor %}
<button type="submit" class="btn btn-outline-success">Войти</button>
</form>
URL-паттерн страницы входа.
Создадим новый файл
urls.py.Указываем имя приложения и в списке
urlpatterns добавляем путь для страницы авторизации.Код:
from django.urls import path
from user_app import views
app_name = 'user_app'
urlpatterns = [
path('login/', views.CustomLoginView.as_view(), name='login'),
]
Теперь необходимо добавить приложение в основной файл. Откроем файл
urls.py в директории проекта и добавим новую строку:path('user/', include('user_app.urls', namespace='user_app')),Все страницы связанные с пользователем будут начинаться на
user. Например:
-
user/login - авторизация-
user/signup - регистрация-
user/<username> - профиль- И так далее....
Выход.
Выход с сайта - очень простой функционал, но есть два варианта его исполнения:
1. Если вам не нужна промежуточная страница.
2. Если вам нужна промежуточная страница.
Второй вариант, это продолжение первого. В первом случае при выходе пользователя с сайта, он будет переправлен на указанную вами страницу, например, главную. Во втором же варианте, после выхода пользователя с сайта, он попадёт на специальную страницу на которой, например, будет кнопка повторной авторизации или ещё что-то, что вам необходимо.
Рассмотрим оба варианта.
Откроем файл
urls.py в директории приложения. Добавим в список urlpatterns следующую строку и определим новый импорт:from django.contrib.auth import views as auth_views
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
Для выхода используем встроенное в Django представление. Определяем URL и название.
На этом месте начинаются различия между первым и вторым вариантом.
Для первого варианта достаточно открыть файл
settings.py и определить новый параметр LOGOUT_REDIRECT_URL, прописав в нём прямой путь или имя URL-паттерна.Пример для главной страницы:
LOGOUT_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = 'blog:index'
Для второго варианта необходимо в URL-паттерне внутри представления добавить путь к шаблону:
path('logout/', auth_views.LogoutView.as_view(template_name='user_app/logout.html'), name='logout'),Затем создать этот шаблон с необходимым вам содержимым, например:
{% extends 'blog/base.html' %}
{% block content %}
<div class="form-section container">
<h2>Вы успешно вышли с сайта</h2>
<a href="{% url 'user_app:login' %}">Войти в личный кабинет снова</a>
</div>{% endblock %}Готово.
Кнопка входа и выхода в шапке сайта.
Остался последний "штрих" - добавить кнопку для входа и выхода в шапку сайта.
При этом, кнопка входа должна быть видна только неавторизованному пользователю, а кнопка выхода авторизованному соответственно.
🔥1
Откроем файл
Файлы к посту, можно получить в боте по коду: 896551
Пост на сайте.
Поддержать канал.
header.html и в удобное место добавим следующий код:{% if user.is_authenticated %}
<div class="dropdown">
<button class="btn btn-primary my-btn dropdown-toggle me-3" type="button"
data-bs-toggle="dropdown" aria-expanded="false">
{{ request.user }}
</button>
<ul class="dropdown-menu dd-color">
<li><a class="dropdown-item dd-item-color" href="{% url 'user_app:logout' %}">Выйти</a></li>
</ul>
</div>
{% else %}
<li class="nav-item">
<a class="nav-link btn btn-primary my-btn me-3"
href="{% url 'user_app:login' %}">Войти</a>
</li>
{% endif %}
Можно запустить Django и протестировать возможность входа и выхода с сайта.Файлы к посту, можно получить в боте по коду: 896551
Пост на сайте.
Поддержать канал.
🔥2
Приветствую.
На одном из проходимых мной курсов, итоговым заданием необходимо было сделать прототип сервиса по сокращению ссылок.
Задание достаточно простое и мне стало интересно, а что если вынести фронт на Vue, а бэк пусть остаётся на Django?
Также, дополнительным плюсом будет иметь свой сервис сокращения ссылок, поскольку часто ссылка + UTF метки занимают много места, а сервисы типа click.ru|bit.ly|vk.cc и прочие, помечаются как небезопасные.
Результатом стал сайт https://lkeep.ru/
Работать будет так: https://lkeep.ru/l/EMDRTbZUpi
На данный момент из "косяков" только отсутствие адаптивной вёрстки и неработающий(недописанный) функционал копирования ссылки в лк.
Мне нужны ваши отзывы. Как вам, может что-то улучшить/изменить? Пишите в комментариях!
P.S. Регистрация обязательна, иначе это будет такая же помойка как и другие сайты.
На одном из проходимых мной курсов, итоговым заданием необходимо было сделать прототип сервиса по сокращению ссылок.
Задание достаточно простое и мне стало интересно, а что если вынести фронт на Vue, а бэк пусть остаётся на Django?
Также, дополнительным плюсом будет иметь свой сервис сокращения ссылок, поскольку часто ссылка + UTF метки занимают много места, а сервисы типа click.ru|bit.ly|vk.cc и прочие, помечаются как небезопасные.
Результатом стал сайт https://lkeep.ru/
Работать будет так: https://lkeep.ru/l/EMDRTbZUpi
На данный момент из "косяков" только отсутствие адаптивной вёрстки и неработающий(недописанный) функционал копирования ссылки в лк.
Мне нужны ваши отзывы. Как вам, может что-то улучшить/изменить? Пишите в комментариях!
P.S. Регистрация обязательна, иначе это будет такая же помойка как и другие сайты.
Django 32. Форма регистрации
Авторизацию мы добавили. Теперь добавим возможность регистрироваться на сайте.
В целом, процесс примерно такой же как и с авторизацией.
Продолжаем работать в директории приложения
Почти весь пост будем использовать, написанный в прошлом посте код с небольшими изменениями.
Форма регистрации.
Откроем файл
Далее добавляем 4 поля:
Поля
Поле
Далее прописываем внутренний класс
Авторизацию мы добавили. Теперь добавим возможность регистрироваться на сайте.
В целом, процесс примерно такой же как и с авторизацией.
Продолжаем работать в директории приложения
user_app.Почти весь пост будем использовать, написанный в прошлом посте код с небольшими изменениями.
Форма регистрации.
Откроем файл
forms.py и создадим новый класс RegistrationForm, унаследованный от UserCreationForm.Далее добавляем 4 поля:
username, email, password1 и password2.Поля
username и password можно скопировать из формы авторизации. Они такие же. Разве, что в поле password2 изменим название и описание поля.Поле
email описывается похожим образом, но для валидации используется EmailInput.Далее прописываем внутренний класс
Meta, в котором указываем модель и поля формы.Код:
Представление страницы регистрации.
Перейдём в файл
И здесь снова берём за основу уже написанный код, а именно поля и переопределение метода из представления авторизации.
Изменяя значения:
-
-
-
-
Ниже переопределяем метод
Код:
URL-паттерн страницы регистрации.
Открываем файл
Шаблон страницы регистрации.
В директории с шаблонами приложения
Копируем код из файла
Изменяем в коде текстовые значения, и добавляем отображение ошибки под поле, если в поле есть ошибка.
Код:
Добавляем кнопку в шапку.
Последнее, что осталось сделать, это добавить кнопку регистрации в шапку.
Откроем файл
Готово. Теперь на сайте есть и авторизация, и регистрация. Осталось добавить возможность сбрасывать пароль пользователю. После чего можно будет заняться профилем.
Файлы к посту, можно получить в боте по коду: 618284
Пост на сайте.
Поддержать канал.
from django.contrib.auth.forms import UserCreationForm
class RegistrationForm(UserCreationForm):
username = forms.CharField(
max_length=150,
label='Имя пользователя',
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Введите имя пользователя'
})
)
email = forms.EmailField(
widget=forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': 'Введите email'
})
)
password1 = forms.CharField(
max_length=128,
label='Пароль',
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Введите пароль'
})
)
password2 = forms.CharField(
max_length=128,
label='Подтверждение пароля',
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Повторите пароль'
})
)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', ]
Представление страницы регистрации.
Перейдём в файл
views.py и создадим новый класс CustomRegistrationView, унаследованный от CreateView.И здесь снова берём за основу уже написанный код, а именно поля и переопределение метода из представления авторизации.
Изменяя значения:
-
form_class - прописываем класс формы регистрации.-
template_name - прописываем файл шаблона.-
extra_context - прописываем заголовок страницы.-
get_success_url - изменяем страницу, на которую будет перенаправлен пользователь, в данном случае это страница авторизации.Ниже переопределяем метод
form_valid. В котором сохраним нового пользователя.Код:
class CustomRegistrationView(CreateView):
form_class = RegistrationForm
template_name = 'user_app/signup.html'
extra_context = {'title': 'Регистрация на сайте'}
def get_success_url(self):
return reverse_lazy('user_app:login')
def form_valid(self, form):
user = form.save()
return super().form_valid(form)
URL-паттерн страницы регистрации.
Открываем файл
urls.py и добавляем новый URL-паттерн для страницы регистрации:path('signup/', views.CustomRegistrationView.as_view(), name='signup'),Шаблон страницы регистрации.
В директории с шаблонами приложения
user_app создадим файл signup.html.Копируем код из файла
login.html.Изменяем в коде текстовые значения, и добавляем отображение ошибки под поле, если в поле есть ошибка.
Код:
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% for field in form %}
<p class="form-label">{{ field.label }}</p>
<p>{{ field }}</p>
{% if field.errors %}
<ul class="alert alert-danger">
{{ field.errors }}
</ul>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-outline-success">Зарегистрироваться</button>
</form>
Добавляем кнопку в шапку.
Последнее, что осталось сделать, это добавить кнопку регистрации в шапку.
Откроем файл
header.html и добавим кнопку рядом с авторизацией:<li class="nav-item">
<a class="nav-link btn btn-primary my-btn me-3"
href="{% url 'user_app:signup' %}">Регистрация</a>
</li>
Готово. Теперь на сайте есть и авторизация, и регистрация. Осталось добавить возможность сбрасывать пароль пользователю. После чего можно будет заняться профилем.
Файлы к посту, можно получить в боте по коду: 618284
Пост на сайте.
Поддержать канал.
🔥1
Снова пятница, пора бы отдохнуть, а то я чёт устал)
Сегодня хочу предложить фильм "Капитан Фантастик"
Синопсис: Бен живёт в лесу с шестью своими детьми. Они говорят на нескольких языках, разбираются в квантовой физике, философии и литературе, а вместо Рождества отмечают день рождения Ноама Хомского. Они умеют охотиться, способны разделать добычу и приготовить её на костре, а также находятся в прекрасной физической форме. Когда их мать, лежащая в больнице, кончает с собой, всему семейству приходится отправиться в большой мир на её похороны.
Если у вас есть подписка КП, то смотреть можно тут: https://www.kinopoisk.ru/film/841147/
Если подписки нет, то тут: https://www.sspoisk.ru/film/841147
А как у вас прошла неделя? Какие планы на выходные? Рассказывайте)
Сегодня хочу предложить фильм "Капитан Фантастик"
Синопсис: Бен живёт в лесу с шестью своими детьми. Они говорят на нескольких языках, разбираются в квантовой физике, философии и литературе, а вместо Рождества отмечают день рождения Ноама Хомского. Они умеют охотиться, способны разделать добычу и приготовить её на костре, а также находятся в прекрасной физической форме. Когда их мать, лежащая в больнице, кончает с собой, всему семейству приходится отправиться в большой мир на её похороны.
Если у вас есть подписка КП, то смотреть можно тут: https://www.kinopoisk.ru/film/841147/
Если подписки нет, то тут: https://www.sspoisk.ru/film/841147
А как у вас прошла неделя? Какие планы на выходные? Рассказывайте)
🔥3
Приветствую.
От подписчика @sstormss поступило предложение написать небольшой командный проект.
Идея: Библиотека фанфиков.
Фреймворк: Django.
Функционал: Пользователи, личный кабинет, публикации, комментарии, разделение на жанры, обсуждения/форумы, оценки, лайки и так далее.
Подробное ТЗ будет сформировано в течении пары недель.
Цель: Научиться работать в команде над групповым PET-проектом.
Сроки не регламентированы. В свободное время.
Если хотите присоединиться, пишите в комментарии и вступайте в чат https://t.iss.one/+cm-ITbA-JTczMTRi
От подписчика @sstormss поступило предложение написать небольшой командный проект.
Идея: Библиотека фанфиков.
Фреймворк: Django.
Функционал: Пользователи, личный кабинет, публикации, комментарии, разделение на жанры, обсуждения/форумы, оценки, лайки и так далее.
Подробное ТЗ будет сформировано в течении пары недель.
Цель: Научиться работать в команде над групповым PET-проектом.
Сроки не регламентированы. В свободное время.
Если хотите присоединиться, пишите в комментарии и вступайте в чат https://t.iss.one/+cm-ITbA-JTczMTRi
Telegram
Кот на салфетке
Чат для канала https://t.iss.one/press_any_button
Обсуждаем посты, программирование и просто болтаем.
Обсуждаем посты, программирование и просто болтаем.
🔥2
Голосовалка на тему прошлого поста.
Хотите присоединиться к нашему PET-проекту?
Хотите присоединиться к нашему PET-проекту?
Anonymous Poll
37%
Да, хочу. Уже вступаю в группу!
53%
Хочу, но нет времени
10%
Не хочу. Я лучше самостоятельно))
🔥2
Приветствую.
Следующие посты чуть задержатся. Последнюю неделю было много дел, частью из которых хочу с вами поделиться.
1. 12-го числа подал заявку на стажировку в LAD Academy, в качестве Backend Python/Django разработчика.
15-го числа был последний день для отправки тестового задания - сайта на Django. Поэтому отложил все дела и занимался им. И, считаю, что вышло весьма добротно.
Результаты отбора будут 30-31 октября, о чём я конечно же сообщу 🙂
Код и само задание доступно на GitHub.
Ссылка на стажировку.
2. Параллельно с этим получил небольшой "заказ" - разработать приложение по конвертации файлов, заточенных под один ЧПУ-станок в другой. Реализовал с помощью библиотеки
Код доступен на GitHub.
В целом, неделя была продуктивной. В скором времени вернусь к постам.
У меня к вам два вопроса:
1. Помимо Django, на какую тему вам было бы интересно почитать посты? (желательно прям с идеями).
2. Если у вас есть комментарии, предложения, критика к коду тестового задания или программы - не стесняйтесь писать в комментарии.
Спасибо.
Пост на сайте.
Поддержать канал.
Следующие посты чуть задержатся. Последнюю неделю было много дел, частью из которых хочу с вами поделиться.
1. 12-го числа подал заявку на стажировку в LAD Academy, в качестве Backend Python/Django разработчика.
15-го числа был последний день для отправки тестового задания - сайта на Django. Поэтому отложил все дела и занимался им. И, считаю, что вышло весьма добротно.
Результаты отбора будут 30-31 октября, о чём я конечно же сообщу 🙂
Код и само задание доступно на GitHub.
Ссылка на стажировку.
2. Параллельно с этим получил небольшой "заказ" - разработать приложение по конвертации файлов, заточенных под один ЧПУ-станок в другой. Реализовал с помощью библиотеки
Flet.Код доступен на GitHub.
В целом, неделя была продуктивной. В скором времени вернусь к постам.
У меня к вам два вопроса:
1. Помимо Django, на какую тему вам было бы интересно почитать посты? (желательно прям с идеями).
2. Если у вас есть комментарии, предложения, критика к коду тестового задания или программы - не стесняйтесь писать в комментарии.
Спасибо.
Пост на сайте.
Поддержать канал.
🔥6
Приветствую.
6-го числа был пост с опросом о городах подписчиков.
Я благополучно про это забыл, исправляюсь)
Всего отметилось 19 человек из 20 городов (можно было бы и по активнее!).
В результате получился такой список:
- Бельдяжки - Vadim
- Бор - Кристина
- Волгоград - Евгений Акопян
- Волжский - Тимур Машков
- Воронеж - Mike_Ploskiy
- Грахово - Arthur
- Люблин, Польша - Иван Пеньковский
- Магнитогорск - Михаил Воропаев
- Мерсин, Турция - ole
- Москва - Segio B, Vadim
- Нижний Новгород - Кристина
- Нови Сад, Сербия - Mariya
- Новосибирск - Сергей, Андрей Богданов
- Прохладный - Roman Eremenko
- Рязань - Рыжов Василий
- Санкт-Петербург - ole
- Сарапул - proDreams
- Симферополь - Александр Бабичев
- Уфа - Regina
- Хабаровск - Юрий Б.
Прикрепляю криво сделанную карту с отметками городов.
P.S. Прости, Юра. Хабаровск не влез в экран))
6-го числа был пост с опросом о городах подписчиков.
Я благополучно про это забыл, исправляюсь)
Всего отметилось 19 человек из 20 городов (можно было бы и по активнее!).
В результате получился такой список:
- Бельдяжки - Vadim
- Бор - Кристина
- Волгоград - Евгений Акопян
- Волжский - Тимур Машков
- Воронеж - Mike_Ploskiy
- Грахово - Arthur
- Люблин, Польша - Иван Пеньковский
- Магнитогорск - Михаил Воропаев
- Мерсин, Турция - ole
- Москва - Segio B, Vadim
- Нижний Новгород - Кристина
- Нови Сад, Сербия - Mariya
- Новосибирск - Сергей, Андрей Богданов
- Прохладный - Roman Eremenko
- Рязань - Рыжов Василий
- Санкт-Петербург - ole
- Сарапул - proDreams
- Симферополь - Александр Бабичев
- Уфа - Regina
- Хабаровск - Юрий Б.
Прикрепляю криво сделанную карту с отметками городов.
P.S. Прости, Юра. Хабаровск не влез в экран))
🔥5💩1🤣1