#32. Forms
Forms, request, user, authorization
HTML форма
Форма предназначена для обмена данными между пользователем и сервером.
Веб-форма — это набор текстовых полей, списков, кнопок и других элементов управления, посредством которых посетитель страницы может предоставить ей тот или иной вид информации. Формы в Интернете повсюду — благодаря формам мы можем создавать учетные записи электронной почты, просматривать и покупать товары в интернет-магазинах, осуществлять финансовые транзакции и многое другое. Самая простая форма — это одиночное текстовое поле поисковых систем
Тег <form>
устанавливает форму на веб-странице. Он “сообщает” браузеру, что данные из этого тега нужно сгруппировать и подготовить к отправке на сервер.
Принимает два параметра: action
и method
.
-
action
описывает куда форма должна оправить данные, в нашем случае это будетurl
, если не указан явно, то форма будет отправлена на тот же урл, на котором сейчас находится. -
method
- отвечает за метод запроса, варианты этоget
иpost
.get
используется по умолчанию.
Post-формы используются для передачи данных, не подлежащих огласке, например, логин и пароль. Get-формы, используются для общедоступной информации, например строка поиска.
Внутри формы мы указываем нужное кол-во тегов <input>
с нужными типами. Именно эти данные будут в последствии переданы серверу.
<form>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname">
</form>
Основные типы инпутов
number - ввод числа
text - ввод текста
checkbox - чекбокс (выбор нескольких элементов через галочки)
radio - радиобаттон (выбор только одного элемента из списка)
button - классическая кнопка (если в форме есть один такое элемент, но нет сабмита, браузер автоматически подсчитает его сабмитом)
hidden - скрытое поле, чаще всего нужно для целей безопасности или добавления информации и данным не отображая её ( Не отображается)
submit - отправка формы
Это далеко не все типы инпутов, которые могут быть. Ссылка на типы инпутов
GET форма
Создадим простейшую форму, для этого добавим url в urlpatterns
, функцию во views.py
для обработки и html страницу в templates
.
В urls.py
from .views import form_view
urlpatterns = [
path('form-url/', form_view, name='form-view'),
]
Во views.py
def form_view(request):
return render(request, 'form.html')
В form.html
{% extends 'base.html' %}
{% block content %}
<form method="get" action="{% url 'form-view' %}">
<label>
<input type="text" name="my_name">
</label>
<button type="submit">Send name</button>
</form>
{% endblock %}
Обратите внимание, мы указали get форму, экшеном является url
, на эту же страницу, который обрабатывается нашей же функцией form_view
.
В форме у нас один input
которому мы указали 2 аттрибута type
и name
.
Аттрибут name
нам необходим для того, что бы мы смогли обработать данные во view.
Также у нас есть кнопка submit
, необходима для того, что бы отправить запрос, на сервер.
При нажатии на кнопку формируется и отправляется request.
Примерно вот так будет выглядеть наша страница если зайти на адрес http://127.0.0.1:8000/form-url/:
request
Объект request
, тот который принимает первым параметром функции во views.py
и его же передаём первым в функцию render
. Зачем он нужен, и из чего он состоит?
Зачем нужен. Нужен для того, что бы обрабатывать любые пользовательские или служебные данные которые были переданы.
Из чего состоит. Состоит из переданных данных или файлов (если были переданы), и служебной информации (информация о пользователе, метод запроса, о том на какой url
был запрос, из какого браузера, другая системная информация)
Что будет после нажатия кнопки Send name
?
Будет сформирован GET
(метод формы) запрос всеми заполненными нами данными и отправлен на сервер.
Обратите внимание на новый url.
my_name
- это предварительно указанный атрибут name
на нашей форме, а user
значение которое я передал в этот input
.
В случае GET
запроса данные передаются явно, прям в url
в виде ключ-значение. Если бы значений было бы больше одного они были бы соединены при помощи символа &
(например, если бы я добавил еще и поле с указанным атрибутом name
со значением age
и заполнил бы его значением 26
, то url
после запроса выглядел бы так /form-url/?my_name=Vlad&age=26
. Никакой разницы между заполнением формы, или записью этих данных руками прям в строке браузера для GET
запроса нет.
Обработка данных во view
Мы можем обработать данные во view
, при помощи переменной request
, данные из GET
запроса, будет находиться в переменной request.GET
анные находятся в виде словаря, где ключами является атрибут name
в каждом инпуте формы.
Эти данные можно использовать для любых целей, но чаще всего через GET
передаются данные по фильтрации, или дополнительные параметры отображения, например, когда вы добавляете фильтры в интернет магазине, пишете текст в поиске, или когда на ютубе пересылаете ссылку с таймкодом, она тоже передаётся как GET
параметр
POST запрос
Давайте заменим метод нашей формы с get
на post
:
В form.html
:
{% extends 'base.html' %}
{% block content %}
<form method="post" action="{% url 'form-view' %}">{
<label>
<input type="text" name="my_name">
</label>
<button type="submit">Send name</button>
</form>
{% endblock %}
Что произойдёт при отправке такого запроса?
Произойдёт ошибка, примерно вот такая:
Это ошибка CSRF токена.
CSRF (англ. cross-site request forgery — «межсайтовая подделка запроса», также известна как XSRF) — вид атак на посетителей веб-сайтов, использующий недостатки протокола HTTP. Если жертва заходит на сайт, созданный злоумышленником, от её лица тайно отправляется запрос на другой сервер (например, на сервер платёжной системы), осуществляющий некую вредоносную операцию (например, перевод денег на счёт злоумышленника). Для осуществления данной атаки жертва должна быть аутентифицирована на том сервере, на который отправляется запрос, и этот запрос не должен требовать какого-либо подтверждения со стороны пользователя, которое не может быть проигнорировано или подделано атакующим скриптом.
Что бы понять что такое CSRF токена, нужно понимать разницу того, где используются разные запросы.
GET
запросы, это запросы общедоступные и информационные, открыть страницу, отфильтровать данные итд.
POST
запросы, это запросы с чувствительными данными, создание записей в базе, передача пароля, отправка денег со счёта на счёт, итд.
Так вот, если GET
запрос отправить 5 раз подряд, то с точки зрения сервера ничего не изменится, вы просто 5 раз подряд запросите одну и туже информацию.
Если изменить параметры, то тоже ничего страшного не произойдёт, просто запросятся другие данные.
А вот если повторить несколько раз или подделать данные в POST
запросе, то можно совершить разные проблемные действия, создание лишних записей в базе данных, или перевод средств на счёт злоумышленников вместо ожидаемого, итд.
Поэтому в Django изначально есть дополнительное требование к POST
формам, это еще одно скрытое поле, заранее сгенерированное сервером. Оно называется CSRF токен
, где он проверяется и почему мы видим ошибку, мы разберём на следующих занятиях.
Для того что бы добавить нужный токен, используется специальный темплейт тег {% csrf_token %}
, его нужно добавить в любом месте внутри тега <form>
{% extends 'base.html' %}
{% block content %}
<form method="post" action="{% url 'form-view' %}">
{% csrf_token %}
<label>
<input type="text" name="my_name">
</label>
<button type="submit">Send name</button>
</form>
{% endblock %}
Что изменится с точки зрения html:
<html lang="en"><head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="background-color: aqua">
<form method="post" action="/form-url/">
<input type="hidden" name="csrfmiddlewaretoken" value="Lr9N7x9yZaksWqLpz5GXgqiiSOAgc4kCkRCGW0A5bas9mc50H87PMAqW1Jdu7iBy">
<input type="text" name="my_name">
<button type="submit">Send name</button>
</form>
</div>
</body>
</html>
Появилось поле типа hidden
, это значит, что оно не будет отображаться, но эти данные все равно попадут на сервер. Это часто используется когда вам нужно передать данные которые у тебя уже есть при отрисовке, но их не видно явно, допустим есть мы пишем коментарий к коментарию, то что бы грамотно его создать, нам нужен id родителя, его обычно и передают как hidden
филд.
Теперь наш запрос отправится успешно.
Обратите внимание, урл не изменятся!
Потому что данные отправленные через POST
не должны быть общедоступны.
Обработка во view
Обработать данные из POST
запроса, можно точно так же, данные будут находиться в переменной request.POST
, если это просто данные и в request.FILES
если были переданы файлы.
Обратите внимание, что вместе с нашими данными был передан и csrf
токен. Обычно при обработке данных он не нужен, но данные были переданы, а значит они прийдут на сервер.
Джанго формы
Джанго предоставляет нам возможность генерировать html
формы из кода на python
!
Что для этого нужно. Создадим в нашем приложении файл forms.py
Внутри этого файла укажем.
forms.py
from django import forms
class MyForm(forms.Form):
nickname = forms.CharField(label='My nickname', max_length=100)
age = forms.IntegerField(label='My age')
Обработчик для урла заменим на:
Во views.py
заменим нашу функцию на:
from django.shortcuts import render
from .forms import MyForm
def form_view(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = MyForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# some actions
return render(request, 'form_was_valid.html')
# if a GET (or any other method) we'll create a blank form
else:
form = MyForm()
return render(request, 'form.html', {'form': form})
Не забываем импортировать форму
Изменим файл form.html
:
{% extends 'base.html' %}
{% block content %}
<form method="post" action="{% url 'form-view' %}">
{% csrf_token %}
{{ form }} {# Инпуты были заменены на переменную, в которой лежит объект класса Form #}
<button type="submit">Send form</button>
</form>
{% endblock %}
и создадим файл form_was_valid.html
:
{% extends 'base.html' %}
{% block content %}
<div style="background-color: deeppink"> FORM WAS VALID</div>
<a href="{% url 'form-view' %}">To the form page</a>
{% endblock %}
Что именно мы сделали.
Описание формы
В файле forms.py
мы создали класс формы, в котором описали два атрибута nickname
и age
.
Они будут соответствовать двум инпутам, текстовому и числовому.
Типов существует естественно гораздо больше
Основные типы:
BooleanField
- булеан
CharField
- текст
ChoiceField
- поле для выбора
DateTimeField
- дата время
EmailField
- имейл
FileField
- файл
IntegerField
- целое число
MultipleChoiceField
- множественный выбор
И многие другие, почитать про них нужно тут
У каждого поля есть дополнительные параметры
Агрументы полей:
required
- является ли поле обязательным
label
- лейбл, текст к инпуту
label_suffix
- символ между лейблом и инпутом
initial
- значение по умолчанию
widget
- описано ниже
help_text
- подсказка к инпуту
error_messages
- переписать стандартные тексты для ошибок типов полей
validators
- дополнительные проверки поля
localize
- информация о переводе формы на другие языки
disabled
- сделать поле не активным (без возможности изменения)
Подробнее тут
Виджеты
У полей формы есть такое понятие как виджет, он отвечает за то, как именно будет отображаться конкретный филд, например, для текста базово это текстовое поле, а для даты и времени, это встроенный пикер (выпадающее окно с календарём и часами) итд.
Виджет можно указать отличающийся от стандартного.
Прочитать про виджеты нужно тут.
Описание view
В переменной request хранится информация о том, какой именно тип запроса к нам пришел, а это значит, что простым if мы можем разграничить логику которая будет обрабатывать разные типы запросов.
Если мы просто открываем страницу в браузере, то на самом деле мы посылаем обыкновенный GET
запрос.
Взглянем на код. При GET
запросе, мы не попадаем в первое условие, переменной form
назначаем объект класса MyForm
без каких либо данных, и после этого рендерим страницу передав на страницу пустой объект класса формы.
При рендере объекта класса формы в шаблоне, этот объект преобразуется в набор инпутов, с уже указанными атрибутами name
Если мы заполним данные, и нажмём на кнопку Send form
, то мы отправим по этому же урлу запрос, но уже типа POST
с заполнеными данными.
Посмотрим в код еще раз, мы попадём в первый if, и переменной form
назначим объект класса MyFrom
, но предварительно, передав туда данные через request.POST
.
А значит на этом этапе у нас есть объект, с данными переданными нам от клиента.
Данные которые мы получили из реквеста всегда нужно валидировать (проверять)
Валидация формы
Тут вся документация по валидации.
За валидацию данных в форме отвечает встроенный метод is_valid()
который применяется к объекту класса формы.
Этот метод возвращает нам булевое значение, True если данные валидны, False если нет.
После вызова этого метода у переменной к которой он был вызван (в нашем случае переменная form
) появляются дополнительные атрибуты
Если форма была валидна, то появляется дополнительный аттрибут cleaned_data
, это словарь, в котором будет хранятся все данные которые прислал нам пользователь (например логин и пароль)
Если форма не была валидна, то появляется дополнительные аттрибут errors
, который хранит в себе информацию об ошибках конкретных полей, или общих ошибках.
Этот атрибут сразу хранит информацию как отображать эти ошибки в шаблоне, если они существуют
Валидность
Что же вообще такое валидность?
Валидность это соответсвие заданным критериям. Например, если мы ожидаем, что нам пришлют возраст, и мы ожидаем цифру, но нам присылают букву, это значит, что данные не валидны.
Некоторые распространённые виды валидаций можно указать как атрибут поля формы, например, максимальную длинну для строки, или максимальное и минимальное значение для числа.
clean_field
Если мы вызываем метод is_valid()
мы проверяем все описанные валидации. Но где они описаны, и можем ли мы добавить свои?
Описаны они в классе формы, и да мы можем добавить свои.
Все базовые валидации были описаны при создании полей.
Но допустим мы считаем что для нашей формы валидным является только чётный возраст, как нам это проверить?
Для проверки конкретного поля, в форме класса нужно указать, метод который будет начинатся со слова clean_
и после этого название поля, которое мы валидируем.
Все данные будут лежать в аттрибуте self.cleaned_data
.
Если значение валидно, то метод должен возвращать значение этого аттрибута.
Если значение не валидно, то метод должен рейзить ошибку ValidationError
с описанием ошибки, которая позже будет отображаться на html.
В forms.py
:
from django import forms
from django.core.exceptions import ValidationError
class MyForm(forms.Form):
nickname = forms.CharField(label='My nickname', max_length=100)
age = forms.IntegerField(label='My age')
def clean_age(self):
age = self.cleaned_data.get('age')
if not age % 2:
raise ValidationError('Age should be odd')
return age
clean
А что делать если нужно проверить соответствие данных между собой, например, что пользователь не использовал свой возраст как часть своего никнейма?
Для этого мы можем использовать метод clean
в котором можем выполнить любые необходимые нам проверки.
Для выполнения всех базовых проверок обычно используется super
В forms.py
from django import forms
from django.core.exceptions import ValidationError
class MyForm(forms.Form):
nickname = forms.CharField(label='My nickname', max_length=100)
age = forms.IntegerField(label='My age')
def clean_age(self):
age = self.cleaned_data.get('age')
if not age % 2:
raise ValidationError('Age should be odd')
return age
def clean(self):
cleaned_data = super().clean()
age = cleaned_data.get('age')
nickname = cleaned_data.get('nickname')
if str(age) in nickname:
raise ValidationError('Age cannot be in nickname')
Метод clean
ничего не возвращает, это нормально :)
Если при проверке у вас может быть больше одной ошибки, то raise
вам не подходит.
Для этого может использоваться метод класса формы add_error
, принимает два параметра, название поля к которому относится ошибка (может быть None, если ошибка не относится к какому либо полю, например неправильный юзернейм и/или пароль.
В forms.py
:
from django import forms
from django.core.exceptions import ValidationError
class MyForm(forms.Form):
nickname = forms.CharField(label='My nickname', max_length=100)
age = forms.IntegerField(label='My age')
def clean_age(self):
age = self.cleaned_data.get('age')
if not age % 2:
raise ValidationError('Age should be odd')
return age
def clean(self):
cleaned_data = super().clean()
age = cleaned_data.get('age')
nickname = cleaned_data.get('nickname')
if str(age) in nickname:
self.add_error('age', 'Age cannot be in nickname')
self.add_error(None, 'This form always incorrect')
Отображение формы в шаблоне.
Итак, если наша форма была валидна, то мы отрендерили вообще другую страницу, но если всё таки была не валидна, то мы отрендерим форму, у которой есть атрибут errors
, ошибки сразу же будут отрисованы.
Так же у нас есть способы по разному отрисовывать формы:
У объекта формы есть стандартные поля и методы, которые мы можем указывать в шаблоне, например.
{{ form.as_table }}
- рендер в виде таблицы, через теги
{{ form.as_p }}
рендер каждого поля через
теги
{{ form.as_ul }}
рендер в виде списка через теги
Так-же, можно рендерить форму не целиком, а например, по отдельным филдам, при помощи стандартного обращения через точку, например {{ form.name }}
У каждого поля есть аттрибут errors
который хранит информацию об ошибках по этому полю, если они были обнаружены. {{ form.my_field.errors }}
Если запустить форму через for в итерируемом объекте будут поля.
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
И многие другие атрибуты и методы, подробно можно прочитать тут
Методы и свойства модели User
Модель User
Django предоставляет нам встроенную модель юзера, у которой уже реализовано много полей и методов
Документация про юзера тут
class User(AbstractUser):
"""
Users within the Django authentication system are represented by this model.
Username and password are required. Other fields are optional.
"""
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
Стандартный юзер содержит в себе такие полезные поля как:
username
password
email
first_name
last_name
is_active
is_staff
is_superuser
Также содержит много системных полей.
Также содержит базовый метод set_password и информацию о группах доступа.
Для использования модели пользователя, которую нам нужно расширить (а это нужно почти всегда) используется наследование от базового абстрактного юзера.
Выглядит примерно так:
from django.contrib.auth.models import AbstractUser
from django.db import models
class MyUser(AbstractUser):
birth_date = models.DateField()
avatar = models.ImageField(blank=True, null=True)
Для того, что бы Django оценивала эту модель как пользователя, в settings.py
нужно в любом месте указать:
AUTH_USER_MODEL = 'myapp.MyUser'
Где myapp
- название приложения, MyUser
- название модели.
Юзер обязательно
должен быть описан до первой миграции!! Иначе джанго автоматически будет использовать базового встроенного юзера, и использовать сразу несколько у вас не получится. (Это работает потому что если не переписать модель и настройку, то юзер который уже есть в базе данных таблицу и её нельзя будет переопределить под другую модель после)
Все возможные подробности про модель юзера тут
Кроме стандартных полей юзер содержит в себе информацию о группах которых состоит пользователь, о пользовательских правах, содержит два поля статуса is_staff
и is_superuser
чаще всего стафф это сотрудника которым можно в админку, но у них ограниченные права, суперюзеру можно всё, но всегда зависит от ситуации.
is_stuff
- поле для определения сотрудника (допустим сотрудник магазина, который добавляет товары)
is_superuser
- поле для определения администратора (например, может изменять список сотрудников, а чаще всего обладает практически не ограниченными параметрами)
Также хранит инфо о последнем логине пользователя и дате создания пользователя.
Объект юзера содержит много полезных методов
get_username() # получить юзернейм
get_full_name() # получить имя и фамилию через пробел
set_password(raw_password) # установить хешированый пароль
check_password(raw_password) # проверить пароль на правильность
set_unusable_password() # разрешить использовать пустую строку как пароль
email_user(subject, message, from_email=None, **kwargs) # отправить пользователю имейл
Например:
from django.contrib.auth.models import User
u = User.objects.get(username='blabla')
u.check_password('some_cool_password') # True
И другие методы, отвечающие за доступы, группы итд.
Менеджер юзера
Содержит дополнительные методы
`create_user(username, email=None, password=None, **extra_fields)`
`create_superuser(username, email, password, **extra_fields)`
create_user
отличается от create
тем, что create_user
правильно задаст пароль, через set_password
Мы не храним пароль в чистом виде, только хешированым
Логин
На самом деле логин состоит из нескольких частей, давайте их рассмотрим
Идентификация, Аутентификация, Авторизация
Идентификация — это процедура распознавания субъекта по его идентификатору (проще говоря, это процесс определения конкретного лица по имени, логину или номеру).
Например, получить конкретного пользователя из базы.
Идентификатором может быть:
- номер телефона
- номер паспорта
- номер страницы в социальной сети и т.д.
Аутентификация – это процедура проверки подлинности (пользователя проверяют с помощью пароля, письмо проверяют по электронной подписи и т.д.)
Например, проверить логин и пароль на соответствие.
- Пароль – то, что мы знаем (слово, PIN-код, код для замка, графический ключ)
- Устройство – то, что мы имеем (пластиковая карта, ключ от замка, USB-ключ)
- Биометрика – то, что является частью нас (отпечаток пальца, портрет, сетчатка глаза)
Авторизация – это предоставление доступа к какому-либо ресурсу.
Например, к электронной почте.
Примеры авторизации:
- Открытие двери после проворачивания ключа в замке
- Доступ к электронной почте после ввода пароля
- Разблокировка смартфона после сканирования отпечатка пальца
- Выдача средств в банке после проверки паспорта и данных о вашем счете
Как это работает
Для того, что бы пользователь мог авторизоваться на сайте, нам нужны его входные данные и стандартные методы authenticate, login
Метод authenticate
отвечает сразу за два процесса, аутентификации и идентификации, принимает, имя пользователя и пароль, и если находит совпадение то, возвращает объект пользователя(модели), если не находит, то возвращает None
.
Если нам вернулся объект юзера, значит что аутентификация пройдена, и пользователь идентифицирован.
Метод login
принимает, реквест, и объект модели пользователя и отвечает за процесс авторизации, после этого действия, во всех следующих запросах, в переменной request
будет хранится наш текущий пользователь.
По этому стандартным способом, для авторизации является примерно такой код:
В forms.py
from django.contrib.auth import authenticate
from django import forms
class AuthenticationForm(forms.Form):
username = forms.CharField(max_length=254)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
if username and password:
self.user = authenticate(username=username, password=password)
if self.user is None:
raise forms.ValidationError()
в view.py
from .forms import AuthenticationForm
from django.contrib.auth import login
def my_login(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = AuthenticationForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# some actions
login(request, form.user)
return HttpResponseRedirect('/')
# if a GET (or any other method) we'll create a blank form
else:
form = AuthenticationForm()
return render(request, 'login.html', {'form': form})
Logout
Для вывода пользователя из системы, используется метод logout
который принимает только реквест.
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
Проверка на то, что пользователь уже зашел в систему
В реквесте всегда есть поле user
, у которого всегда есть аттрибут is_authenticated
проверяя его, мы можем определять является ли пользователь авторизированным
request.user.is_authenticated
Закрыть страницу от не залогиненего пользователя
Для того, что бы не предоставлять доступ, для не залогиненых пользователей, существует два способа, для функционально описанных вью это декоратор @login_required
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
Он так-же может принимать ссылку, на страницу логина, и автоматически отправлять на эту страницу, для не залогиненного пользователя.
from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def my_view(request):
pass
В теплейтах user доступен через переменную user
{% if user.is_authenticated %}
{{ user.username }}
{% else %}
Anonymous
{% endif %}
Домашнее задание / Практика:
-
Напишите форму, в которой можно указать имя, пол, возраст и уровень владения английским (выпадающим списком), если введенные данные это парень старше 20-и (включительно) и уровнем английского B2 выше, или девушка старше 22-ух и уровнем выше чем B1 то перейти на страницу где будет написано, что вы нам подходите, или что не подходит соответсвенно.
-
Пишем страницу логина и логаута руками, проверяем, что всё работает.
-
Написать страницу для регистрации. (не забываем про
set_password
)
Следующая страница должна открываться только залогиненым пользователям -
Пишем страницу для смены пароля. (Запрашиваем текущий пароль 2 раза, и проверяем через
check_password
) -
Написать страницу с гет формой, для поиска по тексту ваших комментариев, отобразить все найденные частичные совпадение, без учёта регистра.
-
Добавить к поиску по комментариям галочку, что бы при нажатой галочке показывало только твои комментарии