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

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

Реклама и взаимопиар: @Murzyev1995
Сотрудничество и др.: @proDreams
Download Telegram
Приветствую.

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


Фильм на сегодня: Голограмма для короля (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
А достаточно ли ты хорош чтобы иметь синдром самозванца?
Anonymous Poll
25%
Я - Топ!
16%
Да
59%
Нет, но да
Скинули тут занятную статейку переложения программистов на классы в D&D.

https://habr.com/ru/companies/domclick/articles/517736/

Не могу определиться, я Варвар или Воин или мультикласс))
Django 34.2. Простой профиль пользователя - страница настроек

В предыдущем посте, мы сделали страницу профиля, теперь надо сделать страницу настроек пользователя.

На этой странице пользователь сможет изменить свои данные и поменять пароль.

Также сделаем страницу со всеми постами пользователя.

В процессе напишем две формы, два представления и два шаблона.
Работать будем с файлами в директории приложения user_app. Приступим.


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

У нас будет две формы:
- Форма изменения данных пользователя - это имя пользователя (username), email, имя и фамилия.
- Форма смены пароля.

Форма изменения данных.
Это типовая форма модели.

Создадим класс UserInfoForm, унаследованный от forms.ModelForm.

Описываем четыре поля, добавляя названия полей и стили: username, email, first_name, last_name.
В подклассе Meta указываем модель User и вышеупомянутые поля.
Код формы:
class UserInfoForm(forms.ModelForm):  
username = forms.CharField(
max_length=150,
label='Имя пользователя (username)',
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Введите имя пользователя'
})
)
email = forms.EmailField(
widget=forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': 'Введите email'
})
)
first_name = forms.CharField(
max_length=150,
label='Имя',
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Введите ваше имя'
})
)
last_name = forms.CharField(
max_length=150,
label='Фамилия',
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Введите вашу фамилию'
})
)

class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name']


Форма изменения пароля.
Форму смены пароля писать с нуля не будем, т.к. Django предоставляет встроенную форму PasswordChangeForm.
Мы всего лишь переопределим поля, добавив стили форме.

Создадим класс UserPasswordForm, унаследованный от PasswordChangeForm и пропишем три поля: old_password, new_password1, new_password2.

Код формы:
class UserPasswordForm(PasswordChangeForm):  
old_password = forms.CharField(
max_length=128,
label='Старый пароль',
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Введите старый пароль'
})
)
new_password1 = forms.CharField(
max_length=128,
label='Новый пароль',
help_text=password_validation.password_validators_help_text_html(),
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Введите новый пароль'
})
)
new_password2 = forms.CharField(
max_length=128,
label='Подтверждение нового пароля',
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'placeholder': 'Повторите новый пароль'
})
)



Представление страницы настроек.
Достаточно сложное и объёмно представление.

Нам надо передать на страницу две формы, а затем обрабатывать их независимо друг от друга.

Помимо того, что страница должна быть доступна только авторизованному пользователю, она также должна быть доступна только тому пользователю, чья страница настроек открыта.
Например, у нас есть маршрут /user/testuser/settings, где testuser это имя пользователя. И если по этому адресу перейдёт testuser, то он увидит настройки, но если другой пользователь откроет эту страницу, то должна быть ошибка 403 Forbidden.

Откроем файл views.py.
Создадим класс UserSettingsView, в этот раз у нас будет два наследования:
- От класса LoginRequiredMixin, проверяющего авторизован пользователь или нет.
- От уже знакомого нам класса TemplateView.

Прописываем единственное поле template_name с указанием файла шаблона.

Далее у нас будет переопределено три метода:
Первым мы переопределим метод dispatch(). С ним мы ещё не сталкивались.

Метод `dispatch()` - это один из ключевых методов в классовых представлениях Django. Он является частью жизненного цикла обработки запросов и играет важную роль в определении того, какой метод (GET, POST, PUT, DELETE и т. д.) должен обрабатывать входящий HTTP-запрос.
`dispatch()` работает следующим образом:
1. Когда клиентский запрос поступает на представление, Django вызывает метод `dispatch()` этого представления.
2. `dispatch()` анализирует метод HTTP запроса (GET, POST, PUT, DELETE и т. д.), который пришел от клиента.
3. В зависимости от метода запроса, `dispatch() вызывает соответствующий метод класса представления. Например, если это GET-запрос, `dispatch()` вызовет метод `get(), если это POST-запрос - метод `post()`, и так далее.
4. После обработки запроса соответствующим методом, `dispatch()` возвращает HTTP-ответ.

В нашем методе, мы проверяем, является ли пользователь переданный в адресной строке текущим авторизованным пользователем.
Если является - вызываем метод dispatch() из родительского класса, в противном случае возвращаем 403 Forbidden.

Вторым переопределённым методом будет get_context_data. С ним мы уже сталкивались ранее и он вполне должен быть знаком, но и тут есть нюансы.
В этом методе мы передаём две формы и т.к. у нас формы моделей, нам при их сохранении в переменную, в качестве аргумента необходимо передать объект модели, с которым будет работать форма. Если этого не сделать, при сохранении данных будет создаваться новый объект модели, а не обновляться необходимый.

В качестве аргумента для формы UserInfoForm мы передаём ключевой аргумент instance с объектом пользователя.
А для формы UserPasswordForm, мы передаём объект пользователя в качестве позиционного аргумента.

Третьим мы переопределим метод post. Он достаточно большой и с первого взгляда можно запутаться, но всё достаточно тривиально.

В теле метода у нас находится if-elif-else конструкция.
- if проверяет отправку формы с изменением данных.
- elif проверяет отправку формы изменения пароля.
- else возвращает эту же страницу без изменений.

Структура внутри if и elif в целом одинаковая, различается только действие в случае успеха.

В переменную form помещаем нашу форму, передав в неё объект пользователя и данные отправленные с формы на сайте.
Напомню, что для UserInfoForm, объект пользователя передаётся как ключевой аргумент instance, т.е. он будет вторым аргументом, а данные формы как позиционный, т.е. первым.
В случае с формой UserPasswordForm, оба аргумента передаются как позиционные и первым идёт объект пользователя, а вторым данные формы.

Затем проверяем валидность формы.

Если форма валидна и данные корректны:
Сохраняем форму, тем самым внеся изменения в связанный с ней объект пользователя.
Записываем сообщение об успешном изменении, используя функцию messages.
Возвращаем результат.

В этом моменте происходят различия кода.
При обработке формы изменения данных, для возврата результата мы используем функцию redirect, перенаправляющую пользователя на новую страницу. Она нужна, поскольку в адресной строке прописывается имя пользователя и при изменении имени пользователя и вызове стандартного метода get, происходит перенаправление, но путь в адресной строке не меняется, что влечёт за собой ошибки, а при использовании redirect, пользователь переходит на новый URL-адрес, включающий в себя новое имя пользователя.

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

Если форма не валидна:
Получаем контекст представления и при помощи метода render обновляем страницу, включающую в себя введённые ранее пользователем данные.
Код представления:
class UserSettingsView(LoginRequiredMixin, TemplateView):  
template_name = 'user_app/profile_settings_page.html'

def dispatch(self, request, *args, **kwargs):
if request.user == get_object_or_404(User, username=self.kwargs.get('username')):
return super().dispatch(request, *args, **kwargs)
else:
raise HttpResponseForbidden("Вы не имеете доступа к этой странице.")

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_info_form'] = forms.UserInfoForm(instance=self.request.user)
context['user_password_form'] = forms.UserPasswordForm(self.request.user)
context['title'] = f'Настройки профиля {self.request.user}'
return context

def post(self, request, *args, **kwargs):
if 'user_info_form' in request.POST:
form = forms.UserInfoForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
messages.success(request, 'Данные успешно изменены.')
return redirect('user_app:user_profile_settings', form.cleaned_data.get('username'))
else:
context = self.get_context_data(**kwargs)
context['user_info_form'] = form
return render(request, self.template_name, context)
elif 'user_password_form' in request.POST:
form = forms.UserPasswordForm(request.user, request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Пароль успешно изменён.')
return self.get(request, *args, **kwargs)
else:
context = self.get_context_data(**kwargs)
context['user_password_form'] = form
return render(request, self.template_name, context)
else:
return self.get(request, *args, **kwargs)



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

В этом файле прописываем две формы: отображение сообщения об успешном изменении и кнопку возврата в профиль.

Обратите внимание на код кнопок отправки формы. В них мы указываем атрибут name. Именно по этому атрибуту мы отслеживаем в представлении то, какая форма была отправлена.
Код шаблона:
{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}

{% block content %}
<div class="container mt-3">
<div class="row d-flex justify-content-between align-items-center">
<div class="col-lg-10 col-sm-12">
<h2>Настройки профиля {{ user }}</h2>
</div>
<div class="col-lg-2 col-sm-12 text-right">
<a class="btn btn-primary my-btn h-100"
href="{% url 'user_app:user_profile' username=user.username %}">Вернуться в профиль</a>
</div>
</div>
<hr>
<div class="row d-flex justify-content-evenly">
<div class="row text-center">
{% if messages %}
<p class="alert alert-success">
{% for message in messages %}
{{ message }}<br>
{% endfor %}
</p>
{% endif %}
</div>
<div class="col-lg-4 col-sm-12">
<h3 class="mb-3 mt-3">Изменить данные:</h3>
<form method="post">
{% csrf_token %}
{{ user_info_form.as_p }}
<button class="btn btn-primary my-btn mt-2" type="submit" name="user_info_form">Отправить</button>
</form>
</div>
<div class="col-lg-4 col-sm-12">
<h3 class="mb-3 mt-3">Изменить пароль:</h3>
<form method="post">
{% csrf_token %}
{{ user_password_form.as_p }}
<button class="btn btn-primary my-btn mt-2" type="submit" name="user_password_form">Отправить
</button>
</form>
</div>
</div>
</div>
{% endblock %}



Страница с постами пользователя.

Представление.

В файле views.py создадим класс UserPostsView, унаследованный от ListView.

Пропишем три поля:
- template_name - файл шаблона.
- context_object_name - имя переменной в шаблоне
- paginate_by - количество элементов на странице

Переопределим метод get_queryset. В нём вернём список постов, отфильтрованных по переданному в URL-адресе пользователю.

В переопределённом методе get_context_data, в блоке try-except получим объект пользователя по имени пользователя из URL-адреса и запишем его в контекст.

Код представления:
class UserPostsView(ListView):  
template_name = 'user_app/user_posts_page.html'
context_object_name = 'posts'
paginate_by = 10

def get_queryset(self):
return PostModel.post_manager.filter(author__username=self.kwargs.get('username'))

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
try:
author = get_object_or_404(User, username=self.kwargs.get("username"))
except User.DoesNotExist:
raise Http404("Пользователь не найден")
context['author'] = author
context['title'] = f'Посты пользователя {author}'
return context


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

В качестве шаблона я использовал код из файла tag_page.html.


URL-паттерны страницы настроек и всех постов пользователя.
Откроем файл urls.py в директории приложения user_app и добавим следующие строки в список urlpatterns:
path('<str:username>/settings/', views.UserSettingsView.as_view(), name='user_profile_settings'),  
path('<str:username>/posts/', views.UserPostsView.as_view(), name='user_posts'),



Завершение.
Осталось изменить шаблон страницы профиля, описанный в прошлом посте, а именно, исправить код кнопок.

Кнопка все посты:
<a class="btn btn-primary my-btn mb-3"  
href="{% url 'user_app:user_posts' username=user_profile.username %}">Все посты {{ user_profile }}
</a>


Кнопка настроек профиля:
<a class="btn btn-primary my-btn me-1"  
href="{% url 'user_app:user_profile_settings' username=user_profile.username %}">Настройки профиля
</a>
Готово. За эти два поста, мы реализовали публичный профиль пользователя, страницу настроек и страницу всех постов, используя стандартную модель пользователя Django.

Файлы к посту, можно получить в боте по коду: 828730
Пост на сайте.
Поддержать канал.
🔥2👏1
Позавчера было ровно 15 месяцев как началась учёба в GB.

Как же блин быстро летит время.

Очередной пост на Пикабу: Обучение: пятнадцатый месяц

😉Расчехляйте лайкомёты)
🔥3
😂После релиза программисты становятся лучшими партизанами, а тестировщики ищейками)
🔥4💯1
👴👴👴
1
😂Баян, но блин, не без доли правды)
😁1
Не стареющий баянчик вам в ленту)
🔥5😁2
Приветствую.

У меня почему-то Хэллоуин ассоциируется не то с комедией, не то с трэш-хоррорами. И вот в преддверии этого события, у меня для вас забавный фильм.


Фильм на сегодня: Смерть ей к лицу (1992).

Краткий синопсис: Эликсир вечной молодости! Сколько женщин мечтают о нем! О нем мечтали и бродвейская звезда Мэдлин Эштон, и Хелен Шарп, у которой эта коварная Мэдлин отбила жениха Эрнеста Мэнвилла, гениального врача-специалиста по пластическим операциям.

Прошли годы, и изрядно полинявшие и уставшие друг от друга дамочки, обращаются за чудотворным снадобьем к некоей колдунье. Итак, эликсир выпит, кожа посвежела, груди подтянулись, но скоро качество лекарства будет проверено... смертью.

Посмотреть можно тут: https://www.sspoisk.ru/film/1889/

А какие ассоциации у вас вызывает Хэллоуин?
👍2