Код на салфетке
2.22K subscribers
745 photos
14 videos
2 files
788 links
Канал для тех, кому интересно программирование на Python и не только.

Сайт: https://pressanybutton.ru/
Чат: https://t.iss.one/+Li2vbxfWo0Q4ZDk6
Заметки автора: @writeanynotes

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
Снова пятница, пора бы отдохнуть, а то я чёт устал)

Сегодня хочу предложить фильм "Капитан Фантастик"

Синопсис: Бен живёт в лесу с шестью своими детьми. Они говорят на нескольких языках, разбираются в квантовой физике, философии и литературе, а вместо Рождества отмечают день рождения Ноама Хомского. Они умеют охотиться, способны разделать добычу и приготовить её на костре, а также находятся в прекрасной физической форме. Когда их мать, лежащая в больнице, кончает с собой, всему семейству приходится отправиться в большой мир на её похороны.

Если у вас есть подписка КП, то смотреть можно тут: https://www.kinopoisk.ru/film/841147/

Если подписки нет, то тут: https://www.sspoisk.ru/film/841147

А как у вас прошла неделя? Какие планы на выходные? Рассказывайте)
🔥3
Приветствую.

От подписчика @sstormss поступило предложение написать небольшой командный проект.

Идея: Библиотека фанфиков.
Фреймворк: Django.
Функционал: Пользователи, личный кабинет, публикации, комментарии, разделение на жанры, обсуждения/форумы, оценки, лайки и так далее.
Подробное ТЗ будет сформировано в течении пары недель.

Цель: Научиться работать в команде над групповым PET-проектом.

Сроки не регламентированы. В свободное время.

Если хотите присоединиться, пишите в комментарии и вступайте в чат https://t.iss.one/+cm-ITbA-JTczMTRi
🔥2
Голосовалка на тему прошлого поста.

Хотите присоединиться к нашему PET-проекту?
Anonymous Poll
37%
Да, хочу. Уже вступаю в группу!
53%
Хочу, но нет времени
10%
Не хочу. Я лучше самостоятельно))
🔥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. Прости, Юра. Хабаровск не влез в экран))
🔥5💩1🤣1
Приветствую.

Давайте проведём новый опрос:

Какой ваш основной язык программирования и почему?

А если вы только учитесь, то какому языку отдаёте приоритет и также почему?

Хотел сперва сделать опрос, но тогда, он был бы не сильно информативен. По этому, просто пост.

Пишите ответы в комментарии, я уверен, многим будет интересно почитать то, к чему лежит выбор у других людей.
🔥2
Django 33. Сброс пароля пользователя

В посте "Django 13. Сброс пароля", мы уже писали сброс пароля, но то было для панели администратора.
В этом посте, реализуем сброс пароля для обычных пользователей.

Также для корректной работы, необходимо указать почтовый сервис.
Как его настроить я описывал в посте "Django 12. Настройка отправки почты".
А в постах "Docker 5.1 Почтовый сервер на Docker Mailserver - настройка домена" и "Docker 5.2 Почтовый сервер на Docker Mailserver - настройка и запуск" рассказывал как запустить свой почтовый сервер.

Для реализации сброса пароля, можно пойти простым или сложным путём.

Простой путь, это использовать уже встроенные в Django представления и формы, именно так мы и сделали в прошлый раз.
👍2
Достаточно в файле urls.py указать паттерны:
from django.contrib.auth import views as auth_views


path('password-reset/',
auth_views.PasswordResetView.as_view(template_name='users/pass_reset.html'),
name='password-reset'),
path('password_reset_confirm/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(template_name='users/password_reset_confirm.html'),
name='password_reset_confirm'),
path('password_reset_complete/',
auth_views.PasswordResetCompleteView.as_view(template_name='users/password_reset_complete.html'),
name='password_reset_complete'),
path('password-reset/done/',
auth_views.PasswordResetDoneView.as_view(template_name='users/password_reset_done.html'),
name='password_reset_done'),


Указав собственные шаблоны.
Однако, в нашем случае, уже есть сброс пароля для админа и они будут конфликтовать.
Можно вовсе убрать сброс пароля для админ и оставить общий, но куда интереснее переопределить представления.

Этим и займёмся.


Переопределение форм.
Начнём с переопределения форм для того, чтобы добавить в них поддержку стилей из Bootstrap.

Откроем файл forms.py в директории приложения user_app.

Тут нам надо переопределить две формы: форму ввода электронной почты для сброса и форму ввода нового пароля.
Мы не будем переписывать их полностью, а просто наследуемся от них и изменим стандартные поля.

Код форм:
from django.contrib.auth import password_validation  
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm


class CustomPasswordResetForm(PasswordResetForm):
email = forms.EmailField(
label="Email",
max_length=254,
widget=forms.EmailInput(
attrs={'class': 'form-control',
'placeholder': 'Введите Email',
"autocomplete": "email"}
)
)


class CustomSetPasswordForm(SetPasswordForm):
error_messages = {
"password_mismatch": "Пароли не совпадают"
}
new_password1 = forms.CharField(
label='Новый пароль',
widget=forms.PasswordInput(
attrs={'class': 'form-control',
'placeholder': 'Введите новый пароль',
"autocomplete": "new-password"}
),
strip=False,
help_text=password_validation.password_validators_help_text_html(),
)
new_password2 = forms.CharField(
label='Подтверждение нового пароля',
strip=False,
widget=forms.PasswordInput(
attrs={'class': 'form-control',
'placeholder': 'Подтвердите новый пароль',
"autocomplete": "new-password"}
),
)


В первой форме CustomPasswordResetForm, унаследованной от PasswordResetForm, мы переопределили только поле email.
Добавив класс из Bootstrap и прописав текст внутри поля.

Во второй форме CustomSetPasswordForm, унаследованной от SetPasswordForm, мы переопределили два поля new_password1 и new_password2, а так же прописали сообщение, если пароли не совпадают в поле error_messages.


Переопределение представлений.
Откроем файл views.py в директории приложения user_app.

Тут мы создадим два собственных представления:
- CustomPasswordResetView - представление страницы, где пользователь вводит электронную почту для сброса. Наследуемся от PasswordResetView.
- CustomUserPasswordResetConfirmView - представление страницы на которую пользователь перейдёт из письма для ввода нового пароля. Наследуемся от PasswordResetConfirmView.
Код представлений.
from django.contrib.auth.views import PasswordResetView, PasswordResetConfirmView
from user_app.forms import CustomPasswordResetForm, CustomSetPasswordForm


class CustomPasswordResetView(PasswordResetView):
template_name = 'user_app/password_reset.html'
email_template_name = 'user_app/password_reset_email.html'
form_class = CustomPasswordResetForm
success_url = reverse_lazy('user_app:password_reset_done')


class CustomUserPasswordResetConfirmView(PasswordResetConfirmView):
template_name = 'user_app/password_reset_confirm.html'
success_url = reverse_lazy('user_app:password_reset_complete')
form_class = CustomSetPasswordForm


В этих представлениях, переопределим следующие поля:
- template_name - файл шаблона.
- email_template_name - файл шаблона для отправки по электронной почте. Только в представлении CustomPasswordResetView.
- form_class - класс с формой.
- success_url - путь, куда будет перенаправлен пользователь после отправки формы.

Шаблоны страниц.
В директории с шаблонами приложения user_app, необходимо создать пять файлов:
- password_reset.html - первая страница сброса пароля. На этой странице пользователь вводит свой email.
- password_reset_done.html - вторая страница сброса пароля. На этой странице сообщаем пользователю, что письмо со ссылкой для сброса было отправлено на email.
- password_reset_confirm.html - третья страница сброса пароля. На этой странице пользователь вводит новый пароль.
- password_reset_complete.html - четвёртая и последняя страница сброса пароля. На этой странице сообщаем, что пароль был изменён.
- password_reset_email.html - страница шаблона для электронной почты. Код страницы взят из стандартного Django-шаблона, с одним лишь изменением - ссылкой на которую перейдёт пользователь из письма. Если этот файл не изменить, то в письме будет ссылка на сброс пароля администратора, а нам не нужно, что бы пользователи знали адрес панели администратора.

password_reset.html и password_reset_confirm.html.
Стандартный шаблон формы:
<form method="POST">  
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-danger mt-3">Запросить новый пароль</button>
</form>


Различия только в тексте кнопки. Вообще можно сделать один файл и передавать текст кнопки через контекст.

password_reset_done.html.
В этом шаблоне сообщаем, что письмо отправлено и добавляем кнопку возврата на главную страницу.
<div class="container mt-3">  
<h2>Ссылка для смены пароля отправлена на вашу почту.</h2>
<a href="{% url 'blog:index' %}" class="btn btn-danger mt-3">Вернуться на главную</a>
</div>


password_reset_complete.html.
В этом шаблоне сообщаем, что пароль изменён и добавляем две кнопки: переход на главную и вход на сайт.
<div class="container mt-3">  
<h2>Вы успешно обновили пароль</h2>
<p>Ваш пароль заменен на новый!</p>
<a href="{% url 'user_app:login' %}" class="btn btn-danger mt-3">Войти</a>
<a href="{% url 'blog:index' %}" class="btn btn-danger mt-3">Вернуться на главную</a>
</div>


password_reset_email.html.
Изменённый стандартный шаблон письма со ссылкой для восстановления пароля.
Изменить необходимо имя URL-шаблона в этой строке:
{{ protocol }}://{{ domain }}{% url 'user_app:password_reset_confirm' uidb64=uid token=token %}


Код шаблона:
{% load i18n %}{% autoescape off %}  
{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}

{% translate "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'user_app:password_reset_confirm' uidb64=uid token=token %}
{% endblock %}
{% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }}

{% translate "Thanks for using our site!" %}

{% blocktranslate %}The {{ site_name }} team{% endblocktranslate %}

{% endautoescape %}
URL-паттерны страниц.
Откроем файл urls.py и в список urlpatterns добавим новые паттерны:
from django.contrib.auth import views as auth_views


path('password-reset/', views.CustomPasswordResetView.as_view(), name='password-reset'),
path('password_reset_confirm/<uidb64>/<token>/',
views.CustomUserPasswordResetConfirmView.as_view(),
name='password_reset_confirm'),
path('password_reset_complete/',
auth_views.PasswordResetCompleteView.as_view(template_name='user_app/password_reset_complete.html'),
name='password_reset_complete'),
path('password-reset/done/',
auth_views.PasswordResetDoneView.as_view(template_name='user_app/password_reset_done.html'),
name='password_reset_done'),


Последний штрих.
Осталось добавить ссылку на страницу сброса пароля в шаблон страницы входа.

Откроем файл login.html и добавим следующую строчку где-нибудь внизу:
Забыли пароль? <a href="{% url 'user_app:password-reset' %}">Сбросить пароль</a>


Готово. Теперь пользователь сможет сбросить свой пароль.

Файлы к посту, можно получить в боте по коду: 568363
Пост на сайте.
Поддержать канал.
🔥1
Приветствую.

Снова неделя пролетела, а вроде бы ещё вчера было воскресенье. Пора отдыхать!


Фильм на сегодня: Голограмма для короля (2016).

Краткий синопсис: Алан Клей - муж, отец, бизнесмен. Но его бизнес терпит крах, его брак трещит по швам, и он не знает, где взять деньги на оплату обучения дочери. Чтобы избежать банкротства и разорвать замкнутый круг, Клей отправляется в Саудовскую Аравию, где он надеется продвинуть свой дерзкий технологический проект. Во время томительного ожидания встречи с королем Алан познает скрытые тайны арабского мира, которые преображают реальность, подобно голограмме. Никто не подозревал, чем обернется это путешествие к Красному морю…

Если у вас есть подписка КП, то смотреть можно тут: https://www.kinopoisk.ru/film/771067/

Если подписки нет, то тут: https://www.sspoisk.ru/film/771067/

А как провели неделю вы?
Подписчики поймут...👀
💯8🔥2🤣1
👀А кто-то их читает?
😁5
Django 34.1. Простой профиль пользователя - страница профиля

Что у нас уже есть?
- Регистрация пользователя
- Авторизация пользователя
- Сброс пароля

Следующим шагом необходимо реализовать профиль пользователя.

Я знаю три способа реализации личного кабинета / профиля пользователя:
- Простой - страничка с доступной из стандартной модели пользователя информацией, а также, например, со списком постов пользователя, его комментариями и другими данными, где пользователь указан в модели.
- Средний - создание дополнительной модели профиля. В этой модели указываются дополнительные поля, методы и прочее, которых нет в стандартной модели.
- Сложный - создание собственной модели пользователя, основываясь на базовом классе или описывая всё с нуля, если это необходимо.

Поскольку у всех разные требования и необходимости, я хочу рассмотреть все три способа, а у вас будет представление о способах реализации и можно будет выбрать наиболее подходящий.
Начнём с самого простого варианта.

Мы реализуем:
- Страницу профиля. На ней будет информация о пользователе и список его постов, кнопка перехода на страницу настроек и кнопка перехода ко всем постам пользователя. Для этого нам надо будет сделать представление и шаблон.
- Страницу настроек профиля. На ней будет две формы: со сменой имени пользователя и email, а так же со сменой пароля. Для этого нам надо будет сделать представление, шаблон и две формы.


Представление страницы профиля.
Напишем простое представление, которое будет передавать в шаблон объект и посты пользователя.

Откроем файл views.py в приложении user_app.

Создадим класс UserProfileView, унаследованный от TemplateView.

В нём прописываем единственное поле template_name, указывающее на файл шаблона.

Ниже переопределяем метод get_context_data.

В нём мы получаем объект класса User, полученный из переданного в адресной строке имени пользователя и записываем в переменную.
Получение объекта оборачиваем в try-except, в случае, если передано некорректное или отсутствующее в базе имя пользователя, вместо страницы с ошибкой получения данных, вернулась страница ошибки 404 (до написания собственных страниц ошибок, мы тоже дойдём.).

Записываем в контекст объект пользователя, список его последних пяти постов и заголовок страницы.

Код представления:
from django.contrib.auth.models import User
from django.views.generic import TemplateView
from django.shortcuts import get_object_or_404
from django.http import Http404

from blog.models import PostModel


class UserProfileView(TemplateView):
template_name = 'user_app/profile_page.html'

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
user = get_object_or_404(User, username=self.kwargs.get('username'))
except User.DoesNotExist:
raise Http404("Пользователь не найден")
context['user_profile'] = user
context['user_posts'] = PostModel.post_manager.filter(author=user)[:5]
context['title'] = f'Профиль пользователя {user}'
return context


**Шаблон страницы профиля.**
В директории с шаблонами приложения создадим новый файл profile_page.html.

Внутри разделим его на две части:
- В левой будет список постов и кнопка (пока заглушка) для перехода на страницу всех постов пользователя.
- В правой будет информация о пользователе. На данный момент, это имя пользователя (username), имя и фамилия (если указаны в профиле), а также кнопка для перехода на страницу изменения профиля (пока тоже заглушка).
Код шаблона:
{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}

{% block content %}
<div class="container mt-3">
<h2>Профиль пользователя {{ user_profile }}</h2>
<hr>
<div class="row">
<div class="col-xl-8 сol-sm-12">
<h3 class="mb-3">Последние посты:</h3>
{% for post in user_posts %}
<div class="row">
<div class="col-lg-3 col-sm-12 d-flex align-items-center">
<img src="{{ post.image.url }}" alt="{{ post.title }}" class="img-fluid">
</div>
<div class="col-lg-9 col-sm-12">
<h4 class="head2"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h4>
<div class="mb-3">
<p class="lead"><a
href="{{ post.category.get_absolute_url }}">{{ post.category }}</a>
| {{ post.publish | date:"d F Y" }} |
Просмотров: {{ post.views }}</p>
</div>
{{ post.short_body | safe }}
</div>
</div>
<hr class="m-1">
{% endfor %}
<div class="text-center mt-3">
<a class="btn btn-primary my-btn mb-3" href="#">Все посты {{ user_profile }}</a>
</div>
</div>
<div class="col-xl-4 col-sm-12 border border-start border-0">
<h3 class="mb-3">Информация:</h3>
<p>
<b>Имя пользователя:</b> {{ user_profile }}
{% if user_profile.first_name %}
<br>
<b>Имя:</b> {{ user_profile.first_name }}
{% endif %}
{% if user_profile.last_name %}
<br>
<b>Фамилия:</b> {{ user_profile.last_name }}</p>
{% endif %}
{% if user_profile == user %}
<a class="btn btn-primary my-btn me-1" href="#">Настройки профиля</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}



Ссылка на профиль в шапке и в карточке поста.
Теперь добавим ссылку на профиль в шапку сайта.

Откроем файл header.html и там, где у нас кнопка "выход", выше добавляем "профиль":
<li>  
<a class="dropdown-item dd-item-color" href="{% url 'user_app:user_profile' username=user.username %}">Профиль</a>
</li>


Также добавим ссылку в карточку поста или в сам пост:
<a href="{% url 'user_app:user_profile' username=post.author %}">{{ post.author }}</a>


В первом случае, мы передаём ключевой аргумент username текущего авторизованного пользователя, а во втором случае, берём его из поля author у поста.

URL-паттерн страницы профиля.
Последнее, что осталось сделать, это прописать URL-паттерн.

Открываем файл urls.py в директории приложения user_app и в список urlpatterns добавляем новую строку:
path('<str:username>/', views.UserProfileView.as_view(), name='user_profile'),


Готово. Теперь у нас есть простая страница профиля. В следующем посте сделаем страницу настроек и страницу всех постов.

Файлы к посту, можно получить в боте по коду: 764197
Пост на сайте.
Поддержать канал.
👍3