davidobryakov | Unsorted

Telegram-канал davidobryakov - @davidobryakov

629

Немного преподаю, немного управляю. Автор — @kantegory Github — https://github.com/kantegory Дзен — https://dzen.ru/dobryakov Boosty — https://boosty.to/dobryakov YouTube — https://youtube.com/@dobryakov Блог — https://blog.kantegory.me

Subscribe to a channel

@davidobryakov

👩‍💻 Компонент для номера телефона (продолжение)

#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;


А как вы решали подобные задачи? Делитесь, будет интересно узнать.

P. S. как появится немного времени, планирую тоже самое адаптировать под vue-компонент и поделиться результатом

Читать полностью…

@davidobryakov

👩‍💻 Что нужно знать 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).

Читать полностью…

@davidobryakov

👩‍💻 Вычисляемые свойства во 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;
};


Код, который использовался в тесте:

1) С использованием computed:

const normalisedOptions = computed(() => {
return normaliseOptions(props.options);
});


2) С использованием watch + ref:

// 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, давая нам возможность поработать с оригинальным объектом. Подробнее про него можно почитать в документации.

Читать полностью…

@davidobryakov

👩‍💻 Как мы 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_mode

network_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

Читать полностью…

@davidobryakov

Как менялось моё рабочее место 2021-2024

Делитесь своими фотографиями в комментариях👇

Читать полностью…

@davidobryakov

📆 Правильное форматирование дат в JS

#frontend

Буквально месяц назад писал свои мысли по поводу таких решений, как moment/day.js и ругал их за то, что они не используют нативные браузерные API.

И вот, моя мечта сбылась. На неделе я сидел и листал тематические каналы в поисках чего-то нового и интересного и наткнулся на пост про библиотеку formkit/tempo">tempo. В основе это решение использует браузерный объект Date и Intl API. При этом, API самой библиотеки достаточно простое и имеет довольно подробную документацию.

Читать полностью…

@davidobryakov

👩‍💻 219 файлов с кодом для drag-n-drop

#frontend

Проблема, с которой мы недавно столкнулись — это redux в зависимостях у react-beautiful-dnd. Есть какое-то внутреннее сопротивление тому факту, что кто-то втягивает целый стейт-менеджер в библиотеку, которая должна давать возможность перетащить карточку из одной колонки в другую. Разумеется, я несколько утрирую ситуацию, но в данном случае решение кажется необоснованным.

В моём понимании, библиотека для drag-n-drop, в целом, не нуждается в том, чтобы иметь свой собственный стетйт-менеджер, возможностей локального стейта компоненты для такого решения более, чем достаточно.

В случае, если стор всё-таки требуется, я предлагаю следующее решение: "стор должен поступать внешней зависимостью, согласно типизированного библиотекой интерфейса". То есть, сделать возможность передать стор внутрь библиотеки, создав кастомную обёртку, подходящую для конкретной библиотеки. Понимаю, что от этого сложность может возрасти, но почти наверняка такой подход позволит уменьшить итоговый бандл, хотя и увеличит время и сложность разработки. С другой стороны, это решение разработанное инженерами Atlassian, вряд ли они недостаточно компетентны, хотя по работе в Jira этого иногда и не скажешь.

Возможно, если у меня дойдут руки, то мы соберём отдельное собственное решение для drag-n-drop. В случае, если это произойдёт — я обязательно поделюсь ссылкой на решение.

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

Читать полностью…

@davidobryakov

👩‍💻 Нативные модалки

#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 позволяет отрисовать затемнение общего фона страницы, чтобы отделить модальное окно от всего остального интерфейса. Стилизация самого элемента и формы не так интересна, её можно пропустить.

Дальше по коду идёт кнопка, с обработчиком onclick, в котором обозначен вызов метода 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>


Таким образом, мы получаем полнофункциональное модальное окно, которое можно и нужно использовать вместо самописных велосипедов. Доступность у такого элемента точно будет выше, чем у написанного руками. Бывают случаи, когда фремйворки соблюдают рекомендации WAI ARIA, как например, Bootstrap. Но если есть возможность заменить ненативный элемент нативным, это всегда большой плюс.

Расширенная версия поста доступна у меня в блоге.

Читать полностью…

@davidobryakov

👩‍💻 Лучший менеджер зависимостей - это docker

#docker #python

Именно такую фразу мне сказал старший товарищ при одной из бесед, но тогда я его не понял. Был ли он прав? Разбираемся с примерами.

Спустя несколько лет работы с docker, я пришёл к выводу, что не имеет смысла выбирать между pipenv, poetry или conda, поскольку их использование внутри docker является затруднительным и требует плодить костыли, вместо того чтобы просто установить зависимости через pip.

Основная проблема заключается в том, что внутри контейнера вам никакое виртуальное окружение не нужно, а значит - мы затягиваем лишнюю зависимость, которая увеличит время сборки, зато локальная разработка без докера становится удобнее и приятнее. Только я, хоть убей, не пойму - какой смысл разрабатывать локально приложения на python без докера. Пожалуй, есть только один небольшой минус - нужно вписать новую зависимость в requirements.txt вручную. Но и этого можно избежать, если вы просто зайдёте в терминал контейнера, установите зависимость там и сделаете pip freeze в нужный файлик (разумеется, при условии, что зависимости попадают в volume с приложением).

Полная версия у меня в блоге.

Читать полностью…

@davidobryakov

Спасибо, что были со мной в этом году!

Хотя и не сказать, что я достаточно часто делился с вами постами и видео, но обещаю, что в 2024м вас ждёт много интересного, планы, как всегда, наполеоновские.

В знак благодарности решил разыграть 5 годовых подписок на телеграм премиум среди вас, давно было интересно, как это вообще работает :)

Читать полностью…

@davidobryakov

Новое свойство 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


В данный момент Pull request с этим свойством уже принят в черновиках CSSWG на github.

Пример кода, который я накидал для демки:

<!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>

Читать полностью…

@davidobryakov

⌛️ Наводим порядок в жизни

На этот пост меня натолкнула статья с хабра, но здесь я хочу поделиться исключительно своим опытом. Я не буду рассказывать вам про различные популярные практики, не вижу смысла дублировать эту информацию. Хочу рассказать только о том, как я учился управлять своим временем, какие ошибки я совершал, и к чему это в итоге привело.

Впервые задумываться о тайм-менеджменте я начал на втором-третьем курсе университета, когда количество моих активностей перестало умещаться в голове. Не могу сказать, что я когда-либо гнался за невероятной продуктивностью (ну, да, конечно), но меня довольно часто спрашивали как мне удаётся все успевать. И только недавно я начал быть до конца честным с собой и говорить: "никак" (отчасти, именно изменение моего отношения к этому и стало мотивом для переосмысления и работы в этом направлении).

Но вернёмся на 4 года назад, тогда я выработал для себя ряд правил и очень много сил уделил самодисциплине. Основными правилами для меня были:

- публичность (хотя бы для аудитории из 5-7 своих друзей, к слову, так и начинался этот канал);
- наличие чёткого плана на день и неделю;
- строго отведённое время на работу над каким-то делом (доходить могло вплоть до того, что я мог оставлять задание на лабораторную не до конца решенным, оставляя его на следующий день);
- строго отведённое время на отдых (в это время я мог почитать, погулять, да и просто непродуктивно посидеть в интернете).

В то время благодаря этим правилам, мне удалось закрыть все предметы к середине семестра, перевестись на бюджет и довольно сильно развиться в профессиональном плане. Но хватило меня где-то на полгода жизни в таком темпе (что неудивительно), после чего я подустал и долго не мог вернуться к нормальному тайм-менеджменту. Это, на самом деле, довольно частая проблема и в какой-то момент одной только мотивации становится недостаточно, нужна дисциплина — регулярность и постоянство.

Чтобы не повторять прошлых ошибок, я решил, что не хочу вести слишком подробные списки дел, фиксировать все планы в календаре и стараться успевать всё на свете. Начал с малого:

- составление примерного плана на неделю каждое воскресенье (с оценкой пунктов по времени);
- фиксирование всех обязательных еженедельных мероприятий в календаре (например, поход в зал).

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

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

- режим сна и бодрствования (ложусь до полуночи, встаю в 7:15);
- обязательная суставная разминка каждое утро (очень хорошо помогает проснуться);
- раннее начало рабочего дня (8:30-9:00).

Таким образом, мне удалось добиться желанной спокойной работы с утра, вплоть до начала первых созвонов, что позволило раскидывать 80% задач в начале дня и оставшиеся 20% в течение дня. Также, мой рабочий день перестал растягиваться и самое позднее, когда я завершаю работу при текущем графике — 19:00 (это не значит, что я работаю более 10 часов в день, ведь у меня, как и у всех могут быть перерывы на обед/ужин/небольшую прогулку). Кроме прочего, суставная разминка оказалась очень полезна не только для того, чтобы проснуться, но и чтобы мышцы быстрее приходили в себя после тяжёлых тренировок. Великой радостью стало и отсутствие какой-либо работы на выходных, что позволяет мне сейчас сидеть и писать этот пост, а не пытаться пофиксить очередной баг в проде :)

💬 Делитесь своим опытом в комментариях, будет интересно почитать

Читать полностью…

@davidobryakov

👩‍💻 Полезные плагины для разработчика в 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 упоминать в подборке большого смысла не вижу, потому что это довольно очевидные вещи.

💬 Пишите в комментариях, какими плагинами пользуетесь вы, будет интересно почитать :)

Читать полностью…

@davidobryakov

📂 Друзья, мне тут принесли общую папку каналов про ИТ. Собрано много интересных тем, чтобы держать руку на пульсе: тематические мероприятия, аналитика, QA, нейронные сети, а также авторские каналы про мир больших данных и разработки.

😎 Добавляйте себе папку и будьте в курсе всего самого интересного и необычного в ИТ-индустрии!


❗️ Внимание: чтобы все работало, важно установить последнюю версию Telegram

Читать полностью…

@davidobryakov

👩‍💻 Работа с 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

Читать полностью…

@davidobryakov

👩‍💻 Компонент для номера телефона

#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'
}
]

Читать полностью…

@davidobryakov

👩‍💻 Браузерные ОС

#браузеры #linux #wasm

На одной из прошлых работ гендиректор сказал мне, что считает, что операционные системы со временем перетекут в браузер, в качестве аргумента он упомянул Electron, который является базой для большого числа современных десктоп-приложений. Я запомнил это, но не придавал значения его словам, хотя они с каждым днём всё ближе к реальности.

Пару лет назад я писал про WebVM, который представляет из себя linux без графической оболочки, работающий через WASM. Пару недель назад видел несколько постов про InternetOS puter. По сути, она представляет из себя графическую оболочку рабочего стола, работающую на веб-технологиях (js + jQuery).

В отличие от WebVM, puter это только GUI, операции происходят на сервере (заметно, что на каждое действие, отправляется запрос в API). Документации для API-сервера пока что нет. Его исходного кода мне также не удалось найти (допускаю, что плохо искал, хотя и заглянул в каждый репозиторий). Разработчики обещают опубликовать документацию по API-серверу к концу марта, чего я буду ждать с нетерпением.

Как вы думаете, есть ли будущее у браузерных операционных систем? Смогут ли они со временем стать заменой привычным десктопным оболочкам?

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

Читать полностью…

@davidobryakov

Маска для номера телефона

#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>


С таким решением мы будем строго принимать один обозначенный формат (пример сильно упрощён, более комплексный вариант регулярного выражения можно подглядеть тут) с общей подсказкой для пользователя, без маски для ввода. Как давно вы в последний раз видели поле ввода для номера телефона без маски?

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

Вернёмся к основной теме. Без добавления JS-кода маску мы не получим. Можно поискать готовое решение, но с этим тоже есть проблема. Решения, которые сразу будут хорошо работать из коробки найти сложно. Довольно часто, многие из этих решений уже содержат в себе стилизацию, которую не всегда легко переделать под нужный дизайн.

На мой взгляд, всё, что должно быть в хорошем решении подобной задачи:

1) возможность управления паттерном форматирования, валидации;
2) отсутствие лишних зависимостей и любой стилизации, она должна поступать извне, либо легко кастомизироваться;
3) интернационализация.

Почти идеальным решением, на мой взгляд, является imask. Он предоставляет только функциональную часть с очень простыми API. К примеру, для российского номера телефона код будет выглядеть так:

IMask(
document.getElementById('phone-mask'),
{
mask: '+{7}(000)000-00-00'
}
)


Кроме прочего, решение имеет адаптацию под популярные веб-фреймворки (react, vue, angular, svelte) и даже неплохой задел на интернационализацию за счёт использования динамических масок. Пожалуй, единственным минусом этого решения является необходимость написания большого количества кода для более тонкой настройки. С другой стороны, это даёт возможность широкой кастомизации и позволяет менять паттерны и маски так, как вам требуется, не отнимая возможности стилизовать свои поля ввода так, как вы считаете нужным.

Читать полностью…

@davidobryakov

👩‍💻 Вычисляем дату с учётом рабочих дней на 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)


На том же сайте было упоминание библиотеки holidays, чтобы проставить сразу корректный список всех праздников, относительно определённой страны. Принцип работы простой:

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


К сожалению, нельзя сказать, что это решение покрывает все кейсы. К примеру, в 2024м году майские праздники в России по производственному календарю продлятся с 9 по 12 мая. Библиотека же, считает не так:

>>> date(2024, 5, 9) in ru_holidays
True
>>> date(2024, 5, 10) in ru_holidays
False


В этом примере, 9 мая ещё входит в список праздников, а вот 10 мая уже нет. Возможно, что numpy будет определять корректно?

>>> 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 выходных.

Ищем открытое API

Следующее, на что я наткнулся, было открытое API производственного календаря для России и Казахстана. Работа с ним максимально простая. К примеру, чтобы получить список всех "отклонений" от обычного календаря для России, достаточно выполнить GET-запрос по адресу: https://production-calendar.ru/get/ru/2024/json?compact=1. Результат полностью совпадает с производственным календарём на сайте КонсультантПлюс.

Мастерим решение

С учётом всех вводных реализуем решение (полная версия с его разбором у меня в блоге):

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)


Результат работы этой функции для сегодняшней даты (04.03.2024): 2024-03-12. Соответственно, функция работает корректно.

Заключение

Решение подобных базовых задач очень позитивно складывается на опыте начинающих и продолжающих разработчиков.

Полная версия с разбором решения доступна у меня в блоге.

💬 Поделитесь, сталкивались ли вы с подобными задачами? Какое решение в итоге находили?

Читать полностью…

@davidobryakov

👩‍💻 Стор или глобальные реактивные переменные во 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


В комментариях в качестве основных плюсов использования Pinia вместо глобальных реактивных переменных, приводили аргументы только про возможность использования SSR и DevTools.

Эта дискуссия действительно заставила меня задуматься, в следующем своём проекте на Vue, хочу попытаться применить подобную концепцию на практике.

P. S.

Оказалось, автор является частью русскоязычного сообщества @vuejs_ru и поддерживает и развивает ресурс Vue FAQ. Я бегло по нему прошёлся и не могу не выразить искреннее восхищение проделанной работой. Прошу обратить своё внимание на этот ресурс, в нём собрано довольно много информации по фреймворку.

Читать полностью…

@davidobryakov

👩‍💻 Оптимизация запросов в 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

Также, стоит упомянуть и о существовании объекта 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)
)


Полная версия с примерами доступна у меня в блоге.

📖 Источники:

1) Статья с хабра, в которой всё доходчиво объясняют: https://habr.com/ru/articles/752574/
2) Документация Django: https://docs.djangoproject.com/en/5.0/ref/models/querysets/

Читать полностью…

@davidobryakov

👩‍💻 Firefox использует chrome под капотом?

#firefox #chrome #браузеры

К такой мысли можно случайно прийти, если открыть логотип браузера, который выводится на странице открытия новой вкладки, поскольку он открывается по адресу:

chrome://branding/content/about-logo.png


Пользователь reddit решил пойти дальше простых и очевидных догадок и обратился к сообществу с вопросом.

Самый популярный комментарий с ответом:

Chrome refers to "user interface chrome", which is the borders and widgets that frame the content part of a browser window.

Google Chrome (the web browser) is named after this element of GUI.


Перевожу на русский:

Chrome, в данном случае, относится к термину "хром пользовательского интерфейса", который представляет собой рамки и виджеты, находящиеся вокруг контентной части окна браузера.

Браузер Google Chrome был назван так позже этого элемента пользовательского интерфейса.


Если спускаться по ветке комментариев ниже, можно также узнать, что термин появился задолго до первых браузеров. Дальнейшее исследование темы привело меня на stackoverflow (кто бы сомневался).

В ответе говорится следующее:

It is a euphemism for the graphical framework and elements surrounding the content, and thus means different things depending on the context.

То есть:

Это эвфемизм для графического фреймворка и элементов, находящихся вокруг контента, значение термина может разниться, поскольку зависит от контекста.

Читать полностью…

@davidobryakov

"был в сети 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' });


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

Читать полностью…

@davidobryakov

Есть ощущение, что раньше я никогда публично не подводил итоги года.

Не скажу, что в этом году появилось какое-то жгучее желание, но вот поделиться тем, что мы вместе сделали за год — считаю вполне можно.

Из важных наблюдений — в мою жизнь потихоньку вернулась систематизация к концу года. Максимализма поубавилось — редко нынче возникает желание сидеть и каждый день выдавать по посту среднего или низкого качества, но в данный момент уже на январь у меня готовится много интересного (и хочется верить, что тот жизненный опыт, который я получал на работе, пока не писал посты станет неплохим подспорьем).

Обещаю в 2024м перестать пропадать из публичного пространства и делиться с вами опытом, наблюдениями и интересными новостями значительно чаще, чем удавалось в 2023м.

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

Планы на 2023й у меня были довольно большие, но не могу сказать, что все из них по итогам были выполнены. Впрочем, это не главное. Могу сказать, наверное, что с головой уходил в работу, отчего времени и сил не хватало на всё остальное.

Обещаю себе, что с понедельника всё изменится. Удачно совпало начало года с началом недели. Тут точно у каждого любителя начать новую жизнь в понедельник — не получится отвертеться, как и у меня, надеюсь :)

Ну а кроме циферок активности в телеграме и на ютубе у меня есть и личные итоги, 2023 мне запомнился потому, что:

- я впервые встал на сноуборд;
- я снова начал играть в настольный теннис;
- я постарался вернуть дисциплину в свою жизнь, и это уже приносит свои плоды;
- я попробовал играть в разные видеоигры (в детстве я играл в них очень редко, а после 18 лет у меня и вовсе не было времени, поэтому даже в довольно старых играх для меня много откровений);
- я сделал не только самый провальный проект на работе, но и внёс довольно значимый вклад в самый успешный проект (когда-нибудь позже расскажу).

А каким для вас был этот год?

Всех с наступающим! 🎉

Читать полностью…

@davidobryakov

Практические советы для начинающих 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, вам следует завести их ключи свойствами класса модели, либо вынести их в отдельный класс, чтобы обращаться к константе, а не указывать конкретное значение.

Читать полностью…

@davidobryakov

🕹 Подборка игр для изучения фронтенд-разработки

#подборка #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, здесь вы учитесь управляться с экземплярами классов, вызывать их методы, чтобы всячески управлять роботом на экране, ведя его к цели каждого из представленных уровней.

💬 Есть что добавить? Поделись в комментариях :)

Читать полностью…

@davidobryakov

👩‍💻 Протоколы в python

#python #типизация

Листая хабр на предмет интересных статеек наткнулся на текст про протоколы в python. Эта заметка в меру корявенькая и некоторые вопросы рассматривает не совсем верно (а какие-то нюансы и вовсе не затрагивает), но спасибо ей, что побудила меня сесть и поразбираться в том, как это работает в действительности. Более удачная статейка с хабра по теме, если кому интересно.

Я разобрался в том, как работают протоколы и постарался сделать краткую выжимку из спецификации, обратив внимание на самые важные моменты (на мой взгляд, конечно).

📖 Читать на Teletype: https://blog.kantegory.me/python-protocols

💬 Буду рад, если поделитесь мнением в комментариях :)

Читать полностью…

@davidobryakov

🤔 Фраза дня: "Функциональные требования"

Вышло видео с моей лекцией для ДПО про оценку качества реализации программного средства. Рассказываю про характеристики из различных стандартов и стараюсь вывести из них нечто более близкое к привычной разработке посредством различных сочетаний этих самых характеристик.

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

На видео читаю эту лекцию уже в третий раз, но впервые в жизни трансляция шла из студии, так что строго прошу не судить :)

Смотреть на YouTube

Читать полностью…

@davidobryakov

👩‍💻 Контейнеры в Firefox

Я пользуюсь Firefox уже более 3х лет. И он всё это время продолжает меня радовать. Уже больше полутора лет хочу рассказать вам об одной функции, которая кажется мне довольно удобной. Это контейнеры. С их помощью можно не только разделить своё взаимодействие с браузером на разные профили (работа, учёба, развлечения), но и забыть про режим "Инкогнито" в разработке, поскольку можно поддерживать сразу несколько активных сессий на разрабатываемом сайте, при этом не бегая между окнами, а всего лишь переключаясь между вкладками.

Контейнеры помогают мне практически ежедневно, потому что мультиаккаунтинг, встроенный в браузер — это незаменимая вещь. В случае же, если вам необходимо подменять User Agent, вы также можете задавать их внутри контейнеров и использованием плагина "User Agent Switcher and Manager". Не сказать, что это часто бывает полезно, ведь спецификация User Agent потихоньку уходит в прошлое, но иногда это может помочь при тестировании.

Если вам интересно узнать об этой функции больше, то можете почитать официальный гайд от Mozilla.

Знали про эту функцию? Находите ли вы её полезной? Пишите в комментариях 👇

Читать полностью…

@davidobryakov

👩‍💻 Микросервисы — Бэкенд-разработка на 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

Читать полностью…
Subscribe to a channel