Код на салфетке
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
Состоялся релиз третьей версии AIOgram.

Обновить версию можно с помощью PyPI: pip install -U aiogram.
Но имейте в виду, что в этой версии есть много критических изменений относительно второй версии.

В связи с этим, я подготовил перевод документации по миграции со второй на третью версию библиотеки.
Ознакомиться с материалом можно на сайте.
🔥3
Было две викторины, на тему создания Django проекта и Django приложения.
Результаты следующие:
- Создание Django проекта - 46% верных ответов среди 28 участвующих.
- Создание Django приложения - 58% верных ответов среди 24 участвующих.

В связи с этим небольшой пост, по созданию проекта и приложения с кратким описанием структуры.
🔥1
Создание Django проекта
Чтобы создать проект в Django, нам нужно открыть терминал и перейти в папку, где мы хотим создать наш Django проект.

Здесь мы запускаем команду:

django-admin startproject myproject


Эта команда создаст для нас новый каталог, названный myproject, и заполнит его файлами, необходимыми для создания проекта Django.


Структура проекта Django
Давайте рассмотрим структуру директорий и файлов, созданных командой startproject.

myproject/
|-- myproject/
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| |-- asgi.py
| `-- wsgi.py
`-- manage.py


- myproject/ - это каталог верхнего уровня, который содержит наш Django проект.
- myproject/myproject/ - это каталог, который содержит первое Django приложение.
- myproject/__init__.py - пустой файл, который просто говорит Python, что каталог myproject должен рассматриваться как Python-пакет.
- myproject/settings.py - - файл, содержащий настройки Django-проекта, такие как база данных, интернационализация, часовой пояс, а также другие настройки.
- myproject/urls.py - - файл, содержащий шаблоны URL-адресов проекта Django. Как только вы определите эти шаблоны, Django будет использовать их для пересылки пользователей на соответствующее представление.
- myproject/asgi.py и myproject/wsgi.py - файлы, содержащие настройки для ASGI и WSGI серверов. ASGI - это новый способ работы с асинхронным Python, в то время как WSGI - это протокол для связи Python-приложений и веб-сервера.
- manage.py - исполняемый файл, который используется для запуска различных команд в веб-приложении Django, например запуска сервера разработки, создания миграций базы данных и многое другое.


Создание приложения в Django
Для создания нового приложения в Django необходимо запустить следующую команду из терминала:

python manage.py startapp my_app


Где my_app - это название нового приложения.

После выполнения этой команды Django создаст новый каталог с именем my_app внутри вашего проекта. В этом каталоге будут находиться необходимые файлы для создания нового приложения.


Структура приложения Django
Рассмотрим структуру каталогов и файлов, которые были созданы при выполнении команды startapp:

my_app/
|-- __init__.py
|-- admin.py
|-- apps.py
|-- models.py
|-- tests.py
`-- views.py


- __init__.py - пустой файл, который указывает Python, что каталог my_app должен рассматриваться как Python-пакет.
- admin.py - файл, содержащий настройки администраторской панели (Django Admin) для вашего приложения.
- apps.py - файл, содержащий настройки приложения, такие как его название.
- models.py - файл, содержащий определения моделей для вашего приложения. Модели - это объектно-реляционная отображаемая таблица базы данных.
- tests.py - файл, содержащий модули тестирования для вашего приложения.
- views.py - файл, содержащий определения представлений (views) для вашего приложения. В этих представлениях определено, что происходит, когда пользователь переходит на страницу вашего сайта.
👍6
Django 28. Добавляем пагинацию на сайт

Пагинация - это способ разделения больших объёмов данных на отдельные страницы, например, вывод списка пользователей, товаров или записей блога.

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

Добавление функционала достаточно простое, но оно отличается в зависимости от способа написания представлений.

Разберём оба варианта.
🔥2
Изменение представлений.

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

Для добавления пагинации в данные страницы достаточно добавить в код представления буквально одно поле - paginate_by с указанием количества элементов на страницу.

На примере класса страницы тегов:
class TagPageView(ListView):  
model = models.PostModel
template_name = 'blog/tag_page.html'
context_object_name = 'posts'
paginate_by = 10

# методы класса

Пагинация в FBV.
В функциональных представлениях пагинация добавляется немного другим способом.

В функции представления необходимо добавить три переменные.
1. paginator - ей присваиваем объект класса Paginator, передавая в него переменную со списком постов и количество элементов на страницу.
2. page_number - в неё получаем значение текущей страницы, если его нет, то по умолчанию устанавливаем 1 страницу.
3. posts - переменная, в которую получаем список постов для конкретной страницы

На примере функции страницы тегов:
from django.core.paginator import Paginator


def tag_page(request, tag_name):
posts_list = models.PostModel.objects.filter(tags__slug=tag_name).distinct()

paginator = Paginator(posts_list, 10)
page_number = request.GET.get('page', 1)
posts = paginator.page(page_number)

context = {
'tag_name': tag_name,
'posts': posts,
}

return render(request,
'blog/tag_page.html',
context)


Добавляем в шаблон.
После передачи данных в шаблон их нужно обработать.

Для оформления вывода будем использовать стили пагинатора из Bootstrap.

Перейдём в директорию шаблонов и создадим файл pagination.html. Для удобства можно создать директорию modules и поместить его туда.

В файле напишем три блока:
- Первый отвечает за кнопку назад, функционал и отображение.
- Второй за кнопки страниц.
- Третий за кнопку вперёд.

Код.
<nav aria-label="Page navigation">  
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Назад</a></li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Назад</span>
</li> {% endif %}

{% for num_page in page_obj.paginator.page_range %}
{% if num_page == page_obj.number %}
<li class="page-item active"><a class="page-link" href="#">{{ num_page }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ num_page }}">{{ num_page }}</a></li>
{% endif %}
{% endfor %}

{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Вперед</a></li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Вперед</span>
</li> {% endif %}
</ul>
</nav>

В данном файле мы получаем все данные из переменной page_obj. Это переменная, представляющая собой объект страницы.


Подключаем на странице.
Теперь этот файл необходимо подключить в шаблоне страницы, продолжим пример на странице тегов.
Откроем файл tag_page.html.

До закрытия тега div с классом container добавляем следующий блок:

Для CBV:
<div class="col-12">  
{% include "blog/modules/pagination.html" %}
</div>

Для FBV:
<div class="col-12">  
{% include "blog/modules/pagination.html" with page_obj=posts %}
</div>

Обратите внимание. Во втором случае, мы передаём в шаблон переменную posts под видом переменной page_obj. Этого можно и не делать, заменив в файле pagination.html все page_obj на posts

Теперь можно запустить Django и убедиться, что пагинация работает.

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

Сегодня, в день программиста.

Хочу пожелать всем причастным хорошего и чистого кода, не требовательных заказчиков и что бы все задачи решаемые вами были в радость, а не вызывали отторжение.
12
Django 29.1 Добавляем поиск на сайт

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

В Django есть простой механизм поиска с помощью фильтра contains или icontains. Первый чувствителен к регистру, а второй нет.
Например: PostModel.objects.filter(full_body__icontains='поисковый запрос').
Но такой поиск ищет "в лоб" точно подходящие слова или фразы.

Помимо этого способа, используя СУБД PostgreSQL, открываются расширенные возможности поиска. Такие, как выдача результатов в зависимости от частоты встречаемого запроса в тексте или поиск по триграммному сходству.

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

Откроем файл views.py и создадим новый класс SearchPageView унаследованный от TemplateView.

Код класса:
from django.shortcuts import render  
from django.views.generic import TemplateView
from django.core.paginator import Paginator

from blog import models, forms

class SearchPageView(TemplateView):
template_name = 'blog/search.html'

def get(self, request, *args, **kwargs):
form = forms.SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']

# ... тут код поиска

paginator = Paginator(results, 10)
page_number = request.GET.get('page', 1)
results = paginator.get_page(page_number)

context = {"query": query,
"results": results}

return render(request,
self.template_name,
context)

return render(request,
self.template_name,
{"query": request.GET['query']})


В теле класса пропишем поле template_name в котором укажем на шаблон страницы поиска.

Далее создадим метод get, принимающий self, request, *args, **kwargs.

В нём создадим переменную form и присвоим ей нашу будущую форму поиска, передав в неё GET-запрос - forms.SearchForm(request.GET)

Затем проверка на валидность формы. Если форма валидна, в переменную query получаем поисковый запрос очищенный от лишних пробелов.

Далее идёт код поиска, его рассмотрим чуть позже.

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

Создаём переменную context, передавая в неё поисковый запрос и результаты поиска.

Ниже возвращаем рендер страницы с результатами поиска. А вне блока if прописываем ещё один рендер, возвращающий страницу с поисковым запросом, но без результатов.

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

**Поиск по частоте упоминания в тексте или полнотекстовый поиск**
Код:
from django.contrib.postgres.search import SearchVector, SearchRank, SearchQuery

search_vector = SearchVector('title', 'full_body')
search_query = SearchQuery(query)
results = (models.PostModel.post_manager.annotate(search=search_vector,
rank=SearchRank(search_vector, search_query,
cover_density=True)
).filter(search=search_query)).order_by('-rank')



Сначала определяем поля для поиска с помощью `SearchVector`. В нашем случае, поиск будет производиться по полям 'title' и 'full_body'.

Затем создаем объект `SearchQuery`, который представляет собой запрос для поиска, передавая в него поисковый запрос.

Затем осуществляется фильтрация и сортировка результатов поиска:

Обращаясь к нашему собственному менеджеру постов, выдающему только опубликованные посты, методом annotate` к списку постов мы добавляем новое поле `search. Которое содержит поисковые вектора.

SearchRank` используется для присвоения ранга каждому результату поиска, с учетом `search_vector` и `search_query. Здесь установлен параметр `cover_density=True`, чтобы учесть плотность ключевых слов на странице.

В итоге, результаты фильтруются по `search_query` и сортируются в порядке убывания ранга (-rank).


Поиск по сходству или триграммный поиск.
Код:
from django.contrib.postgres.search import TrigramSimilarity

similarity = (TrigramSimilarity('full_body', query) +
TrigramSimilarity('title', query))
results = models.PostModel.post_manager.annotate(
similarity=similarity
).filter(similarity__gt=0.1).order_by('-similarity')
В переменной similarity находится результат применения `TrigramSimilarity` - Это функция, которая на основе алгоритма трехграмм вычисляет сходство между строками. В нашем случае функция применяется к полям 'full_body' и 'title'.

Далее, как и в предыдущем коде используется собственный менеджер и метод annotate.

Затем происходит фильтрация результатов. Используется фильтрация по значению `similarity`, где сходство больше 0.1. То есть, в результаты попадут только те посты, у которых сходство с запросом больше 0.1.

В итоге сортируем результаты в порядке схожести с запросом.

Для своего сайта я выбрал второй вариант.

Продолжение в следующем посте.


Пост на сайте.

Поддержать канал.
Django 29.2 Добавляем поиск на сайт, продолжение

Форма поиска.
Напишем форму поиска. Для этого в директории приложения создадим новый файл forms.py.

Внутри файла создадим класс SearchForm унаследованный от forms.Form.

Пропишем поле query. Поле будет являться символьным полем с максимальной длинной в 100 символов.

Ниже напишем метод clean_query. Данный метод будет очищать поисковый запрос от лишних пробелов внутри строки.

Код формы:
from django import forms  


class SearchForm(forms.Form):
query = forms.CharField(max_length=100,
widget=forms.TextInput(
attrs={
'class': 'form-control me-2 mx-1',
'placeholder': 'Что ищем?',
}
))

def clean_query(self):
query = self.cleaned_data['query']
cleaned_query = " ".join(query.split())
return cleaned_query
🔥1
Собственный менеджер для постов.

Нам также понадобится собственный менеджер, возвращающий только опубликованные посты.

Откроем файл models.py и после импортов создадим класс PostManager унаследованный от models.Manager.
Внутри класса переопределим метод get_queryset.

Код менеджера:
class PostManager(models.Manager):  
def get_queryset(self):
return super().get_queryset().filter(status=PostModel.Status.PUBLISHED)


Далее найдём модель поста и добавим новое поле, определяющее нашего менеджера - post_manager = PostManager()


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

Откроем или создадим файл, если у вас его нет, context_processors.py.

В файле напишем функцию get_search_form принимающую request.
В теле функции сразу пишем возврат словаря, в котором будет ключ search_form, а значением форма.

Код:
from blog import forms

def get_search_form(request):
return {'search_form': forms.SearchForm()}


Далее необходимо зарегистрировать функцию. Открываем файл settings.py и находим переменную TEMPLATES.
В ней находим список context_processors и в него дописываем функцию - 'blog.context_processors.get_search_form',

Также не уходя из файла, в переменную INSTALLED_APPS добавим строку - 'django.contrib.postgres',.


URL-маршрут для страницы поиска.
Откроем файл urls.py приложения и добавим новую строку:
path('search/', views.SearchPageView.as_view(), name='search_page'),


Указывая по какому адресу будет открываться поиск, какое представление за него отвечает и какое имя будет у маршрута.

Блок поиска в шапке сайта.
Для добавления поиска в шапку сайта, откройте файл header.html и пропишите в удобном для вас месте следующий код:
<li class="nav-item">
<form class="d-flex" method="GET" action="{% url 'blog:search_page' %}">
{{ search_form.query }}
<button class="btn btn-primary search-btn" type="submit">Поиск</button>
</form>
</li>



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

Откроем файл pagination.html и к каждой ссылке добавим проверку на наличие переменной query. Если она есть, то в адрес добавляется переменная с запросом.
Например, так было:
<li class="page-item"><a class="page-link" href="?page={{ num_page }}">{{ num_page }}</a></li>


А так должно быть:
<li class="page-item"><a class="page-link" href="?page={{ num_page }}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}">{{ num_page }}</a></li>



Шаблон страницы поиска.

В шаблоне будем выводить поисковый запрос пользователя, результаты поиска и пагинацию.
Код шаблона:
{% extends "blog/base.html" %}  
{% block title %}{{ query }}{% endblock %}

{% block content %}
<section class="info">
<div class="container">
<h1>Результаты по запросу: {{ query }}</h1>
<hr> </div> </section>
<section class="posts">
{% if results %}
<div class="container">
<div class="row">
<div class="col-lg-9 col-sm-12 cat-block">
{% for post in results %}
<div class="row">
<div class="col-lg-4 col-sm-12 d-flex align-items-center">
<img src="{{ post.image.url }}" alt="{{ post.title }}" class="img-fluid">
</div> <div class="col-lg-8 col-sm-12">
<h2 class="head2"><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
<div class="mb-3">
<p class="lead"><a
href="{{ post.category.get_absolute_url }}">{{ post.category }}</a>
| {{ post.author }} | {{ post.publish | date:"d F Y" }} |
Просмотров: {{ post.views }}</p>
{% for tag in post.tags.all %}
<a href="{% url 'blog:tag_page' tag.slug %}"><span
class="badge bg-secondary">{{ tag.name }}</span></a>
{% endfor %}
</div>
{{ post.short_body | safe }}
</div>
</div> <hr class="m-3">
{% endfor %}
</div>
<div class="col-lg-3 col-sm-12">
<h2 class="mb-3">Ссылки</h2>
{% block social_links %}
{% include 'blog/index_page/social_links.html' %}
{% endblock %}
</div>
</div> <div class="col-12">
{% include "blog/modules/pagination.html" with page_obj=results %}
</div>
</div> {% else %}
<div class="container">
<h3>Нет результатов</h3>
</div> {% endif %}
</section>
{% endblock %}


На этом всё. Действий много, но они всё "точечные". Теперь на сайте есть работающий поиск.

Файлы к посту, можно получить в боте по коду: 159845
Пост на сайте.
Поддержать канал.
👍3
Полезные инструменты - Certbot - бесплатный SSL-сертификат для сайта

В настоящее время даже думать не стоит о запуске сайта без оснащённого SSL-сертификатом. Более того, в скором времени сайты работающие на HTTP вовсе перестанут открываться в браузерах без "танцев с бубном" и десятка подписей, что вы осознаёте все риски.

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

Существуют разные виды сертификатов, различающиеся лишь "уровнем доверия". В кавычках, потому, что по большому счёту это "плата за воздух" и "бренд", но это отдельная тема для дискуссии.

Нас же интересует бесплатный сертификат Let's Encrypt. Сертификат выдаётся на 3 месяца. Если у вас небольшой сайт или страничка PET-проекта, этого достаточно на первое время.
👍1
Важно.
В данном посте будет показано получение сертификата на ОС Windows 11.
В качестве терминала используется PowerShell версии 7.3.6.
Настройки домена на примере хостинг-провайдера TimeWeb.

Пост по настройке на Linux с возможностью автопродления будет позже.


Что такое SSL-сертификат и для чего он нужен.
SSL-сертификат, или Secure Socket Layer, это цифровая подпись, подтверждающая подлинность веб-сайта и безопасное соединение между пользователем и сервером. SSL-сертификаты необходимы для сайтов, обрабатывающих конфиденциальную информацию, например, данные кредитных карт, личную информацию, данные аутентификации и так далее.

Он обеспечивает следующие функции:
- Шифрование данных: SSL-сертификаты шифруют информацию, передаваемую между пользователем и сайтом, обеспечивая защиту от перехвата и подмены данных.
- Идентификация сайта: SSL-сертификаты содержат информацию о домене, для которого они выданы, что позволяет браузеру убедиться в подлинности сайта.
- Установление безопасного соединения: SSL-сертификаты создают безопасное соединение между браузером пользователя и сервером сайта, защищая от атак “человек посередине”.
- Подпись кода: SSL-сертификаты могут использоваться для подписи исполняемого кода, чтобы подтвердить его подлинность и целостность.


Certbot.
Для получения сертификата будем использовать программу Certbot.

Certbot — это программа с открытым исходным кодом, предназначенная для получения и продления SSL-сертификатов для обеспечения безопасности и доверия на веб-сайтах. Она работает на платформах Linux, macOS и Windows и поддерживает широкий спектр центров сертификации, включая Let’s Encrypt и ACME. Certbot прост в использовании и автоматизирует весь процесс получения и обновления сертификатов.

Официальный сайт программы: https://certbot.eff.org/

Установка.
Необходимо скачать и установить программу.

Скачиваем по ссылке: https://github.com/certbot/certbot/releases/latest/download/certbot-beta-installer-win_amd64_signed.exe

И устанавливаем. Буквально далее-далее-финиш. В процессе можете изменить стандартный путь установки, если необходимо.

Проверить работоспособность можно в терминале введя команду: certbot --version.
В результате выполнения должна отобразиться строчка certbot 2.6.0. (актуальная на момент написания поста версия.)
👍3
Получение сертификата.
Запускаем PowerShell от имени администратора. Это важно! Иначе сертификат невозможно будет сгенерировать!

И вводим следующую команду: certbot certonly --authenticator manual --preferred-challenges dns

Параметры команды:
- certonly - указывает, что мы хотим получить только сертификат.
- --authenticator manual - указывает, что мы в ручном режиме предоставим доказательства владения доменом.
- --preferred-challenges dns - указывает, что в качестве подтверждения будет использоваться TXT-запись в DNS домена.

Выполняем. Далее нас попросят ввести домен или домены через пробел или запятую, к которым будет относиться сертификат. Принято вводить две версии домена, с www и без. Также я дополнительно введу и почтовый поддомен. Выходит такая строка: pressanybutton.ru www.pressanybutton.ru mail.pressanybutton.ru.

Внимание! Есть ещё второй вариант добавления неограниченного количества поддоменов в сертификат. Для этого указываем два домена: domain.ru и *.domain.ru. Это упростит получение сертификата, если у вас несколько поддоменов.
Однако, стоит отметить, что данный способ не указан в документации, гарантировать его работу не могу.

Нажимаем Enter и далее начнётся "испытание".
На этом этапе будьте предельно внимательны! Если вы ввели всего один домен, не спешите нажимать Enter после вывода задания, также, если вы ввели несколько доменов, не нажимайте Enter после вывода задания для последнего домена. Поскольку DNS запись обновляется не сразу. Об этом чуть ниже.

Для каждого введённого домена будет выведено имя хоста и текстовая запись, которые необходимо создать в панели управления вашим доменом.

На примере mail.pressanybutton.ru:
Хост: _acme-challenge.mail.pressanybutton.ru.
Значение: QkUdVKpxx1jbCin6Yf64l5IaKgJEYrgIG0iYYbNFTj4

Поскольку у меня три домена введено, то и три записи надо создать. На примере одной из них в панели управления TimeWeb:
Необходимо создать TXT-запись.
Выбираем раздел "Домены", там необходимый домен, затем вкладку "DNS".
Нажимаем на кнопку "Добавить запись" и выбираем "TXT".
В разделе Хост выбираем "Ввести вручную" и вводим _acme-challenge.mail.
В поле значение вводим: QkUdVKpxx1jbCin6Yf64l5IaKgJEYrgIG0iYYbNFTj4.

И так делаем, пока не дойдём до последнего домена. Когда создадите запись для последнего домена, не спешите нажимать Enter. DNS-записи могут обновляться достаточно долго, до 24-х часов. Отслеживать добавилась запись к домену или нет, можно по ссылке указанной в тексте под кодом значения. Перейдя по ней, можно увидеть, есть запись у домена или ещё нет. Если есть, убедитесь, что и для других указанных доменов она тоже присутствует, нажимайте Enter.
Также стоит отметить, что до появления записи в DNS, терминал следует держать открытым, иначе придётся всё проделывать заново.

Если, всё выполнено верно и не было никаких ошибок, будет выведено последнее сообщение и завершится работа программы.

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

Пути расположения файлов:
Сертификат: C:\Certbot\archive\pressanybutton.ru\fullchain.pem
Приватный ключ: C:\Certbot\archive\pressanybutton.ru\privkey.pem
В директории так же будет ещё два других сертификата, но их применение только в специализированных ситуациях. В качестве сертификата используйте только fullchain.pem.

Поздравляю! Теперь у вас есть действующий сертификат для сайта, сроком на 3 месяца.

Файлы к посту, можно получить в боте по коду: 395898
Пост на сайте.
Поддержать канал.
👍2
Код на салфетке
Получение сертификата. Запускаем PowerShell от имени администратора. Это важно! Иначе сертификат невозможно будет сгенерировать! И вводим следующую команду: certbot certonly --authenticator manual --preferred-challenges dns Параметры команды: - certonly…
Дополнение к посту.

После небольшого спора о способе указания поддоменов в сертификат и проверке этого способа, в текст поста было добавлено следующее дополнение:

Внимание! Есть ещё второй вариант добавления неограниченного количества поддоменов в сертификат. Для этого указываем два домена: domain.ru и *.domain.ru. Это упростит получение сертификата, если у вас несколько поддоменов.
Однако, стоит отметить, что данный способ не указан в документации, гарантировать его работу не могу.


Присоединяйтесь к чату, у нас весело и иногда интересно 😉
👍2
Сегодня ровно 14 месяцев с начала обучения по годовой программе в GeekBrains.

Я выкладывал пост на 12й месяц, а про 13й забыл. Исправляюсь)

Обучение: тринадцатый месяц⁠⁠
Обучение: четырнадцатый месяц⁠⁠

😊Буду рад лайкам подпискам и вот этому всему)
🔥8