== != is
Автор: Андрей Лебедев
Поговорим о разнице между оператором "==" и ключевым словом "is". Если до этого вы не читали мой пост о переменных в питоне и коробочках, сейчас самое время сделать это, потому что мы будем продолжать всё ту же аналогию.
Итак, вспомним, что у нас есть:
*️⃣ Переменная - она же бирка на коробочке
*️⃣ Коробочка - она же ячейка памяти
*️⃣ Заводской номер коробочки - он же адрес ячейки памяти
*️⃣ Содержимое коробки - значение
*️⃣ Склад - память в целом.
Вспомнили? Отлично. Есть шанс, что если с разработкой не срастётся, всегда можно будет устроиться работать на склад. Шутка, всё обязательно получится, для этого мы здесь.
Автор: Андрей Лебедев
Поговорим о разнице между оператором "==" и ключевым словом "is". Если до этого вы не читали мой пост о переменных в питоне и коробочках, сейчас самое время сделать это, потому что мы будем продолжать всё ту же аналогию.
Итак, вспомним, что у нас есть:
*️⃣ Переменная - она же бирка на коробочке
*️⃣ Коробочка - она же ячейка памяти
*️⃣ Заводской номер коробочки - он же адрес ячейки памяти
*️⃣ Содержимое коробки - значение
*️⃣ Склад - память в целом.
Вспомнили? Отлично. Есть шанс, что если с разработкой не срастётся, всегда можно будет устроиться работать на склад. Шутка, всё обязательно получится, для этого мы здесь.
❤2🔥1
Оператор "=="
Итак, для чего нужен оператор "=="? Он позволяет проверить, совпадает ли содержимое двух коробок на складе (значения, хранящиеся в двух ячейках памяти). Он похож на работника склада, который умеет выполнять только эту задачу и никакую другую. Негусто, конечно, но зато справляется он со своей функцией на ура.
Как поставить работнику задачу? Дать ему данные с двух бирок (переменные) и попросить его сравнить содержимое коробок, на которых они наклеены. Рассмотрим на примере.
Наш трудяга стремглав летит проверять, что у нас лежит в коробках с бирками name и same_name. А лежит у нас и там, и там одинаковая строка "Стэн". Оператор радостно нам сообщает об этом: True!
Закрепим - оператор "==" сравнивает переменные по значению (содержимое двух коробок с бирками, которые мы ему передали).
Ключевое слово "is"
Помимо работника, у нас в команде есть ключевое слово "is" (пускай на этот раз будет сотрудница). Умеет опять-таки немного, но работу свою выполняет чётко. Ей мы также даём информацию о том, что написано на двух бирках (названия переменных), а она проверяет, наклеены ли они на одну и ту же коробку (ссылаются ли переменные на одну и ту же ячейку памяти).
Рассмотрим на примере.
Из поста про переменные мы помним, что две одинаковые строки, которые мы кладем в две разные переменные, на самом деле будут лежать в одной и той же ячейке памяти, что и видно по адресу, который мы в обоих случаях вывели в консоль
То есть ещё раз: оператор "==" показывает, одинаковое ли значение (содержимое) хранится в двух ячейках памяти (коробках), на которые ссылаются две переменные (наклеены две бирки). Ключевое слово "is" показывает, ссылаются ли две переменные (наклеены ли две бирки) на одну и ту же ячейку памяти (коробку), или нет.
А зачем это вообще нужно?
Разве бывает так, чтобы ответы наших работников по поводу одинакового содержимого двух разных переменных различались?
Бывает. Рассмотрим на примере:
Несложно заметить, что во всех четырёх наших переменных в итоге окажется одна и та же строка (если мы честно ввели слово “Стэн” без кавычек, когда нас об этом попросили). Но в первых трех случаях эта строка оказалась в одной и той же ячейке, на которую “наклеились” три переменные:
Почему же в четвертом случае (для переменной
А вот то, что мы вводим с клавиатуры и передаем в переменную
Советую не верить мне на слово, а «пощупать» всё в PyCharm самостоятельно.
Ещё один пример
Итак, для чего нужен оператор "=="? Он позволяет проверить, совпадает ли содержимое двух коробок на складе (значения, хранящиеся в двух ячейках памяти). Он похож на работника склада, который умеет выполнять только эту задачу и никакую другую. Негусто, конечно, но зато справляется он со своей функцией на ура.
Как поставить работнику задачу? Дать ему данные с двух бирок (переменные) и попросить его сравнить содержимое коробок, на которых они наклеены. Рассмотрим на примере.
name = "Стэн"
same_name = "Стэн"
print(name == same_name)
Наш трудяга стремглав летит проверять, что у нас лежит в коробках с бирками name и same_name. А лежит у нас и там, и там одинаковая строка "Стэн". Оператор радостно нам сообщает об этом: True!
Закрепим - оператор "==" сравнивает переменные по значению (содержимое двух коробок с бирками, которые мы ему передали).
Ключевое слово "is"
Помимо работника, у нас в команде есть ключевое слово "is" (пускай на этот раз будет сотрудница). Умеет опять-таки немного, но работу свою выполняет чётко. Ей мы также даём информацию о том, что написано на двух бирках (названия переменных), а она проверяет, наклеены ли они на одну и ту же коробку (ссылаются ли переменные на одну и ту же ячейку памяти).
Рассмотрим на примере.
name = “Стэн"
print(id(name))
same_name = “Стэн"
print(id(same_name))
print(name == same_name)
print(name is same_name)
Из поста про переменные мы помним, что две одинаковые строки, которые мы кладем в две разные переменные, на самом деле будут лежать в одной и той же ячейке памяти, что и видно по адресу, который мы в обоих случаях вывели в консоль
print’ом. И нашей сотруднице это видно, о чём она нам и заявляет: True.То есть ещё раз: оператор "==" показывает, одинаковое ли значение (содержимое) хранится в двух ячейках памяти (коробках), на которые ссылаются две переменные (наклеены две бирки). Ключевое слово "is" показывает, ссылаются ли две переменные (наклеены ли две бирки) на одну и ту же ячейку памяти (коробку), или нет.
А зачем это вообще нужно?
Разве бывает так, чтобы ответы наших работников по поводу одинакового содержимого двух разных переменных различались?
Бывает. Рассмотрим на примере:
a = "Стэн"
b = "Стэн"
c = "Ст" + "эн"
d = input('Введите слово "Стэн": ') # Введем слово "Стэн" (без кавычек)
print(id(a)) # 4352025456
print(id(b)) # 4352025456
print(id(c)) # 4352025456
print(id(d)) # 4353014544
print(a == b) # True
print(a is b) # True
print(a == c) # True
print(a is c) # True
print(a == d) # True
print(a is d) # False
Несложно заметить, что во всех четырёх наших переменных в итоге окажется одна и та же строка (если мы честно ввели слово “Стэн” без кавычек, когда нас об этом попросили). Но в первых трех случаях эта строка оказалась в одной и той же ячейке, на которую “наклеились” три переменные:
a, b, c.Почему же в четвертом случае (для переменной
d) потребовалось новая ячейка в памяти? Всё дело в том, как Python пытается экономить память. На этапе компиляции кода он способен сравнивать строки между собой, а также проверять результат простых операций со строками (вроде конкатенации строк “Ст” и “эн”). Так как этот результат соответствует уже существующей в памяти строке “Стэн”, нам незачем создавать еще один такой же объект. Смело клеим бирку на уже созданную ранее коробку со Стэном. Процесс оптимизации работы со строками и некоторыми другими объектами называется “интернированием”, и мы когда-нибудь уделим ему отдельное внимание.А вот то, что мы вводим с клавиатуры и передаем в переменную
d, попадает в память уже в момент работы кода и поэтому для новой строки подбирается новая ячейка.Советую не верить мне на слово, а «пощупать» всё в PyCharm самостоятельно.
Ещё один пример
🔥4👍1
list_one = [1, 3, 5]Пройдёмся сверху вниз по коду примера:
list_two = [1, 3, 5]
list_three = list_one
list_four = list_one.copy()
print(id(list_one)) # 4509660416
print(id(list_two)) # 4511778176
print(id(list_three)) # 4509660416
print(id(list_four)) # 4510270528
print(list_one == list_two) # True
print(list_one is list_two) # False
print(list_one == list_three) # True
print(list_one is list_three) # True
print(list_one == list_four) # True
print(list_one is list_four) # False
1️⃣ Мы создали список из трех цифр и записали его в переменную
list_one.
2️⃣ Далее создали точно такой же список и записали его в переменную list_two.3️⃣ Затем на ячейку, к которой уже “прикреплена” переменная
list_one, "вешаем" еще одну переменную list_three.4️⃣ Наконец, в переменную
list_four записываем результат копирования списка из list_one.Первое, на что стоит обратить внимание,
list_one и list_two записались в разные ячейки памяти, что видно и по номерам этих ячеек, и по работе нашей прекрасной сотрудницы "is", которая выдает “False”, хотя её коллега "==" справедливо подтверждает, что содержатся в обеих переменных идентичные списки. Со строками из предыдущего примера в такой же ситуации результат, как мы помним, был другим. Да, такое поведение напрямую связано с особенностями этих типов данных, о чем мы подробно поговорим в следующий раз.Второе, на что стоит обратить внимание,
list_one и list_three “прицепились” к одной и той же ячейке. Это видно по работе функции id(), а также по показаниям наших сотрудников - "==" и "is".Третий важный момент: когда мы используем функцию
copy() для копирования списка, в переменную list_four попадает идентичный список с числами. Но записывается он уже в новую ячейку памяти, что легко видно по результатам работы id(), "==" и "is". Возможно, вы спросите, для чего вообще нужна эта манипуляция c copy(), если можно было бы, как и в случае с list_two, просто вручную пересоздать точно такой же список. Ну, хорошо, когда это можно сделать “на берегу” до запуска кода. А если список попадает к нам каким-то другим образом? Например, вводится через консоль или получается автоматически с какого-нибудь сайта? Если нам нужна точная его копия в другой ячейке памяти (а иногда это просто-таки необходимо), то copy() - один из возможных выходов из ситуации.Ну и последнее
list_one = [1, 3, 5]Работаем всё с теми же четырьмя переменными, в которых находится всё то же самое. К списку в
list_two = [1, 3, 5]
list_three = list_one
list_four = list_one.copy()
list_one.append(7)
print(list_one) # [1, 3, 5, 7]
print(list_two) # [1, 3, 5]
print(list_three) # [1, 3, 5, 7]
print(list_four) # [1, 3, 5]
list_one добавляем еще одно число и выводим в консоль все четыре переменные. Для тех, кто внимательно следил за ходом нашей мысли, не должно стать сюрпризом, что изменения, помимо списка в переменной list_one, коснулись и списка в переменной list_three. Почему? Потому что это один и тот же список, который “лежит” в одной и той же коробке, к которой мы прицепили две бирки: list_one и list_three. Списки из list_two и list_four остались нетронутыми, потому что каждый из них спокойно себе лежит в своей “коробке”.А вот если бы мы пытались проделать нечто подобное со строками или целыми числами, результат снова был бы отличным, но это уже совсем другая история…
Пост на сайте
Поддержать проект
#Python #код_в_мешке #переменные #управление_памятью #операторы #ключевые_слова #is #оператор_сравнения
🔥5
Django 36. Добавление постов пользователем
Автор: Иван Ашихмин
Продолжаем развивать наш блог. Мы с вами реализовали регистрацию и создали профиль пользователя, теперь реализуем добавление постов не только Администратором, но и Авторами.
Создать форму для добавления постов, создания новых категорий или загрузки файлов - задача нехитрая. Куда сложнее дать авторам возможность редактировать и при необходимости удалять уже созданные ими ранее посты.
В этом и нескольких последующих постах мы подробно рассмотрим процесс создания страницы с формами, а также приспособим личный кабинет под нужды авторов.
Библиотека pytils.
Автор: Иван Ашихмин
Продолжаем развивать наш блог. Мы с вами реализовали регистрацию и создали профиль пользователя, теперь реализуем добавление постов не только Администратором, но и Авторами.
Создать форму для добавления постов, создания новых категорий или загрузки файлов - задача нехитрая. Куда сложнее дать авторам возможность редактировать и при необходимости удалять уже созданные ими ранее посты.
В этом и нескольких последующих постах мы подробно рассмотрим процесс создания страницы с формами, а также приспособим личный кабинет под нужды авторов.
Библиотека pytils.
В модели поста есть поле
Когда мы добавляем пост через панель администратора, его заголовок преобразуется в текст на латинице автоматически.
В случае с добавлением поста на стороне сайта такой возможности нет, поэтому мы прибегнем к помощи библиотеки pytils, в которой есть функция slugify, переводящая текст из кириллицы в латиницу.
Установим библиотеку командой:
И добавим её в
Группа пользователей "Автор".
Для того, чтобы создавать посты могли только авторы, а не все пользователи, необходимо создать такую группу в панели администратора.
Откроем панель администратора и в блоке "Пользователи и группы" откроем раздел Группы.
Нажимаем кнопку "Добавить".
В поле "Имя" вводим название группы, в нашем случае - "Автор".
На данный момент никакие права не выдаём, нам нужна только группа пользователей.
Нажимаем "Сохранить".
Готово. Теперь у нас есть группа "Авторы".
Для добавления пользователя в эту группу необходимо открыть раздел "Пользователи", выбрать нужного пользователя и в блоке с выбором группы добавить группу "Автор" в выбранные.
Форма добавления поста.
Нам нужно создать форму модели для добавления нового поста.
Откроем файл
Ранее мы прописывали каждое поле с его типом и виджетом. В этот раз мы поступим иначе: получим поля из модели и в конструкторе класса
После цикла, дополнительно пропишем полю "категория" ещё один класс для выпадающих списков.
Для тех, кто использует django-ckeditor5:
С использованием данного редактора вне панели администратора есть забавная особенность.
Дело в том, что поле с редактором в модели обязательно к заполнению, но если оставить поле формы обязательным, то форма не будет работать. JavaScript редактора самостоятельно обрабатывает ввод, а исходное поле формы скрывается. В результате получается так, что данные в редакторе есть, а в поле формы при этом ничего не введено. В связи с этим браузер не отправляет форму на сервер, считая, что форма не заполнена.
Для решения этой проблемы достаточно в форме указать, что она необязательна к заполнению, при этом в модели убирать обязательность не нужно. Если будет отправлена форма с пустым полем, то страница просто обновится и укажет на ошибку.
В
И прописываем стандартный внутренний класс
Код формы:
slug. Подробнее о нём я писал в постах "Django 16. Модель категорий" и "Django 20. Модель поста".Когда мы добавляем пост через панель администратора, его заголовок преобразуется в текст на латинице автоматически.
В случае с добавлением поста на стороне сайта такой возможности нет, поэтому мы прибегнем к помощи библиотеки pytils, в которой есть функция slugify, переводящая текст из кириллицы в латиницу.
Установим библиотеку командой:
pip install pytils
И добавим её в
requirements.txt:pytils~=0.4.1
Группа пользователей "Автор".
Для того, чтобы создавать посты могли только авторы, а не все пользователи, необходимо создать такую группу в панели администратора.
Откроем панель администратора и в блоке "Пользователи и группы" откроем раздел Группы.
Нажимаем кнопку "Добавить".
В поле "Имя" вводим название группы, в нашем случае - "Автор".
На данный момент никакие права не выдаём, нам нужна только группа пользователей.
Нажимаем "Сохранить".
Готово. Теперь у нас есть группа "Авторы".
Для добавления пользователя в эту группу необходимо открыть раздел "Пользователи", выбрать нужного пользователя и в блоке с выбором группы добавить группу "Автор" в выбранные.
Форма добавления поста.
Нам нужно создать форму модели для добавления нового поста.
Откроем файл
forms.py в приложении user_app. Создадим новый класс AddPostByAuthorForm, унаследованный от forms.ModelForm.Ранее мы прописывали каждое поле с его типом и виджетом. В этот раз мы поступим иначе: получим поля из модели и в конструкторе класса
__init__ проитерируемся по списку полей, добавляя к атрибутам поля нужный bootstrap-класс.После цикла, дополнительно пропишем полю "категория" ещё один класс для выпадающих списков.
Для тех, кто использует django-ckeditor5:
С использованием данного редактора вне панели администратора есть забавная особенность.
Дело в том, что поле с редактором в модели обязательно к заполнению, но если оставить поле формы обязательным, то форма не будет работать. JavaScript редактора самостоятельно обрабатывает ввод, а исходное поле формы скрывается. В результате получается так, что данные в редакторе есть, а в поле формы при этом ничего не введено. В связи с этим браузер не отправляет форму на сервер, считая, что форма не заполнена.
Для решения этой проблемы достаточно в форме указать, что она необязательна к заполнению, при этом в модели убирать обязательность не нужно. Если будет отправлена форма с пустым полем, то страница просто обновится и укажет на ошибку.
В
__init__ указываем класс редактора и то, что поле не обязательно.И прописываем стандартный внутренний класс
Meta с указанием на модель и перечислением полей.Код формы:
from blog.models import PostModel
class AddPostByAuthorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control', 'autofocus': ''})
self.fields['short_body'].widget.attrs.update({'class': 'django_ckeditor_5'})
self.fields['full_body'].widget.attrs.update({'class': 'django_ckeditor_5'})
self.fields['category'].widget.attrs.update({'class': 'form-select'})
self.fields["short_body"].required = False
self.fields["full_body"].required = False
class Meta:
model = PostModel
fields = ('title', 'image', 'category', 'short_body', 'full_body', 'status', 'telegram_link',
'file', 'tags', 'telegram_body')
Представление страницы добавления поста.
Откроем файл
views.py.Создадим класс
AddPostByAuthorView, унаследованный от двух классов: UserPassesTestMixin и CreateView.UserPassesTestMixin - это класс в Django, предоставляющий удобный способ ограничения доступа к представлениям на основе пользовательских тестов. Он позволяет определить метод test_func, который выполняет проверку и возвращает True, если пользователь должен иметь доступ, или False, если доступ следует отклонить. Если пользователь не проходит тест, то он перенаправляется на страницу, указанную в login_url.В классе пропишем три знакомых нам поля:
template_name, form_class и model.Также три метода:
test_func - в этом методе мы проверяем, что пользователь состоит в группе "Автор". Если пользователь в ней не состоит, то страница добавления поста не откроется.get_success_url - указываем на какую страницу перенаправить пользователя после добавления поста.form_valid - метод, проверяющий валидность формы. В нашем случае мы в ней заполняем два отсутствующих в форме поля: author и slug.Код представления:
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import CreateView
from pytils.translit import slugify
from blog.models import PostModel
from user_app import forms
class AddPostByAuthorView(UserPassesTestMixin, CreateView):
template_name = 'user_app/add_post.html'
form_class = forms.AddPostByAuthorForm
model = PostModel
def test_func(self):
return self.request.user.groups.filter(name='Автор').exists()
def get_success_url(self):
return reverse('user_app:user_profile', kwargs={'username': self.request.user.username})
def form_valid(self, form):
form.instance.slug = slugify(form.instance.title)
form.instance.author = self.request.user
return super().form_valid(form)
URL-паттерн страницы добавления поста.
Откроем файл
urls.py и в список urlpatterns добавим следующую строку:path('post/add_post/', views.AddPostByAuthorView.as_view(), name='add_post'),Кнопка перехода на страницу добавления поста.
Я решил расположить кнопку в меню пользователя в шапке сайта.
Откроем файл
header.html и в блоке авторизованного пользователя добавим проверку на наличие в группе "Автор" и саму кнопку, если он там есть:{% if "Автор" in user.groups.all.0.name %}
<li>
<a class="dropdown-item dd-item-color" href="{% url 'user_app:add_post' %}">Добавить
пост</a>
</li>
{% endif %}Шаблон страницы добавления поста.
В директории с шаблонами приложения
В целом это обычная форма, за исключением одного момента.
Если вы используете
Код шаблона:
В следующем посте сделаем формы добавления категории и файлов.
Файлы к посту, можно получить в боте по коду: 517311
Пост на сайте
Поддержать проект
#Python #Django #модели #формы #представления #гайды #slugify #pytils #группы_пользователей #ckeditor #django_ckeditor #slug
В директории с шаблонами приложения
user_app создадим файл add_post.html.В целом это обычная форма, за исключением одного момента.
Если вы используете
django-ckeditor5, то помимо стандартных csrf_token и form.as_p, необходимо добавить form.media. Без этого редактор не будет отображаться.Код шаблона:
{% extends 'blog/base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="container mt-3 d-flex justify-content-center">
<div class="col-lg-8 col-sm-12">
<h2>Добавление поста</h2>
<form method="post" enctype="multipart/form-data" action="{% url 'user_app:add_post' %}">
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<button type="submit" class="btn btn-success mt-3">Сохранить</button>
</form>
</div>
</div>
{% endblock %}
Готово. Теперь авторы могут добавлять посты.В следующем посте сделаем формы добавления категории и файлов.
Файлы к посту, можно получить в боте по коду: 517311
Пост на сайте
Поддержать проект
#Python #Django #модели #формы #представления #гайды #slugify #pytils #группы_пользователей #ckeditor #django_ckeditor #slug
Маленькая предыстория...
Автор: Некий Вестник Сплетен
Большинство из нас - студенты различных образовательных онлайн-платформ, и все мы прекрасно знаем, как выглядят чаты групп, куда скидывают полностью весь поток обучающихся. Часть студентов начинает активно знакомиться и вступать в диалоги, а часть (и таких, как правило, подавляющее большинство) отсиживаются в сторонке, лишь изредка читая "простыню" текста и спама. Постепенно народ "растекается" по личкам, и лишь немногие объединяются в подгруппы "по интересам".
Автор: Некий Вестник Сплетен
Большинство из нас - студенты различных образовательных онлайн-платформ, и все мы прекрасно знаем, как выглядят чаты групп, куда скидывают полностью весь поток обучающихся. Часть студентов начинает активно знакомиться и вступать в диалоги, а часть (и таких, как правило, подавляющее большинство) отсиживаются в сторонке, лишь изредка читая "простыню" текста и спама. Постепенно народ "растекается" по личкам, и лишь немногие объединяются в подгруппы "по интересам".
🔥3
Так произошло и с нашим учебным потоком. Сначала спонтанным образом появился чат "Исповедальня программистов", затем зародился канал "Код на салфетке" и связанный с ним чат "Кот на салфетке". Он объединил людей разного уровня подготовки и специализаций. Это удивительно и прекрасно, когда люди вот так вот самостоятельно объединяются. Порой можно обсудить насущное или просто пообщаться как "бабушки на лавочке"... В процессе одного такого разговора поступило предложение создать групповой проект. Изначально не было даже идеи, что бы это могло быть, поэтому проект с ходу окрестили “неким”.
Желающие (их и в этом чате предсказуемо оказалось немного) стали активно присоединяться, предлагать варианты, проявлять интерес. Обсудили, поговорили и.... затихли... до поры до времени!
В какой-то момент было решено создать отдельную группу, пригласить заинтересовавшихся и начать обсуждение проекта. Выявилось 15 человек, желающих активно участвовать. В состав вошли люди различного уровня знаний и опыта. Но уже сам факт появления этой маленькой группы воспринимается как маленькое чудо!
С этого момента начался проект! Первые обсуждения, решения, созвоны... И абсолютно никакого понимания, что делать дальше. Но я искренне верю, что совместными усилиями мы реализуем-таки “Некий проект”!
В серии постов в рамках рубрики "Некий проект" я буду время от времени писать о новостях нашего совместного предприятия, первых успехах и сложностях, с которыми мы столкнемся. Не обещаю, что в результате получится рецепт создания собственного "Гугла" с нуля, но скучно точно не будет!
Пост на сайте
Поддержать проект
#Кот_на_салфетке #Код_на_салфетке #Групповой_проект #Некий_проект
Желающие (их и в этом чате предсказуемо оказалось немного) стали активно присоединяться, предлагать варианты, проявлять интерес. Обсудили, поговорили и.... затихли... до поры до времени!
В какой-то момент было решено создать отдельную группу, пригласить заинтересовавшихся и начать обсуждение проекта. Выявилось 15 человек, желающих активно участвовать. В состав вошли люди различного уровня знаний и опыта. Но уже сам факт появления этой маленькой группы воспринимается как маленькое чудо!
С этого момента начался проект! Первые обсуждения, решения, созвоны... И абсолютно никакого понимания, что делать дальше. Но я искренне верю, что совместными усилиями мы реализуем-таки “Некий проект”!
В серии постов в рамках рубрики "Некий проект" я буду время от времени писать о новостях нашего совместного предприятия, первых успехах и сложностях, с которыми мы столкнемся. Не обещаю, что в результате получится рецепт создания собственного "Гугла" с нуля, но скучно точно не будет!
Пост на сайте
Поддержать проект
#Кот_на_салфетке #Код_на_салфетке #Групповой_проект #Некий_проект
🔥5😱1💩1
Docker 6. Собственный GIT-сервис - Гид по быстрому запуску Gitea на вашем сервере!
Автор: Иван Ашихмин
В процессе командной работы над "Неким проектом" (о начале этой работы можно прочитать в посте "Маленькая предыстория...") мы столкнулись со знакомой многим проблемой - последствиями санкций. Выяснилось, что пользователям из некоторых регионов РФ недоступен GitLab, а GitHub не позволяет работать в приватных репозиториях.
Мы перебрали несколько альтернативных вариантов (о чем вы узнаете в постах Некоего Вестника Сплетен) и в результате решили запустить свой собственный GIT-сервер.
Выбор пал на готовое решение
Автор: Иван Ашихмин
В процессе командной работы над "Неким проектом" (о начале этой работы можно прочитать в посте "Маленькая предыстория...") мы столкнулись со знакомой многим проблемой - последствиями санкций. Выяснилось, что пользователям из некоторых регионов РФ недоступен GitLab, а GitHub не позволяет работать в приватных репозиториях.
Мы перебрали несколько альтернативных вариантов (о чем вы узнаете в постах Некоего Вестника Сплетен) и в результате решили запустить свой собственный GIT-сервер.
Выбор пал на готовое решение
Gitea, отличающееся от аналогов легковесностью, а также доступностью привычного функционала GitHub.Установка будет производиться на VPS-сервере с Debian 12. Как установить Docker я рассказывал в посте "Docker 2.2 Установка Docker на Linux".
Официальная документация Gitea доступна по ссылке: https://docs.gitea.com/
Подготовка Docker-compose файла.
Подключитесь по SSH к вашему серверу и перейдите в рабочую директорию.
Затем выполните следующие команды для создания директорий и
Выполним команду
Обратите внимание на отступы и форматирование!
Разберём, что и зачем:
У нас будет два сервиса:
Первый сервис
Второй сервис
Прописываемые параметры:
-
-
-
-
-
-
Обратите внимание на порты в сервисе
Порт
Порт
Запуск и первая настройка.
Для запуска используем команду
После того, как запустятся контейнеры, открываем браузер и переходим по адресу:
Перед нами откроется окно первоначальной конфигурации, разбитое на три блока.
Настройки базы данных.
В этом блоке указываем данные для подключения к БД, но если вы прописали их в
Иначе заполняем данными, выбирая удобную для вашего решения СУБД.
Основные настройки.
В этом блоке указываем:
- Название сайта.
- Настройки путей внутри контейнера.
- Домен или ip-адрес сервера.
Официальная документация Gitea доступна по ссылке: https://docs.gitea.com/
Подготовка Docker-compose файла.
Подключитесь по SSH к вашему серверу и перейдите в рабочую директорию.
Затем выполните следующие команды для создания директорий и
yaml-файла:mkdir -p gitea/{data,config}
cd gitea
touch docker-compose.ymlВыполним команду
nano docker-compose.yml для открытия файла и напишем следующую конфигурацию:Обратите внимание на отступы и форматирование!
version: '3'
services:
db:
image: postgres
restart: always
environment:
- POSTGRES_USER=db_username
- POSTGRES_PASSWORD=db_password
- POSTGRES_DB=db_name
- PGDATA=/var/lib/postgresql/data/pgdata
ports:
- "5432:5432"
volumes:
- ./postgres-db:/var/lib/postgresql/data
git:
image: gitea/gitea:1.20.5-rootless
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=db_name
- GITEA__database__USER=db_username
- GITEA__database__PASSWD=db_password
restart: always
volumes:
- ./data:/var/lib/gitea
- ./config:/etc/gitea
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:2222"
depends_on:
- db
Разберём, что и зачем:
У нас будет два сервиса:
db и git.Первый сервис
db - это контейнер с СУБД PostgreSQL.Второй сервис
git - это контейнер с Gitea.Прописываемые параметры:
-
image: - указывает на то, какой образ из Docker Hub будет использоваться.-
restart: - указывает на правила перезагрузки сервера в случае сбоя.-
environment: - указывает на параметры переменной среды. В нашем случае это: имя пользователя, пароль, имя базы данных, указание на расположение файлов.-
ports: - указывает какие порты открыты в контейнере, <внешний_порт>:<внутренний_порт>.-
volumes: - указывает на подключённые внутри контейнера директории: <директория_вне_контейнера>:<директория_внутри_контейнера>.-
depends_on - определяет порядок запуска контейнеров, т.е. контейнер с Gitea запустится после запуска контейнера с БД.Обратите внимание на порты в сервисе
git.Порт
3000 - это порт веб-интерфейса, его можно изменить на удобный вам.Порт
2222 - это порт SSH-подключения, его также можно изменить более на удобный.Запуск и первая настройка.
Для запуска используем команду
docker compose up -d.После того, как запустятся контейнеры, открываем браузер и переходим по адресу:
https://<ip_сервера>:3000/ или https://<ваш_домен>:3000/, если у вас к серверу прикреплено доменное имя.Перед нами откроется окно первоначальной конфигурации, разбитое на три блока.
Настройки базы данных.
В этом блоке указываем данные для подключения к БД, но если вы прописали их в
docker-compose-файле, пропускаем этот блок.Иначе заполняем данными, выбирая удобную для вашего решения СУБД.
Основные настройки.
В этом блоке указываем:
- Название сайта.
- Настройки путей внутри контейнера.
- Домен или ip-адрес сервера.
- Порты.
- URL-адрес для доступа к веб-интерфейсу.
- Если необходимо, включаем оповещение об обновлениях
Расширенные настройки.
Этот блок состоит из трёх подблоков:
- Настройки электронной почты - в этом блоке указываем настройки почтового сервера. Как запускать свой почтовый сервер я писал в постах "Docker 5.1 Почтовый сервер на Docker Mailserver" и "Docker 5.2 Почтовый сервер на Docker Mailserver"
- Сервер и настройки внешних служб - в этом блоке настраиваются различные службы, такие как регистрация пользователей, OpenID и др.
- Настройки учётной записи администратора - в этом блоке указываются данные администратора.
Environment Configuration.
В этом блоке есть две кнопки установить. Честно, не совсем очевидно зачем их две. Я использовал первую кнопку, после нажатия на которую будет выполнена установка.
Создание репозитория.
После установки откроется главная страница. Кликаем по кнопке "Вход" в правой части экрана.
Вводим учётные данные администратора и авторизуемся.
Попадаем на страницу, где будут отображаться ваши активности, как на GitHub.
Для создания нового репозитория, в правом верхнем углу нажимаем на
Откроется страница, знакомая всем пользователям GitHub.
Заполняем необходимые поля и нажимаем "Создать репозиторий".
После чего, если вы решили не создавать
Дополнительно: Поддержка HTTPS.
По умолчанию веб-интерфейс и всё взаимодействие происходит по
Для включения поддержки
Переходим в директорию
В блоке
Где
Также в параметре
Сохраняем.
После чего возвращаемся на уровень выше и переходим в директорию
Перезагружаем контейнер с
Готово. Теперь веб-интерфейс и всё взаимодействие будет использовать защищённый HTTPS-протокол.
Заключение.
Вот так достаточно просто можно развернуть свой аналог GitHub на собственном сервере. Если команда небольшая, то и ресурсов он будет потреблять не много, что отлично подойдёт начинающим разработчикам.
Пост на сайте
Поддержать проект
#Docker #Docker_Compose #гайды #Gitea #контроль_версий #Git
- URL-адрес для доступа к веб-интерфейсу.
- Если необходимо, включаем оповещение об обновлениях
Gitea.Расширенные настройки.
Этот блок состоит из трёх подблоков:
- Настройки электронной почты - в этом блоке указываем настройки почтового сервера. Как запускать свой почтовый сервер я писал в постах "Docker 5.1 Почтовый сервер на Docker Mailserver" и "Docker 5.2 Почтовый сервер на Docker Mailserver"
- Сервер и настройки внешних служб - в этом блоке настраиваются различные службы, такие как регистрация пользователей, OpenID и др.
- Настройки учётной записи администратора - в этом блоке указываются данные администратора.
Environment Configuration.
В этом блоке есть две кнопки установить. Честно, не совсем очевидно зачем их две. Я использовал первую кнопку, после нажатия на которую будет выполнена установка.
Создание репозитория.
После установки откроется главная страница. Кликаем по кнопке "Вход" в правой части экрана.
Вводим учётные данные администратора и авторизуемся.
Попадаем на страницу, где будут отображаться ваши активности, как на GitHub.
Для создания нового репозитория, в правом верхнем углу нажимаем на
+ и выбираем "Новый репозиторий".Откроется страница, знакомая всем пользователям GitHub.
Заполняем необходимые поля и нажимаем "Создать репозиторий".
После чего, если вы решили не создавать
README-файл, откроется страница с инструкцией по подключению локального репозитория, а если создали, откроется страница репозитория. Всё как на GitHub.Дополнительно: Поддержка HTTPS.
По умолчанию веб-интерфейс и всё взаимодействие происходит по
HTTP-протоколу.Для включения поддержки
HTTPS-протокола необходимо выполнить дополнительные действия.Переходим в директорию
config и открываем файл app.ini командой nano app.ini.В блоке
server добавляем следующие строки:PROTOCOL = https
CERT_FILE = cert.pem
KEY_FILE = key.pem
Где
CERT_FILE и KEY_FILE, указываем названия ваших файлов SSL-сертификата.Также в параметре
ROOT_URL заменяем http на https.Сохраняем.
После чего возвращаемся на уровень выше и переходим в директорию
data, а затем в custom. В эту директорию помещаем ваши файлы SSL-сертификата.Перезагружаем контейнер с
Gitea, выполнив команду docker restart <имя_контейнера>.Готово. Теперь веб-интерфейс и всё взаимодействие будет использовать защищённый HTTPS-протокол.
Заключение.
Вот так достаточно просто можно развернуть свой аналог GitHub на собственном сервере. Если команда небольшая, то и ресурсов он будет потреблять не много, что отлично подойдёт начинающим разработчикам.
Пост на сайте
Поддержать проект
#Docker #Docker_Compose #гайды #Gitea #контроль_версий #Git
🔥6
Питон на измене
Автор: Андрей Лебедев
Поговорим об изменяемости и неизменяемости типов данных в Python с точки зрения управления памятью. Чтобы лучше понять эту тему, прочтите посты о переменных и различиях между оператором "==" и ключевым словом "is".
До сих пор мы фокусировали внимание на коробках (ячейках памяти), бирках (переменных) и сравнении самих коробок между собой. Настала пора подробнее рассмотреть содержимое коробок (значения).
Основные изменяемые и неизменяемые объекты
Как мы знаем, в переменную мы можем записывать объекты разных типов. Также мы уже скорее всего понимаем (или хотя бы слышали), что все они делятся на две категории:
1️⃣ Неизменяемые. Например: int (целые числа), float (числа с плавающей точкой), string (строки), tuple (кортежи), bool (булево значение или логический тип), frozenset(неизменяемое множество).
Автор: Андрей Лебедев
Поговорим об изменяемости и неизменяемости типов данных в Python с точки зрения управления памятью. Чтобы лучше понять эту тему, прочтите посты о переменных и различиях между оператором "==" и ключевым словом "is".
До сих пор мы фокусировали внимание на коробках (ячейках памяти), бирках (переменных) и сравнении самих коробок между собой. Настала пора подробнее рассмотреть содержимое коробок (значения).
Основные изменяемые и неизменяемые объекты
Как мы знаем, в переменную мы можем записывать объекты разных типов. Также мы уже скорее всего понимаем (или хотя бы слышали), что все они делятся на две категории:
1️⃣ Неизменяемые. Например: int (целые числа), float (числа с плавающей точкой), string (строки), tuple (кортежи), bool (булево значение или логический тип), frozenset(неизменяемое множество).
🔥2
2️⃣ Изменяемые. Например: list (список), set (множество), dict (словарь).
Это далеко не исчерпывающий список хотя бы потому, что в нём фигурируют только встроенные типы данных (и то не все), тогда как существует (и может существовать) масса других изменяемых и неизменяемых объектов, включая экземпляры классов, которые вы сами можете создавать. Но для общего понимания нам достаточно будет сосредоточиться на этих типах.
Что же означает это свойство - изменяемость?
Продолжим аналогию с коробками на складе. Иногда (и даже очень часто) то, что мы положили в коробку, нам нужно изменить, а потом снова положить в ту же коробку, вернув на склад. Мы уже знаем, что когда кардинальным образом меняем содержимое (записываем в переменную объект другого типа, например), коробка всегда будет новая, хотя бирка (название переменной) и останется прежней. Нельзя в переменную вместо записанной туда строки поместить список и ожидать, что ячейка памяти останется прежней:
Как мы видим, название переменной осталось тем же, но вместе с типом данных содержимого изменилась и ячейка памяти, которая его хранит.
А что происходит, когда тип содержимого остается прежним, но мы лишь слегка изменяем сам объект? Оказывается, что таким вот образом «влезть» можно только в те коробки, где хранятся изменяемые типы данных (например, списки). Когда же мы помещаем в коробку неизменяемый тип данных (например, строку), коробка как будто «запечатывается» и вскрыть её, чтобы внести изменения в содержимое, уже не получится.
Рассмотрим не примере:
Мы видим, что добавление ещё одного элемента в список не меняет его местонахождения. Это по-прежнему тот же список в той же самой ячейке памяти.
А вот когда мы совершаем те или иные манипуляции со строкой, это не изменяет её (то есть сам первоначальный объект), и это важный для понимания момент. Когда мы проводим конкатенацию (слияние) двух строк - “Стэн, Эрик, Кайл” и “, Кенни”, мы не меняем ни одну из них, а создаем новую строку в новой ячейке памяти, что нам и показывает функция
А что же происходит с первоначальной строкой?
В нашем примере она удаляется сборщиком мусора, чтобы освободить память. И в данном случае «сборщик мусора» (garbage collector) - это даже не метафора. Так действительно называется алгоритм очистки памяти Python, с которым мы обязательно познакомимся поближе как-нибудь в другой раз.
А вот если бы на первоначальную строку ссылалась ещё одна переменная, то в результате выполнения конкатенации мы увидели бы, что наша строка “Стэн, Эрик, Кайл” тихо-мирно лежит себе всё в той же самой коробочке, в которой и была:
Это далеко не исчерпывающий список хотя бы потому, что в нём фигурируют только встроенные типы данных (и то не все), тогда как существует (и может существовать) масса других изменяемых и неизменяемых объектов, включая экземпляры классов, которые вы сами можете создавать. Но для общего понимания нам достаточно будет сосредоточиться на этих типах.
Что же означает это свойство - изменяемость?
Продолжим аналогию с коробками на складе. Иногда (и даже очень часто) то, что мы положили в коробку, нам нужно изменить, а потом снова положить в ту же коробку, вернув на склад. Мы уже знаем, что когда кардинальным образом меняем содержимое (записываем в переменную объект другого типа, например), коробка всегда будет новая, хотя бирка (название переменной) и останется прежней. Нельзя в переменную вместо записанной туда строки поместить список и ожидать, что ячейка памяти останется прежней:
students = "ученики четвертого класса"
print(type(students), id(students)) # <class 'str'> 4476735024
students = ["Стэн", "Эрик", "Кайл", "Кенни"]
print(type(students), id(students)) # <class 'list'> 4477400320
Как мы видим, название переменной осталось тем же, но вместе с типом данных содержимого изменилась и ячейка памяти, которая его хранит.
А что происходит, когда тип содержимого остается прежним, но мы лишь слегка изменяем сам объект? Оказывается, что таким вот образом «влезть» можно только в те коробки, где хранятся изменяемые типы данных (например, списки). Когда же мы помещаем в коробку неизменяемый тип данных (например, строку), коробка как будто «запечатывается» и вскрыть её, чтобы внести изменения в содержимое, уже не получится.
Рассмотрим не примере:
students_list = ["Стэн", "Эрик", "Кайл"]
print(id(students_list), students_list) # 4516377856 ['Стэн', 'Эрик', 'Кайл']
students_list.append("Кенни")
print(id(students_list), students_list) # 4516377856 ['Стэн', 'Эрик', 'Кайл', 'Кенни']
students_str = "Стэн, Эрик, Кайл"
print(id(students_str), students_str) # 4516970768 Стэн, Эрик, Кайл
students_str += ", Кенни"
print(id(students_str), students_str) # 4515712560 Стэн, Эрик, Кайл, Кенни
Мы видим, что добавление ещё одного элемента в список не меняет его местонахождения. Это по-прежнему тот же список в той же самой ячейке памяти.
А вот когда мы совершаем те или иные манипуляции со строкой, это не изменяет её (то есть сам первоначальный объект), и это важный для понимания момент. Когда мы проводим конкатенацию (слияние) двух строк - “Стэн, Эрик, Кайл” и “, Кенни”, мы не меняем ни одну из них, а создаем новую строку в новой ячейке памяти, что нам и показывает функция
id().А что же происходит с первоначальной строкой?
В нашем примере она удаляется сборщиком мусора, чтобы освободить память. И в данном случае «сборщик мусора» (garbage collector) - это даже не метафора. Так действительно называется алгоритм очистки памяти Python, с которым мы обязательно познакомимся поближе как-нибудь в другой раз.
А вот если бы на первоначальную строку ссылалась ещё одна переменная, то в результате выполнения конкатенации мы увидели бы, что наша строка “Стэн, Эрик, Кайл” тихо-мирно лежит себе всё в той же самой коробочке, в которой и была:
students_str = "Стэн, Эрик, Кайл"
students_str_copy = students_str
print(id(students_str), students_str) # 4372726032 Стэн, Эрик, Кайл
print(id(students_str_copy), students_str_copy) # 4372726032 Стэн, Эрик, Кайл
students_str += ", Кенни"
print(id(students_str), students_str) # 4371484208 Стэн, Эрик, Кайл, Кенни
print(id(students_str_copy), students_str_copy) # 4372726032 Стэн, Эрик, Кайл
🔥1
Вспомним пример из поста про оператор “==“ и ключевое слово “is”. Тогда мы нечто подобное проделывали со списком, на который ссылались две переменные. Когда мы изменяли список, используя первую переменную, то это изменение было видно и во второй переменной. Потому что обе они по-прежнему ссылались на всё тот же объект, который хранится всё в той же ячейке, что и первоначально. Почему? Потому что списки - изменяемый тип данных, и «коробка», в которой он лежит, не «запечатана».
А зачем это нужно? Почему все объекты не могут быть изменяемыми?
Иногда способность неизменяемых объектов «оставаться собой» очень выручает.
Например, когда мы назначаем какой-то объект, записанный в переменную, ключом в словаре. Как мы знаем, каждый ключ в словаре должен быть уникальным, чтобы избежать путаницы: если бы у нас было два одинаковых ключа в словаре, то было бы непонятно, к какому из двух значений мы обращаемся. Это как если бы у двух людей, живущих в одном городе, был бы одинаковый номер телефона: никогда заранее не знаешь, кому дозвонишься на этот раз.
Рассмотрим пример.
В нашем примере у мальчика в детском саду есть любимый мультфильм - «Гадкий я». Он выбрал себе шкафчик с постером этого мультика. В коде это отображается так: переменная
Спустя какое-то время предпочтения мальчика меняются, и он становится фанатом второй части мультфильма. В коде это отображается конкатенацией строк «Гадкий я» и « 2», результат которой перезаписывается в переменную
А почему бы тогда не сделать все объекты неизменяемыми?
Изменяемость некоторых типов данных позволяет экономить значительные объёмы памяти за счёт возможности редактировать объект прямо в текущей ячейке, а не создавать новый. Представим, что у нас есть журнал с оценками школьников. Если бы мы записывали их в неизменяемый список (то есть по сути в кортеж), нам бы пришлось для записи каждой новой оценки создавать ещё один кортеж. Это как если бы каждый раз, выставляя оценку, учитель заполнял бы новый журнал. Со временем пришлось бы заводить отдельный архив для хранения накопившихся “одноразовых” журналов с оценками учеников одного-единственного класса.
Это далеко не всё, что можно было бы сказать об изменяемости объектов в Python, но всему своё время…
Пост на сайте
Поддержать проект
#Python #код_в_мешке #управление_памятью #типы_данных #строки #изменяемость #списки #кортежи #словари #неизменяемость
А зачем это нужно? Почему все объекты не могут быть изменяемыми?
Иногда способность неизменяемых объектов «оставаться собой» очень выручает.
Например, когда мы назначаем какой-то объект, записанный в переменную, ключом в словаре. Как мы знаем, каждый ключ в словаре должен быть уникальным, чтобы избежать путаницы: если бы у нас было два одинаковых ключа в словаре, то было бы непонятно, к какому из двух значений мы обращаемся. Это как если бы у двух людей, живущих в одном городе, был бы одинаковый номер телефона: никогда заранее не знаешь, кому дозвонишься на этот раз.
Рассмотрим пример.
favorite_cartoon = "Гадкий я"
locker_image = favorite_cartoon
lockers = {
favorite_cartoon: ["Куртка", "Шапка", "Водяной пистолет"],
"Шрек": ["Шарф", "Жирафик"],
"Гадкий я 2": ["Свитер", "Резиновый ёжик"]
}
favorite_cartoon += " 2"
В нашем примере у мальчика в детском саду есть любимый мультфильм - «Гадкий я». Он выбрал себе шкафчик с постером этого мультика. В коде это отображается так: переменная
favorite_cartoon ссылается на строку “Гадкий я”. Далее к ней же прикрепляется другая переменная - locker_image, объект которой становится ключом в словаре шкафчиков детского сада.Спустя какое-то время предпочтения мальчика меняются, и он становится фанатом второй части мультфильма. В коде это отображается конкатенацией строк «Гадкий я» и « 2», результат которой перезаписывается в переменную
favorite_cartoon. Если бы строка была изменяемым типом, это привело бы к изменению объекта в той же самой ячейке памяти. А так как на неё же у нас ссылается и переменная locker_image, это привело бы к тому, что в словаре появилось бы две пары ключ-значение с одинаковым ключом, потому что в нашем детском саду уже был шкафчик с постером «Гадкий я 2».А почему бы тогда не сделать все объекты неизменяемыми?
Изменяемость некоторых типов данных позволяет экономить значительные объёмы памяти за счёт возможности редактировать объект прямо в текущей ячейке, а не создавать новый. Представим, что у нас есть журнал с оценками школьников. Если бы мы записывали их в неизменяемый список (то есть по сути в кортеж), нам бы пришлось для записи каждой новой оценки создавать ещё один кортеж. Это как если бы каждый раз, выставляя оценку, учитель заполнял бы новый журнал. Со временем пришлось бы заводить отдельный архив для хранения накопившихся “одноразовых” журналов с оценками учеников одного-единственного класса.
Это далеко не всё, что можно было бы сказать об изменяемости объектов в Python, но всему своё время…
Пост на сайте
Поддержать проект
#Python #код_в_мешке #управление_памятью #типы_данных #строки #изменяемость #списки #кортежи #словари #неизменяемость
🔥3
Приветствую.
Прошел очередной месяц... А значит пора писать новый пост на Pikabu.
Обучение: шестнадцатый месяц
Как обычно, жду лайков, репостов и вот это всё)
Прошел очередной месяц... А значит пора писать новый пост на Pikabu.
Обучение: шестнадцатый месяц
Как обычно, жду лайков, репостов и вот это всё)
🔥6👍2
Django 37. Две формы - добавление категории и файла
Автор: Иван Ашихмин
В прошлом посте мы написали форму добавления поста автором.
В этом посте добавим две формы: добавление новой категории и добавления файла для поста.
В процессе работы мы закрепим создание форм, создадим три представления, применив наследование, а также создадим два шаблона.
Автор: Иван Ашихмин
В прошлом посте мы написали форму добавления поста автором.
В этом посте добавим две формы: добавление новой категории и добавления файла для поста.
В процессе работы мы закрепим создание форм, создадим три представления, применив наследование, а также создадим два шаблона.
🔥1
Однако есть и второй вариант, а именно создание нового связанного объекта (в нашем случае категория и файл поста) прямо на странице добавления нового поста. Примерно также, как это реализовано в панели администратора Django. Подробнее можно прочитать в посте на Boosty "Django - Добавление новой категории в форме создания поста без перезагрузки страницы" (платный контент).
Формы добавления категории и файла поста.
Откроем файл
Форма добавления категории.
Создадим новый класс
Так же, как мы делали в посте "Django 36. Добавление постов пользователем", создаём конструктор класса
В нём добавляем всем полям формы bootstrap-класс
Обратите внимание, в прошлом посте мы делали поле необязательным к заполнению для корректной работы редактора. В этот раз поле необязательно по умолчанию, поэтому пропускаем этот параметр.
Далее создаём подкласс
Код формы:
Форма добавления файла.
Форма такая же, как и предыдущая. Исключение составляет только отсутствие дополнительного класса для поля, модель и сам список полей формы
Код формы:
Представления страницы добавления категории и страницы добавления файла поста.
Откроем файл
Прочитав вступление, вы можете задаться вопросом: "зачем нам три представления, если будет две формы?". Всё верно, мы создадим три представления, а не два.
В посте "Django 27.1 Представления на основе классов" я описывал преимущества классовых представлений, одно из них - наследование.
Оба представления практически идентичны, отчего было бы разумнее сделать общий класс представления и уже от него наследовать два конкретных представления для страниц.
Базовый класс.
Создадим класс
В теле класса будет всего два метода:
Формы добавления категории и файла поста.
Откроем файл
forms.py в директории приложения user_app.Форма добавления категории.
Создадим новый класс
AddCategoryByAuthorForm, унаследованный от ModelForm.Так же, как мы делали в посте "Django 36. Добавление постов пользователем", создаём конструктор класса
__init__.В нём добавляем всем полям формы bootstrap-класс
form-control и дополнительный класс для поля description - django_ckeditor_5, для работы визуального редактора Django CKEditor 5.Обратите внимание, в прошлом посте мы делали поле необязательным к заполнению для корректной работы редактора. В этот раз поле необязательно по умолчанию, поэтому пропускаем этот параметр.
Далее создаём подкласс
Meta, прописываем модель и необходимые поля.Код формы:
from blog.models import CategoryModel
class AddCategoryByAuthorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control', 'autofocus': ''})
self.fields['description'].widget.attrs.update({'class': 'django_ckeditor_5'})
class Meta:
model = CategoryModel
fields = ('title', 'parent', 'description', 'telegram_link')
Форма добавления файла.
Форма такая же, как и предыдущая. Исключение составляет только отсутствие дополнительного класса для поля, модель и сам список полей формы
Код формы:
from blog.models import PostFilesModel
class AddFileByAuthorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control', 'autofocus': ''})
class Meta:
model = PostFilesModel
fields = ('title', 'file')
Представления страницы добавления категории и страницы добавления файла поста.
Откроем файл
views.py в директории приложения user_app.Прочитав вступление, вы можете задаться вопросом: "зачем нам три представления, если будет две формы?". Всё верно, мы создадим три представления, а не два.
В посте "Django 27.1 Представления на основе классов" я описывал преимущества классовых представлений, одно из них - наследование.
Оба представления практически идентичны, отчего было бы разумнее сделать общий класс представления и уже от него наследовать два конкретных представления для страниц.
Базовый класс.
Создадим класс
AddCategoryAndFile с двойным наследованием: UserPassesTestMixin и CreateView.В теле класса будет всего два метода:
🔥1
test_func - для проверки того, что пользователь входит в группу "Автор". Подробнее об этом рассказывал в прошлом посте - "Django 36. Добавление постов пользователем".get_success_url - метод, определяющий куда будет перенаправлен пользователь после заполнения формы. В нашем случае на страницу профиля.Это всё, что будет в нашем классе. Остальные параметры будут заменены в классах-наследниках.
Код класса:
from django.contrib.auth.mixins import UserPassesTestMixin
from django.views.generic import CreateView
class AddCategoryAndFile(UserPassesTestMixin, CreateView):
def test_func(self):
return self.request.user.groups.filter(name='Автор').exists()
def get_success_url(self):
return reverse('user_app:user_profile', kwargs={'username': self.request.user.username})
Представление добавления файла.
Создадим класс
AddFileView, унаследованный от нашего класса AddCategoryAndFile.В этом представлении не будет методов, только четыре поля:
template_name - шаблон страницы.form_class - класс формы.model - используемая модель.extra_context - дополнительные данные, в нашем случае это заголовок страницы.Это весь класс.
Код класса:
from blog.models import PostFilesModel
class AddFileView(AddCategoryAndFile):
template_name = 'user_app/add_file.html'
form_class = forms.AddFileByAuthorForm
model = PostFilesModel
extra_context = {'title': 'Добавление файла'}
Представление добавления категории.
Создадим класс
AddCategoryView, унаследованный от нашего класса AddCategoryAndFile.В этом представлении будут точно такие же поля, как и в предыдущем, а также один метод:
form_valid - в этом методе мы приводим поле slug к корректному виду. Почему это важно сделать, я объяснял в прошлом посте.Код класса:
from blog.models import CategoryModel
class AddCategoryView(AddCategoryAndFile):
template_name = 'user_app/add_category.html'
form_class = forms.AddCategoryByAuthorForm
model = CategoryModel
extra_context = {'title': 'Добавление категории'}
def form_valid(self, form):
form.instance.slug = slugify(form.instance.title)
return super().form_valid(form)
URL-паттерны.
Откроем файл
urls.py в директории приложения user_app и в список urlpatterns добавим следующие строки:path('post/add_category/', views.AddCategoryView.as_view(), name='add_category'),
path('post/add_file/', views.AddFileView.as_view(), name='add_file'),Шаблоны страниц.
В директории с шаблонами приложения
user_app создадим два файла:add_category.html - файл с формой добавления категории.add_file.html - файл с формой добавления файла поста.Оба шаблона практически идентичны.
Различия заключаются в том, что в форме добавления категории выводится
{{ form.media }} для отображения визуального редактора, а в форме добавления файла указано enctype="multipart/form-data", означающее, что форма может передавать файлы.Код страницы добавления категории:
🔥1