👩💻 Компонент для номера телефона (продолжение)
#frontend #react
Ресёрч пришлось продолжать. Я нашёл библиотеку, которая позволяла валидировать номер телефона относительно двухбуквенного кода страны, к которой этот номер относится. Кроме прочего, в этой библиотеке есть и возможность форматирования номера телефона, как раз под маску, принятую в конкретной стране. В описании библиотеки также упоминался react-компонент, который использует эту самую библиотеку. И на основе уже этого компонента, я собрал своё решение с использованием ant-design (разумеется, оно не идеальное, но достаточно хорошее, чтобы использовать в условном продакшне):
import PhoneInput from 'react-phone-number-input';
import PropTypes from 'prop-types';
import { getCountries } from 'react-phone-number-input';
import { Select as AntdSelect } from 'antd';
const phoneCountryLabels = () => {
const countryLabels = {};
getCountries().forEach((country) => (countryLabels[country] = country));
return countryLabels;
};
const CountrySelect = ({ value, onChange, labels, variant = 'filled', ...rest }) => (
<AntdSelect
{...rest}
value={value}
showSearch
optionFilterProp="label"
onChange={onChange}
variant={variant}
size="large"
style={{ '--ant-select-single-item-height-lg': '3rem' }}
/>
);
CountrySelect.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
labels: PropTypes.objectOf(PropTypes.string).isRequired,
variant: PropTypes.string,
};
const PhoneNumberInput = ({ onChange, country = 'US' }) => {
return (
<div className="base-phone-number">
<PhoneInput
onChange={onChange}
defaultCountry={country}
international
limitMaxLength
labels={phoneCountryLabels()}
countrySelectComponent={CountrySelect}
numberInputProps={{
className: 'ant-input ant-input-filled css-var-r1 ant-input-css-var base-input',
}}
/>
</div>
);
};
PhoneNumberInput.propTypes = {
onChange: PropTypes.func.isRequired,
country: PropTypes.string,
};
export default PhoneNumberInput;
👩💻 Что нужно знать python backend developer для устройства на работу
#django #python #backend #fastapi
Пару лет назад я уже писал подобный пост. Тогда он был основан на моём личном опыте в найме (текущий, в общем-то тоже), пришло время актуализировать список. Если считаете, что я что-то упустил, то жду ваших предложений в комментариях.
👩💻 Фреймворки:
- Django (+ DRF),
- FastAPI/Flask (+ alembic, sqlalchemy).
👩💻 Базы данных:
- redis,
- postgresql.
👩💻 Основы:
- HTTP-протокол, HTTP-методы,
- что такое REST, REST API, RESTful,
- что такое CORS и как его победить,
- linux/unix на уровне понимания работы по SSH, права доступа,
- понимание концепции MVC и принципа работы паттерна репозиторий (больше нужен для работы с SQLAlchemy),
- вопросы для подготовки к интервью (список там довольно большой, обращайте внимание на те моменты, по которым вы совсем ничего не знаете).
👩💻 Что нужно знать в django:
Обязательно:
- работа с django ORM: https://docs.djangoproject.com/en/5.0/topics/db/queries/,
- миграции в django ORM: https://docs.djangoproject.com/en/5.0/topics/migrations/,
- менеджеры моделей: https://docs.djangoproject.com/en/5.0/topics/db/managers/,
- сериализаторы, представления и права доступа из DRF: https://www.django-rest-framework.org/tutorial/quickstart/,
- работа с фильтрами через django-filter: https://django-filter.readthedocs.io/en/stable/guide/usage.html.
Опционально:
- формы: https://docs.djangoproject.com/en/5.0/ref/forms/,
- admin actions: https://docs.djangoproject.com/en/5.0/ref/contrib/admin/actions/,
- оптимизация запросов в Django ORM: /channel/davidobryakov/1195,
- отправка почтовых уведомлений: https://docs.djangoproject.com/en/5.0/topics/email/.
На уровне концепции:
- работа с админкой: https://docs.djangoproject.com/en/5.0/ref/contrib/admin/,
- сигналы: https://docs.djangoproject.com/en/5.0/topics/signals/.
Полезные библиотеки и ссылки для django:
- библиотека для установки настроек CORS: https://github.com/adamchainz/django-cors-headers,
- библиотека для авторизации и регистрации через DRF: https://djoser.readthedocs.io/en/latest/,
- библиотека для создания динамической конфигурации приложения, хранимой в БД: https://django-constance.readthedocs.io/en/latest/,
- библиотека, добавляющая хуки жизненного цикла для моделей Django: https://rsinger86.github.io/django-lifecycle/,
- библиотека, предоставляющая кастомную админку с большим количеством настроек: https://django-jazzmin.readthedocs.io/,
- практические советы для начинающих Django-разработчиков: /channel/davidobryakov/1184,
- подборка лучших библиотек для Django: /channel/davidobryakov/1139,
- курс по Django на MDN: https://developer.mozilla.org/ru/docs/Learn/Server-side/Django,
- мой шаблон для Django-проектов: https://github.com/kantegory/django-template.
- работа с ORM SQLAlchemy: https://docs.sqlalchemy.org/en/20/intro.html,
- работа с alembic: https://alembic.sqlalchemy.org/en/latest/,
- понимание async/await: https://docs.python.org/3/library/asyncio-task.html.
👩💻 Что нужно знать/уметь в FastAPI/Flask:
- не поместилось по лимиту в ТГ, читайте полную версию поста в блоге.
Общие требования:
- работа с очередями задач с помощью Celery + Redis/Celery + RabbitMQ (выполнение периодических или отложенных задач, например, отправка электронной почты);
- настройка общения между сервисами посредством RabbitMQ
👩💻 Инструменты:
- git (тренажёр: https://learngitbranching.js.org/?locale=ru_RU),
- docker, docker compose (на уровне: могу прочесть конфиг, могу запустить проект, могу написать свой простой конфиг),
- gunicorn/uvicorn (иметь представление о том что это и для чего используется),
- nginx (понимание на уровне директив location и upstream).
👩💻 Вычисляемые свойства во Vue сломаны?
#frontend #vue #производительность
На reddit наткнулся на пост в комьюнити Vue.JS с описанием проблемы с производительностью у вычисляемых свойств (computed
). Заключается проблема в том, что судя по тестам использованием watch
+ ref
на 50% более эффективно, чем использованием computed
.
Добавим немного контекста. Тест проводился на дропдауне с 1 000 000 элементов. Задача теста состояла в том, чтобы написать функцию, которая будет конвертировать объекты (или строки) с типом selectOption
в объект selectOptionsObject
.
Типы выглядят так:
export type selectOption = selectOptionObject | string;
export type selectOptionObject = {
id: string | number;
render: string;
raw?: any;
};
normaliseOptions
для конвертации:const normaliseOptions = (
options?: selectOption[]
): normalisedOptionObject[] => {
if (!options) return [];
// We will use a straight for loop for performance
const normalisedOptions = [];
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (typeof option === "string") {
normalisedOptions.push({
id: option,
render: option,
});
continue;
}
normalisedOptions.push({
id: option.id.toString(),
render: option.render,
disabled: option.disabled || false,
raw: option.raw,
});
}
return normalisedOptions;
};
const normalisedOptions = computed(() => {
return normaliseOptions(props.options);
});
// Using the pattern below rather than a computed value gives us a 2x performance improvement
const normalisedOptions = ref(normaliseOptions(props.options));
const recomputeOptions = () => {
normalisedOptions.value = normaliseOptions(props.options);
};
watch(
() => props.options,
() => {
recomputeOptions();
}
);
computed
отслеживает каждую вложенную зависимость. Поэтому он будет работать медленнее, если props.options
не является плоской структурой, состоящей из примитивов, потому что требуется провести кратно больше вычислений. Таким образом, корректный код для данного случая выглядит так:const recomputedOptions = computed(() => normaliseOptions(toRaw(props.options)));
toRaw
извлекает необработанный объект из proxy-объекта, созданного Vue, давая нам возможность поработать с оригинальным объектом. Подробнее про него можно почитать в документации.
Читать полностью…
👩💻 Как мы docker с VPN дружили
#docker #backend
В прошлом году столкнулся с задачей по настройке VPN-подключения внутри docker для обеспечения доступа к API, находящемуся во внутренней сети предприятия. Недавно с подобной проблемой столкнулся и мой друг, но ситуации у нас несколько разные: я настраивал связку для openfortivpn
, а ему нужно было настроить для openvpn
.
Основная проблема, которая возникает при настройке VPN-соединения для нескольких контейнеров заключается в необходимости настройки сетевых параметров и особенностях работы docker. Без контейнеризации всё было бы намного проще: вы запускаете VPN-подключение и все ваши запросы к соответствующим хостам внутри VPN-сети проходят через это подключение.
Как работает сеть в docker и docker-compose
По умолчанию, все наши сервисы объединяются в рамках сети default
. При необходимости мы можем выделять отдельные сети для разных сервисов, чтобы изолировать их друг от друга. Кроме прочего, мы можем использовать внешнюю docker-сеть, чтобы объединить сервисы из разных приложений (отделённых своими compose-файлами) между собой.
Нетрудно догадаться, что для обеспечения VPN-подключения внутри docker-сети, нам потребуется уметь чуть больше, чем просто создание docker-сети. Тут на помощь приходят сетевые драйверы и network_mode
.
Сетевые драйверы
В официальной документации описано целых 7 различных сетевых драйверов. Начнём с того, что сам по себе сетевой драйвер нужен для расширения возможностей сети. Рассмотрим какие сетевые драйверы существуют (адаптированная информация из документации):
- bridge — драйвер по умолчанию, необходим для того, чтобы сервисы могли общаться друг с другом внутри docker-сети;
- host — драйвер, который позволяет использовать сеть хоста напрямую, убирая изоляцию контейнера от хоста;
- overlay — драйвер, позволяющий соединить несколько хостов, где находятся docker-контейнеры вместе, чтобы обеспечить взаимодействие между ними (такой вариант отлично подходит для обеспечения коммуникации между нодами в swarm mode);
- ipvlan — драйвер, предоставляющий полный контроль над адресацией IPv4 и IPv6;
- macvlan — драйвер, позволяющий назначить MAC-адрес для контейнера, делая его доступным в виде физического устройства внутри вашей сети;
- none — драйвер, позволяющий изолировать сеть контейнера от хостовой сети и сети других контейнеров;
- network plugins — возможность использования в качестве сетевого драйвера любого внешнего решения.
network_modenetwork_mode
это атрибут сервиса внутри конфигурации docker-compose, необходимый для более тонкой настройки. Существует несколько значений у атрибута newtork_mode
:
- bridge
— дефолтный способ для связи между контейнерами, работает по умолчанию;
- host
— использование единого сетевого пространства между выбранным сервисом и хостом;
- none
— отсутствие сетевого взаимодействия для выбранного сервиса;
- service:[service_name]
— использование сетевого пространства выбранного сервиса;
- container:[container_name/id]
— использование сетевого пространства выбранного контейнера (отличается от прошлого необходимость указания именно container_name/container_id, а не service_name).
К сожалению, в официальной документации compose, подробно ничего не сказано об этом атрибуте, кроме ссылки на то, что значения могут быть те же самые, что и у парамерта --network
, но также добавляется специальная форма service:[service name]
. Возможно, это сделано так именно потому, что все значения по смыслу похожи.
Пример compose-файла
Разницы для конфигурации между openfortivpn
и openvpn
особо нет, поэтому рассмотрим на примере openfortivpn
:
version: '3'Читать полностью…
services:
vpn:
container_name: openfortivpn
restart: unless-stopped
build: .
privileged: true
volumes:
- ./config:/etc/openfortivpn/config.conf
command: ["openfortivpn", "--config=/etc/openfortivpn/config.conf"]
some-service:
image: your-container-image
network_mode: service:vpn
Как менялось моё рабочее место 2021-2024
Делитесь своими фотографиями в комментариях👇
📆 Правильное форматирование дат в JS
#frontend
Буквально месяц назад писал свои мысли по поводу таких решений, как moment
/day.js
и ругал их за то, что они не используют нативные браузерные API.
И вот, моя мечта сбылась. На неделе я сидел и листал тематические каналы в поисках чего-то нового и интересного и наткнулся на пост про библиотеку formkit/tempo">tempo. В основе это решение использует браузерный объект Date
и Intl API
. При этом, API самой библиотеки достаточно простое и имеет довольно подробную документацию.
👩💻 219 файлов с кодом для drag-n-drop
#frontend
Проблема, с которой мы недавно столкнулись — это redux в зависимостях у react-beautiful-dnd. Есть какое-то внутреннее сопротивление тому факту, что кто-то втягивает целый стейт-менеджер в библиотеку, которая должна давать возможность перетащить карточку из одной колонки в другую. Разумеется, я несколько утрирую ситуацию, но в данном случае решение кажется необоснованным.
В моём понимании, библиотека для drag-n-drop, в целом, не нуждается в том, чтобы иметь свой собственный стетйт-менеджер, возможностей локального стейта компоненты для такого решения более, чем достаточно.
В случае, если стор всё-таки требуется, я предлагаю следующее решение: "стор должен поступать внешней зависимостью, согласно типизированного библиотекой интерфейса". То есть, сделать возможность передать стор внутрь библиотеки, создав кастомную обёртку, подходящую для конкретной библиотеки. Понимаю, что от этого сложность может возрасти, но почти наверняка такой подход позволит уменьшить итоговый бандл, хотя и увеличит время и сложность разработки. С другой стороны, это решение разработанное инженерами Atlassian, вряд ли они недостаточно компетентны, хотя по работе в Jira этого иногда и не скажешь.
Возможно, если у меня дойдут руки, то мы соберём отдельное собственное решение для drag-n-drop. В случае, если это произойдёт — я обязательно поделюсь ссылкой на решение.
В целом, ситуация, является лишним доказательством слов про то, что решение, разработанное большой компанией призвано решать проблемы этой самой большой компании, а не ваши.
👩💻 Нативные модалки
#frontend #браузеры
Пару лет назад, я писал про предложение о добавлении элемента, который стал бы нативным попапом. Судя по комментарию в топике этого предложения, идея элемента popup была перенесена в новую концепцию — Popover API. К моему удивлению, в данный момент это API поддерживается большинством современных браузеров, кроме Firefox. Подробности с примерами можно почитать, как в переводе на хабре, так и в оригинале. К своему стыду, я упустил новость о внедрении этого API в браузеры, хотя это произошло уже почти год назад. Постараюсь найти время и собрать под это дело интерактивную демку.
Внедрение различных новых фич в браузеры -- это всегда повод для радости. В декабре я писал про свойство field-sizing
(на момент публикации, в интернете об этом почти не было постов, я в числе первых, кто написал об этом на русском языке), сегодня хочу поделиться с вами новостью про добавление нового элемента <dialog>
.
Как этим пользоваться?
Я собрал небольшую демку на codepen, где используется элемент <dialog>
для отрисовки модального окна. Есть минимальная стилизация. Давайте разберём эту демку и попробуем как-то её усложнить.
<style>
::backdrop {
background: #000;
opacity: 0.75;
}
dialog {
border: unset;
border-radius: 10px;
}
dialog form {
display: flex;
justify-content: end;
}
</style>
<button onclick="dialog.showModal()">Открыть модалку</button>
<dialog id="dialog">
<p>Это нативное модальное окно</p>
<form method="dialog">
<button>ОК</button>
</form>
</dialog>
::backdrop
позволяет отрисовать затемнение общего фона страницы, чтобы отделить модальное окно от всего остального интерфейса. Стилизация самого элемента и формы не так интересна, её можно пропустить.showModal()
на элементе с id dialog
. Внутри тела самого модального окна есть форма, которая отвечает за различные действия, которые можно совершить. По умолчанию, нажатие на кнопку будет закрывать модальное окно.<button onclick="dialog.showModal()">Открыть модалку</button>
<dialog id="dialog">
<p>Хотите продолжить?</p>
<form method="dialog">
<button type="submit" value="no">Нет</button>
<button type="submit" value="yes">Да</button>
</form>
</dialog>
<script>
dialog.addEventListener('close', (event) => {
if (dialog.returnValue === 'yes') { /* ... */ }
});
</script>
👩💻 Лучший менеджер зависимостей - это docker
#docker #python
Именно такую фразу мне сказал старший товарищ при одной из бесед, но тогда я его не понял. Был ли он прав? Разбираемся с примерами.
Спустя несколько лет работы с docker, я пришёл к выводу, что не имеет смысла выбирать между pipenv, poetry или conda, поскольку их использование внутри docker является затруднительным и требует плодить костыли, вместо того чтобы просто установить зависимости через pip.
Основная проблема заключается в том, что внутри контейнера вам никакое виртуальное окружение не нужно, а значит - мы затягиваем лишнюю зависимость, которая увеличит время сборки, зато локальная разработка без докера становится удобнее и приятнее. Только я, хоть убей, не пойму - какой смысл разрабатывать локально приложения на python без докера. Пожалуй, есть только один небольшой минус - нужно вписать новую зависимость в requirements.txt вручную. Но и этого можно избежать, если вы просто зайдёте в терминал контейнера, установите зависимость там и сделаете pip freeze в нужный файлик (разумеется, при условии, что зависимости попадают в volume с приложением).
Полная версия у меня в блоге.
Спасибо, что были со мной в этом году!
Хотя и не сказать, что я достаточно часто делился с вами постами и видео, но обещаю, что в 2024м вас ждёт много интересного, планы, как всегда, наполеоновские.
В знак благодарности решил разыграть 5 годовых подписок на телеграм премиум среди вас, давно было интересно, как это вообще работает :)
Новое свойство field-sizing
или autoresize для textarea без костылей
#frontend #css
Во вчерашней подборке от reddit, увидел интересный пост про новое CSS-свойство field-sizing
, которое позволяет <textarea>
и <input type="text">
автоматически расширяться в зависимости от контента.
Автор треда говорит о том, что совсем скоро это завезут в Chrome Canary. Я покопался и на самом деле это свойство туда уже успели завести, по крайней мере, так было написано на chromestatus. Немного вчитался и обнаружил, как можно запустить Chrome Canary с включённым свойством, поскольку в chrome://flags
оно пока недоступно (команда, работающая у меня на MacOS, вам надо заменить путь до Chrome Canary, самый простой способ узнать это — открыть в своей Chrome Canary chrome://version
):
$ /Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --flag-switches-begin --enable-features=CssFieldSizing --flag-switches-end --origin-trial-disabled-features=WebGPU
<!DOCTYPE html>Читать полностью…
<html lang="en">
<head>
<style>
textarea, input {
field-sizing: content;
min-height: 10px;
min-width: 100px;
}
</style>
</head>
<body>
<div>
<textarea></textarea>
</div>
<div>
<input type="text">
</div>
</body>
</html>
⌛️ Наводим порядок в жизни
На этот пост меня натолкнула статья с хабра, но здесь я хочу поделиться исключительно своим опытом. Я не буду рассказывать вам про различные популярные практики, не вижу смысла дублировать эту информацию. Хочу рассказать только о том, как я учился управлять своим временем, какие ошибки я совершал, и к чему это в итоге привело.
Впервые задумываться о тайм-менеджменте я начал на втором-третьем курсе университета, когда количество моих активностей перестало умещаться в голове. Не могу сказать, что я когда-либо гнался за невероятной продуктивностью (ну, да, конечно), но меня довольно часто спрашивали как мне удаётся все успевать. И только недавно я начал быть до конца честным с собой и говорить: "никак" (отчасти, именно изменение моего отношения к этому и стало мотивом для переосмысления и работы в этом направлении).
Но вернёмся на 4 года назад, тогда я выработал для себя ряд правил и очень много сил уделил самодисциплине. Основными правилами для меня были:
- публичность (хотя бы для аудитории из 5-7 своих друзей, к слову, так и начинался этот канал);
- наличие чёткого плана на день и неделю;
- строго отведённое время на работу над каким-то делом (доходить могло вплоть до того, что я мог оставлять задание на лабораторную не до конца решенным, оставляя его на следующий день);
- строго отведённое время на отдых (в это время я мог почитать, погулять, да и просто непродуктивно посидеть в интернете).
В то время благодаря этим правилам, мне удалось закрыть все предметы к середине семестра, перевестись на бюджет и довольно сильно развиться в профессиональном плане. Но хватило меня где-то на полгода жизни в таком темпе (что неудивительно), после чего я подустал и долго не мог вернуться к нормальному тайм-менеджменту. Это, на самом деле, довольно частая проблема и в какой-то момент одной только мотивации становится недостаточно, нужна дисциплина — регулярность и постоянство.
Чтобы не повторять прошлых ошибок, я решил, что не хочу вести слишком подробные списки дел, фиксировать все планы в календаре и стараться успевать всё на свете. Начал с малого:
- составление примерного плана на неделю каждое воскресенье (с оценкой пунктов по времени);
- фиксирование всех обязательных еженедельных мероприятий в календаре (например, поход в зал).
Это помогло практически безболезненно начать налаживание баланса между работой и личной жизнью, а также привести в порядок дела и закрыть все накопленные долги по работе.
После того, как я понял, что мои дела потихоньку начинают приходить в порядок, а я всё равно ложусь спать довольно поздно и часто до полуночи ещё сижу и занимаюсь работой, я решил продолжить вносить изменения в уклад своей жизни и добавил следующие вещи:
- режим сна и бодрствования (ложусь до полуночи, встаю в 7:15);
- обязательная суставная разминка каждое утро (очень хорошо помогает проснуться);
- раннее начало рабочего дня (8:30-9:00).
Таким образом, мне удалось добиться желанной спокойной работы с утра, вплоть до начала первых созвонов, что позволило раскидывать 80% задач в начале дня и оставшиеся 20% в течение дня. Также, мой рабочий день перестал растягиваться и самое позднее, когда я завершаю работу при текущем графике — 19:00 (это не значит, что я работаю более 10 часов в день, ведь у меня, как и у всех могут быть перерывы на обед/ужин/небольшую прогулку). Кроме прочего, суставная разминка оказалась очень полезна не только для того, чтобы проснуться, но и чтобы мышцы быстрее приходили в себя после тяжёлых тренировок. Великой радостью стало и отсутствие какой-либо работы на выходных, что позволяет мне сейчас сидеть и писать этот пост, а не пытаться пофиксить очередной баг в проде :)
💬 Делитесь своим опытом в комментариях, будет интересно почитать
👩💻 Полезные плагины для разработчика в Firefox
#подборка #firefox #плагины
Довольно давно думал сделать свою небольшую подборку плагинов, которые я активно использую при дебаге и разработке фронтенда, используя браузер Firefox. Месяц назад я уже рассказывал про контейнеры и user agent switcher. Сегодня хочу рассказать про совсем небольшие инструменты, которые могут немного облегчить вашу жизнь.
1) Font finder — https://addons.mozilla.org/ru/firefox/addon/font-inspect/
Расширение позволяет ткнуть в любой участок сайта и узнать какой шрифт там используется, что сильно упрощает процесс поиска и позволяет отследить тот ли шрифт по итогам применился к указанному селектору через CSS, или мы по какой-то причине используем фоллбэк/шрифт из фреймворка. Разумеется, это можно сделать и через dev tools, но этот плагин позволяет вам не копаться во всей CSS-внутрянке отдельной области, а сразу напрямую вытаскивает всё, что вам нужно знать.
2) Autocomplete control — https://github.com/kopach/autocomplete-control
Небольшой плагин, который мало кому может быть полезен, но хорошо работает в моём случае, при записи видео. Очень часто брауезрный автокомплит выводит какие-то личные данные, что не очень хорошо, как по мне. Вещь ситуативная, но иногда может быть полезной.
3) Local storage editor — https://addons.mozilla.org/ru/firefox/addon/localstorage-editor/
Моя вчерашняя находка, надоело постоянно бегать в dev tools и копировать local storage с прод-версии на локальную, чтобы воспроизвести какое-либо ошибочное поведение, поэтому решил поискать решение для своей проблемы и оно нашлось. Интерфейс не самый красивый конечно, но со своими функциями справляется и этого мне достаточно.
4) Mind the time — https://addons.mozilla.org/ru/firefox/addon/mind-the-time/
Простенький таймтрекер, позволяющий отслеживать количество времени, проведённого на том или ином сайте. Позволяет ограничивать время на определённых веб-сайтах, чтобы не сильно отвлекаться от работы. В пассивном режиме позволяет просто оценить насколько много чистого времени было проведено на определённом ресурсе. 3 года назад у меня даже была серия постов про цифровой баланс, в которых я использовал это расширение для подсчёта времени, чтобы понять и проанализировать себя и свои привычки:
/channel/davidobryakov/916
/channel/davidobryakov/924
/channel/davidobryakov/935
5) Video Speed Controller — https://addons.mozilla.org/ru/firefox/addon/videospeed/
Не могу сказать, что эту вещь использую только для разработки, но в том числе и для неё, когда требуется посмотреть какой-то доклад с конференции и хочется смотреть его быстрее, чем в 2x, либо когда видео проигрывается из плеера без поддержки такой функции по умолчанию. Имеет набор шорткодов, которые позволяют не бегать мышкой по экрану, а просто накликать нужную скорость.
Про расширения и плагины для vue/react упоминать в подборке большого смысла не вижу, потому что это довольно очевидные вещи.
💬 Пишите в комментариях, какими плагинами пользуетесь вы, будет интересно почитать :)
📂 Друзья, мне тут принесли общую папку каналов про ИТ. Собрано много интересных тем, чтобы держать руку на пульсе: тематические мероприятия, аналитика, QA, нейронные сети, а также авторские каналы про мир больших данных и разработки.
😎 Добавляйте себе папку и будьте в курсе всего самого интересного и необычного в ИТ-индустрии!
❗️ Внимание: чтобы все работало, важно установить последнюю версию Telegram
👩💻 Работа с docker, docker compose, swarm mode — Бэкенд-разработка на Node.JS #8
#docker #backend #вебинар
Привет! Это запись моего вебинара, в рамках курса по бэкенд-разработке в ИТМО. В этом видео разбираем docker, docker-compose, говорим про swarm mode.
▶️ Смотреть на Дзен | Смотреть на YouTube
Плейлист на Дзен | Плейлист на YouTube
📖 Полезные ссылки:
Статья на хабре про docker swarm: https://habr.com/ru/post/659813/
Раздел документации про swarm mode: https://docs.docker.com/engine/swarm/
Статья про docker-compose: https://habr.com/ru/company/ruvds/blog/450312/
Раздел документации про docker-compose: https://docs.docker.com/compose/
Репозиторий Swirl: https://github.com/cuigh/swirl
Пример настройки приложения с docker swarm: https://www.youtube.com/watch?v=NZRfJORJACQ
Презентация: https://docs.google.com/presentation/d/1QhqPb2nmiwmzZ_e6pcAHAGHRHlep1i04OEmE8PsKRxA/edit?usp=sharing
Репозиторий этого курса: https://github.com/kantegory/ITMO-ICT-Backend-2023
Ютуб: dobryakov" rel="nofollow">https://youtube.com/@dobryakov
Блог: https://blog.kantegory.me
Дзен: https://dzen.ru/dobryakov
Бусти: https://boosty.to/dobryakov
👩💻 Компонент для номера телефона
#frontend #react
Около месяца назад я писал пост про библиотеку imask, чтобы стандартизировать ввод номера телефона, с помощью использования маски. Тогда же я упомянул, что основным минусом, на мой взгляд, является то, что нужно написать много дополнительного кода, чтобы достичь желаемого результата.
Пару недель назад я как раз столкнулся с подобной задачей в рамках проекта на react и захотел попробовать реализовать это с помощью imask, но столкнулся с проблемой: нужно отображать список стран, с привязанным к ним кодом. В imask есть возможность решить это, но нет готового набора кодов номеров телефонов. Я вспомнил о решении, которое мои сотрудники находили. Попробовал его адаптировать и понял, что я особо не могу его кастомизировать (по дизайну требовалось выводить двухбуквенное название страны, а не флаг), несмотря на его описание, — "Advanced, highly customizable phone input component for Ant Design."
Поняв, что нахожусь в сложной ситуации, я пошёл искать библиотеки, которые предоставляют просто список телефонных кодов по странам, чтобы реализовать это с помощью imask и нашёл такое решение. По сути, это простая библиотечка, которая возвращает нам объект из пар ключ-значение в том формате, в котором нам необходимо. Казалось бы, всё замечательно. Можно брать динамически маски в imask, генерировать их с помощью этих самых кодов и идти спокойно пить чай. Но в этот момент я осознал, что у разных стран, буквально, могут быть разные маски для ввода номера телефона и это окончательно закопало мою прекрасную идею об использовании imask для решения этой задачи.
Пример с самого сайта imask, где наглядно видно, что маски вообще не подчиняются единым правилам и могут выглядеть абсолютно как угодно:
[Читать полностью…
{
mask: '+00 {21} 0 000 0000',
startsWith: '30',
lazy: false,
country: 'Greece'
},
{
mask: '+0 000 000-00-00',
startsWith: '7',
lazy: false,
country: 'Russia'
},
{
mask: '+00-0000-000000',
startsWith: '91',
lazy: false,
country: 'India'
},
{
mask: '0000000000000',
startsWith: '',
country: 'unknown'
}
]
👩💻 Браузерные ОС
#браузеры #linux #wasm
На одной из прошлых работ гендиректор сказал мне, что считает, что операционные системы со временем перетекут в браузер, в качестве аргумента он упомянул Electron, который является базой для большого числа современных десктоп-приложений. Я запомнил это, но не придавал значения его словам, хотя они с каждым днём всё ближе к реальности.
Пару лет назад я писал про WebVM, который представляет из себя linux без графической оболочки, работающий через WASM. Пару недель назад видел несколько постов про InternetOS puter. По сути, она представляет из себя графическую оболочку рабочего стола, работающую на веб-технологиях (js + jQuery).
В отличие от WebVM, puter это только GUI, операции происходят на сервере (заметно, что на каждое действие, отправляется запрос в API). Документации для API-сервера пока что нет. Его исходного кода мне также не удалось найти (допускаю, что плохо искал, хотя и заглянул в каждый репозиторий). Разработчики обещают опубликовать документацию по API-серверу к концу марта, чего я буду ждать с нетерпением.
Как вы думаете, есть ли будущее у браузерных операционных систем? Смогут ли они со временем стать заменой привычным десктопным оболочкам?
Мне кажется, что определённый тренд в этом направлении есть и во многом это происходит уже сегодня, правда в облачном гейминге. Количество сервисов, предоставляющих подобную услугу, уже довольно большое, значит, это находит некий отклик среди аудитории. Думаю, в ближайшие годы мы будем наблюдать всё больше развития в этой сфере.
Маска для номера телефона
#frontend #браузеры #стандартизация
Проблема стандартизации форматирования и валидации поля ввода номера телефона абсолютно не новая. Даже в MDN говорят о том, что <input type="tel">
в отличие от <input type="email">
не имеет никакой автоматической валидации, потому что "форматы телефонных номеров сильно раличаются по всему миру".
Решение, которое мы можем без труда соорудить стандартными средствами браузера, не используя дополнительных библиотек выглядит примерно так:
<div>
<input
type="tel"
pattern="(^8|7|\+7)([0-9]{10})"
required
>
</div>
<div>
<small>Формат: +79999999999</small>
</div>
IMask(
document.getElementById('phone-mask'),
{
mask: '+{7}(000)000-00-00'
}
)
👩💻 Вычисляем дату с учётом рабочих дней на python
#python #backend
Полная версия с разбором решения доступна у меня в блоге.
Сегодня на работе столкнулся с задачей, когда надо было посчитать дату оплаты счёта, с учётом праздников и выходных (чтобы выводилась дата по условию "не позднее, чем через 5 рабочих дней"). Ранее подобных вещей я не делал, поэтому пошёл гуглить.
Ищем готовое решение на python
На этапе ресёрча по задаче нашёл такое решение:
import datetime
import numpy as np
start = datetime.date(2022, 2, 15)
end = datetime.date(2022, 3, 16)
# include holidays in a list
days = np.busday_count(start, end, holidays=['2022-02-21'])
print('Number of business days is:', days)
from datetime import date
import holidays
ru_holidays = holidays.RU()
date(2024, 3, 8) in ru_holidays # True
date(2024, 3, 7) in ru_holidays # False
>>> date(2024, 5, 9) in ru_holidays
True
>>> date(2024, 5, 10) in ru_holidays
False
>>> import datetime
>>> import numpy as np
>>> start = datetime.date(2024, 5, 6)
>>> end = datetime.date(2024, 5, 12)
>>> days = np.busday_count(start, end)
>>> days
5
holidays
, numpy считает, что вся неделя с 6 по 12 рабочая, кроме 2 выходных.import datetime
import numpy as np
# глобальные константы откуда-то из settings/django constance
HOLIDAYS = ['2024-03-08', ...]
BUSINESS_DAYS_DELTA = 5
def found_business_days_date(step = 5, _start_date = None):
start_date = _start_date or datetime.datetime.now().date()
end_date = start_date + datetime.timedelta(days=step)
days = np.busday_count(start_date, end_date, holidays=HOLIDAYS)
if days == BUSINESS_DAYS_DELTA:
return end_date
return found_business_days_date(step + 1, start_date)
👩💻 Стор или глобальные реактивные переменные во vue?
#vue #frontend
Несколько месяцев назад, в подборке от reddit мне попался довольно странный вопрос в сабреддите vue.js: "Почему я должен использовать Pinia вместо глобальных реактивных переменных?".
Странным он показался мне, в первую очередь, из-за того, что я никогда об этом всерьёз не задумывался и выбирал использовать какой-либо стейт-менеджер, вместо переменных, потому что так заведено. Как будто, это тема, в которой и так всё ясно, дополнительных исследований не требуется: хочешь реактивность и надёжность — бери стейт-менеджер, он это предоставляет.
В тексте своего сообщения автор опирается на раздел документации Vue, в котором рассказывается о том, как управление состоянием устроено в компонентах. По мнению пользователя reddit, у использования глобальных реактивных переменных есть такие плюсы, как:
1) простота;
2) нативная поддержка фреймворка;
3) нет зависимостей, что означает, не появится ситуации, как с Vuex (который прекратили развивать), в которой придётся переписывать половину кодовой базы;
4) Composition API выглядит более зрело и стабильно и вряд ли изменится в ближайшем будущем (в сравнении с переходом с Vue 2 на Vue 3);
5) возможность использования всей мощи реактивных API, вместо урезанной реактивной обёртки Pinia;
6) разница в эффективности при миллионе операций записи довольна внушительна:
Ref time: 36ms
Pinia ref time: 628ms
Reactive time: 501ms
Pinia reactive time: 809ms
👩💻 Оптимизация запросов в Django ORM
#python #django #backend
Сегодня хочу рассказать про методы select_related
и prefetch_related
: показать разницу между ними, привести примеры использования. Полная версия с примерами доступна у меня в блоге.
В чём разница?
Если говорить совсем кратко, то: select_related
— это JOIN
, то есть происходит 1 запрос, в рамках которого связываются указанные сущности и приходят в python уже связанными, в то время, как prefetch_related
— делает отдельные запросы по каждой указанной связанной сущности и производит связывание на уровне python.
В документации django сказано, что select_related
стоит применять в тех случаях, когда вы используете ForeignKey
, если вы используете ManyToManyField
, ваш выбор — это prefetch_related
.
Подробности и подводные камни
Предположим, что наши модели выглядят следующим образом:
from django.db import models
class Genre(models.Model):
name = models.CharField(max_length=512)
class City(models.Model):
...
class Person(models.Model):
hometown = models.ForeignKey(
City,
)
class Book(models.Model):
name = models.CharField(max_length=512)
author = models.ForeignKey(Person, on_delete=models.CASCADE)
genres = models.ManyToManyField(Genre)
prefetch_related
, важно помнить о такой вещи, как инвалидация кэша на уровне Queryset
(инвалидация может быть вызвана любым дополнительным методом в цепочке, который подразумевает другой запрос к базе данных). Например, при использовании фильтрации в следующем примере, будут выполнены отдельные запросы для каждого объекта Book
:for book in Book.objects.prefetch_related("genres"):
print(book.name, ":")
for genre in Book.genres.filter(age_rating="18+"):
print(" ", genre.name)
Prefetch
, который позволяет использовать подготовленный Queryset
вместе с prefetch_related
. Например, мы хотим отобрать заранее только те жанры, которые имеют возрастной рейтинг "18+" и посчитать количество книг по каждому такому жанру.genres = Genre.objects.filter(age_rating="18+")
.annotate(books_count=Count("book_set"))
queryset = Book.objects.all().prefetch_related(
Prefetch("genres", queryset=genres)
)
👩💻 Firefox использует chrome под капотом?
#firefox #chrome #браузеры
К такой мысли можно случайно прийти, если открыть логотип браузера, который выводится на странице открытия новой вкладки, поскольку он открывается по адресу:
chrome://branding/content/about-logo.png
"был в сети 15 минут назад" — учимся форматировать даты на фронтенде
#frontend #intl
Идея этого поста пришла ко мне во время кодревью, в процессе которого я вспомнил про такую штуку, как dayjs. Библиотека, которая на мой взгляд, в принципе не нужна, потому что существует Intl API (которым, к сожалению, мало кто пользуется). Причём про это даже в саркастичном ключе написал автор канала ExtremeCode. И ещё тогда я пересылал друзьям и писал, что смешно, что такое решение существует. Но пошутили и забыли, а тут я увидел его в MR и сразу забраковал. И дело вовсе не в саркастичных постах.
Если говорить кратко, dayjs это более современная замена moment.js. Только весит moment в распакованном виде 4.35 Мб, а dayjs всего 664 Кб. Впрочем, это не значит, что это решение в чём-то действительно лучше, поскольку, ни то, ни другое не использует браузерные API, а предоставляет вам свой собственный велосипед. И он действительно может быть неплох в каких-то моментах, но вряд ли вы будете спорить с тем, что решение, интегрированное в браузер будет менее оптимальным выбором.
В нашем случае, dayjs был втянут в проект только чтобы отрисовывать текст типа: "30 минут назад", вместо конкретной даты и времени. Именно это мне и не понравилось, что ради одного красивого вывода мы тянем целую отдельную библиотеку. В качестве альтернативы, я накидал небольшой пример для решения задачи, которую мой сотрудник пытался решить с помощью dayjs:
function countTimedeltaFromToday(date: Date) {
const currDate = new Date();
const delta = currDate.getTime() - date.getTime();
const ONE_MILLISECOND = 1000;
const HOUR_IN_SECONDS = 60 * 60;
const DAY_IN_HOURS = 24;
const MONTH_IN_DAYS = 30;
const YEAR_IN_MONTH = 12;
const daysDelta = delta / ONE_MILLISECOND / HOUR_IN_SECONDS / DAY_IN_HOURS;
const monthsDelta = daysDelta / MONTH_IN_DAYS;
const yearsDelta = monthsDelta / YEAR_IN_MONTH;
return {
delta,
daysDelta,
monthsDelta,
yearsDelta,
};
}
function formatDate(intl: Intl.RelativeTimeFormat, date: Date) {
const MAX_DAYS_DELTA = 15;
const MAX_MONTHS_DELTA = 11;
const delta = countTimedeltaFromToday(date);
const { daysDelta, monthsDelta, yearsDelta } = delta;
if (daysDelta < MAX_DAYS_DELTA) {
return intl.format(Math.floor(-daysDelta), 'day');
}
if (monthsDelta < MAX_MONTHS_DELTA) {
return intl.format(Math.floor(-monthsDelta), 'month');
}
return intl.format(Math.floor(-yearsDelta), 'year');
}
const intl = new Intl.RelativeTimeFormat('ru', { style: 'long', numeric: 'auto' });
Есть ощущение, что раньше я никогда публично не подводил итоги года.
Не скажу, что в этом году появилось какое-то жгучее желание, но вот поделиться тем, что мы вместе сделали за год — считаю вполне можно.
Из важных наблюдений — в мою жизнь потихоньку вернулась систематизация к концу года. Максимализма поубавилось — редко нынче возникает желание сидеть и каждый день выдавать по посту среднего или низкого качества, но в данный момент уже на январь у меня готовится много интересного (и хочется верить, что тот жизненный опыт, который я получал на работе, пока не писал посты станет неплохим подспорьем).
Обещаю в 2024м перестать пропадать из публичного пространства и делиться с вами опытом, наблюдениями и интересными новостями значительно чаще, чем удавалось в 2023м.
Кроме прочего, уже около полугода руки никак не доходят выложить очень длинный ролик, каких я раньше не делал. Приложу все усилия, чтобы вы увидели его в январе.
Планы на 2023й у меня были довольно большие, но не могу сказать, что все из них по итогам были выполнены. Впрочем, это не главное. Могу сказать, наверное, что с головой уходил в работу, отчего времени и сил не хватало на всё остальное.
Обещаю себе, что с понедельника всё изменится. Удачно совпало начало года с началом недели. Тут точно у каждого любителя начать новую жизнь в понедельник — не получится отвертеться, как и у меня, надеюсь :)
Ну а кроме циферок активности в телеграме и на ютубе у меня есть и личные итоги, 2023 мне запомнился потому, что:
- я впервые встал на сноуборд;
- я снова начал играть в настольный теннис;
- я постарался вернуть дисциплину в свою жизнь, и это уже приносит свои плоды;
- я попробовал играть в разные видеоигры (в детстве я играл в них очень редко, а после 18 лет у меня и вовсе не было времени, поэтому даже в довольно старых играх для меня много откровений);
- я сделал не только самый провальный проект на работе, но и внёс довольно значимый вклад в самый успешный проект (когда-нибудь позже расскажу).
А каким для вас был этот год?
Всех с наступающим! 🎉
Практические советы для начинающих django-разработчиков
#django #python #backend
Около месяца назад стартовали новый проект на Django и так вышло, что на нём работают Junior+ и Intern в паре. Причём, все последние проекты Junior+ разработчика были на FastAPI, из-за чего Django он подзабыл. По мере разработки я собирал в заметках все советы, которые давал разработчикам на проекте.
Я решил собрать их все в одной статье, дополнив важными особенностями (там есть информация про docker, .env, settings и даже ссылка на наш шаблон). Вот наиболее полезные советы для новичков (на мой взгляд):
1) когда вы делаете поле ForeignKey
, не называйте его model_name_id
, лучше просто model_name
, поскольку model_name_id
в случае FK резервируется ORM под возможность получать именно id, а не инстанс модели целиком, что позволяет избежать лишних запросов к БД;
2) если вы напишите человекочитаемый verbose_name
у всех полей для каждой модели - это сильно облегчит пользование админкой Django;
3) при написании представлений в Django старайтесь в названии представления отражать то, к чему оно действительно относится, также используйте нормальную форму, к примеру не DetailedModelName
, а ModelNameDetailView
;
4) старайтесь держать нейминг приложений согласованным, например, если вы назвали какое-то приложение в множественном числе, в то время, как все остальные названы в единственном числе, - это будет выглядеть неаккуратно;
5) если вы используете choices, вам следует завести их ключи свойствами класса модели, либо вынести их в отдельный класс, чтобы обращаться к константе, а не указывать конкретное значение.
🕹 Подборка игр для изучения фронтенд-разработки
#подборка #frontend
В моём университетском курсе по фронтенду есть домашняя работа, целью которой является прохождение трёх игр (это уже почти классика):
1) Flexbox Froggy — https://flexboxfroggy.com/#ru
Игра про лягушек и кувшинки, которая учит основам вёрстки с помощью flexbox, формирует начальное понимание об использовании flex-контейнеров и позволяет запомнить и закрепить основные свойства, необходимые для работы с flexbox.
2) Grid Garden — https://cssgridgarden.com/#ru
Морковки, грядки, сорняки... прекрасная возможность научиться работать с CSS Grid и прощупать каждый основной момент этой технологии.
3) Learn git branching — https://learngitbranching.js.org/?locale=ru_RU
Тренажёр по git, в котором довольно много теории, подающейся в игровой форме. Но учит эта игра не только базовым вещам, но и более продвинутому уровню (rebase, remotes, etc.). Проверено на личном опыте: все мои подчинённые джуны обязаны проходить эту игру, когда учатся правильной работе с git.
Недавно я задумался о том, какие ещё есть способы для изучения тех же технологий, прочитал несколько подборок и выделил ещё 3 наиболее интересных, на мой взгляд, игры:
1) Grid Attack — https://codingfantasy.com/games/css-grid-attack
Игра в стиле мультяшного фэнтези на целых 80 уровней, в которых нужно защищать своего героя от атаки монстров путём определения корректных значений для различных свойств CSS Grid.
2) FlexBox Adventure — https://codingfantasy.com/games/flexboxadventure
Восьмибитная графика, приятные анимации и обучение CSS, больше тут добавить нечего. Лично мне — понравилось.
3) JSRobot — https://lab.reaal.me/jsrobot/
Ещё одна восьмибитная игра, но в этот раз уже по JS, здесь вы учитесь управляться с экземплярами классов, вызывать их методы, чтобы всячески управлять роботом на экране, ведя его к цели каждого из представленных уровней.
💬 Есть что добавить? Поделись в комментариях :)
👩💻 Протоколы в python
#python #типизация
Листая хабр на предмет интересных статеек наткнулся на текст про протоколы в python. Эта заметка в меру корявенькая и некоторые вопросы рассматривает не совсем верно (а какие-то нюансы и вовсе не затрагивает), но спасибо ей, что побудила меня сесть и поразбираться в том, как это работает в действительности. Более удачная статейка с хабра по теме, если кому интересно.
Я разобрался в том, как работают протоколы и постарался сделать краткую выжимку из спецификации, обратив внимание на самые важные моменты (на мой взгляд, конечно).
📖 Читать на Teletype: https://blog.kantegory.me/python-protocols
💬 Буду рад, если поделитесь мнением в комментариях :)
🤔 Фраза дня: "Функциональные требования"
Вышло видео с моей лекцией для ДПО про оценку качества реализации программного средства. Рассказываю про характеристики из различных стандартов и стараюсь вывести из них нечто более близкое к привычной разработке посредством различных сочетаний этих самых характеристик.
И, конечно, в презентации не обошлось без 3-человечков, старался разбавить ими почти каждый слайд, чтобы не грузить слушателей излишней серьёзностью.
На видео читаю эту лекцию уже в третий раз, но впервые в жизни трансляция шла из студии, так что строго прошу не судить :)
▶ Смотреть на YouTube
👩💻 Контейнеры в Firefox
Я пользуюсь Firefox уже более 3х лет. И он всё это время продолжает меня радовать. Уже больше полутора лет хочу рассказать вам об одной функции, которая кажется мне довольно удобной. Это контейнеры. С их помощью можно не только разделить своё взаимодействие с браузером на разные профили (работа, учёба, развлечения), но и забыть про режим "Инкогнито" в разработке, поскольку можно поддерживать сразу несколько активных сессий на разрабатываемом сайте, при этом не бегая между окнами, а всего лишь переключаясь между вкладками.
Контейнеры помогают мне практически ежедневно, потому что мультиаккаунтинг, встроенный в браузер — это незаменимая вещь. В случае же, если вам необходимо подменять User Agent, вы также можете задавать их внутри контейнеров и использованием плагина "User Agent Switcher and Manager". Не сказать, что это часто бывает полезно, ведь спецификация User Agent потихоньку уходит в прошлое, но иногда это может помочь при тестировании.
Если вам интересно узнать об этой функции больше, то можете почитать официальный гайд от Mozilla.
Знали про эту функцию? Находите ли вы её полезной? Пишите в комментариях 👇
👩💻 Микросервисы — Бэкенд-разработка на Node.JS #7
#микросервисы #backend #вебинар
Привет! Это запись моего вебинара, в рамках курса по бэкенд-разработке в ИТМО. В этом видео разбираем микросервисную архитектуру, говорим про отличия от монолитной и сценарии использования этих подходов.
▶️ Смотреть на Дзен | Смотреть на YouTube
Плейлист на Дзен | Плейлист на YouTube
📖 Полезные ссылки:
Статья от Atlassian: https://www.atlassian.com/ru/microservices/microservices-architecture/microservices-vs-monolith
Статья на хабре: https://habr.com/ru/company/raiffeisenbank/blog/346380/
Пример из видео: https://itnext.io/designing-microservices-with-expressjs-eb23e4f02192
Презентация: https://docs.google.com/presentation/d/1pwV0WHDG2QoV5GK067dd1nbq4_RDQUambryIfVjWQnw/edit?usp=sharing
Репозиторий этого курса: https://github.com/kantegory/ITMO-ICT-Backend-2023
Ютуб: dobryakov" rel="nofollow">https://youtube.com/@dobryakov
Блог: https://blog.kantegory.me
Дзен: https://dzen.ru/dobryakov
Бусти: https://boosty.to/dobryakov