[{"data":1,"prerenderedAt":14579},["ShallowReactive",2],{"navigation_docs":3,"-python-fastapi-typing-pydantic":3379,"-python-fastapi-typing-pydantic-surround":14574},[4,1707,1912,2366,2547,2649,2856,2978,3028,3085,3119,3245,3322,3375],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"Робота з файлами: C-стиль (stdio.h)","\u002Fcpp\u002Fc-style-files","02.cpp\u002F47.c-style-files",{"title":1905,"path":1906,"stem":1907},"Робота з файлами: C++-стиль (fstream)","\u002Fcpp\u002Fcpp-style-files","02.cpp\u002F48.cpp-style-files",{"title":1909,"path":1910,"stem":1911},"План навчання: Курс C++ — Продовження (Статті 29–60+)","\u002Fcpp\u002Fcurriculum-plan","02.cpp\u002Fcurriculum-plan",{"title":1913,"icon":1914,"path":1915,"stem":1916,"children":1917,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[1918,1944,1998,2020,2324,2362],{"title":1919,"icon":1920,"path":1921,"stem":1922,"children":1923,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[1924,1928,1932,1936,1940],{"title":1925,"path":1926,"stem":1927},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":1929,"path":1930,"stem":1931},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":1933,"path":1934,"stem":1935},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":1937,"path":1938,"stem":1939},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":1941,"path":1942,"stem":1943},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":1945,"icon":1946,"path":1947,"stem":1948,"children":1949,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[1950,1954,1958,1962,1966,1970,1974,1978,1982,1986,1990,1994],{"title":1951,"path":1952,"stem":1953},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":1955,"path":1956,"stem":1957},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":1959,"path":1960,"stem":1961},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":1963,"path":1964,"stem":1965},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":1967,"path":1968,"stem":1969},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":1971,"path":1972,"stem":1973},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":1975,"path":1976,"stem":1977},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":1979,"path":1980,"stem":1981},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":1983,"path":1984,"stem":1985},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":1987,"path":1988,"stem":1989},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":1991,"path":1992,"stem":1993},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":1995,"path":1996,"stem":1997},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":1999,"icon":2000,"path":2001,"stem":2002,"children":2003,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[2004,2008,2012,2016],{"title":2005,"path":2006,"stem":2007},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2009,"path":2010,"stem":2011},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2013,"path":2014,"stem":2015},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2017,"path":2018,"stem":2019},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2021,"icon":2022,"path":2023,"stem":2024,"children":2025},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2026,2027,2031,2035,2039,2043,2106,2141,2293],{"title":2021,"path":2023,"stem":2024},{"title":2028,"path":2029,"stem":2030},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2032,"path":2033,"stem":2034},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2036,"path":2037,"stem":2038},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2040,"path":2041,"stem":2042},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2044,"icon":132,"path":2045,"stem":2046,"children":2047},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2048,2050,2054,2058,2062,2066,2070,2074,2078,2082,2086,2090,2094,2098,2102],{"title":2049,"path":2045,"stem":2046},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2051,"path":2052,"stem":2053},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2055,"path":2056,"stem":2057},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2059,"path":2060,"stem":2061},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2063,"path":2064,"stem":2065},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2067,"path":2068,"stem":2069},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2071,"path":2072,"stem":2073},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2075,"path":2076,"stem":2077},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2079,"path":2080,"stem":2081},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2083,"path":2084,"stem":2085},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2087,"path":2088,"stem":2089},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2091,"path":2092,"stem":2093},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2095,"path":2096,"stem":2097},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2099,"path":2100,"stem":2101},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2103,"path":2104,"stem":2105},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2107,"icon":2022,"path":2108,"stem":2109,"children":2110},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2111,2113,2117,2121,2125,2129,2133,2137],{"title":2112,"path":2108,"stem":2109},"React Router: Навігаційна система сучасного вебу",{"title":2114,"path":2115,"stem":2116},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2118,"path":2119,"stem":2120},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2122,"path":2123,"stem":2124},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2126,"path":2127,"stem":2128},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2130,"path":2131,"stem":2132},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2134,"path":2135,"stem":2136},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2138,"path":2139,"stem":2140},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2142,"icon":132,"path":2143,"stem":2144,"children":2145},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2146,2148,2164,2193,2202,2223,2239,2268],{"title":2147,"path":2143,"stem":2144},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2149,"stem":2150,"children":2151,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2152,2156,2160],{"title":2153,"path":2154,"stem":2155},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2157,"path":2158,"stem":2159},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2161,"path":2162,"stem":2163},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2165,"icon":132,"path":2166,"stem":2167,"children":2168,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2169,2173,2177,2181,2185,2189],{"title":2170,"path":2171,"stem":2172},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2174,"path":2175,"stem":2176},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2178,"path":2179,"stem":2180},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2182,"path":2183,"stem":2184},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2186,"path":2187,"stem":2188},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2190,"path":2191,"stem":2192},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2194,"icon":132,"path":2195,"stem":2196,"children":2197,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2198],{"title":2199,"path":2200,"stem":2201},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2203,"icon":132,"path":2204,"stem":2205,"children":2206,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2207,2211,2215,2219],{"title":2208,"path":2209,"stem":2210},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2212,"path":2213,"stem":2214},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2216,"path":2217,"stem":2218},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2220,"path":2221,"stem":2222},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2224,"icon":92,"path":2225,"stem":2226,"children":2227,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2228,2232,2236],{"title":2229,"path":2230,"stem":2231},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2233,"path":2234,"stem":2235},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2083,"path":2237,"stem":2238},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2240,"icon":132,"path":2241,"stem":2242,"children":2243,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2244,2248,2252,2256,2260,2264],{"title":2245,"path":2246,"stem":2247},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2249,"path":2250,"stem":2251},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2253,"path":2254,"stem":2255},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2257,"path":2258,"stem":2259},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2261,"path":2262,"stem":2263},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2265,"path":2266,"stem":2267},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2269,"icon":132,"path":2270,"stem":2271,"children":2272,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2273,2277,2281,2285,2289],{"title":2274,"path":2275,"stem":2276},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2278,"path":2279,"stem":2280},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2282,"path":2283,"stem":2284},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2286,"path":2287,"stem":2288},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2290,"path":2291,"stem":2292},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2294,"icon":132,"path":2295,"stem":2296,"children":2297},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2298,2300,2304,2308,2312,2316,2320],{"title":2299,"path":2295,"stem":2296},"UI Бібліотеки в React",{"title":2301,"path":2302,"stem":2303},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2305,"path":2306,"stem":2307},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2309,"path":2310,"stem":2311},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2313,"path":2314,"stem":2315},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2317,"path":2318,"stem":2319},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2321,"path":2322,"stem":2323},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2325,"icon":2326,"path":2327,"stem":2328,"children":2329,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2330,2334,2338,2342,2346,2350,2354,2358],{"title":2331,"path":2332,"stem":2333},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2335,"path":2336,"stem":2337},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2339,"path":2340,"stem":2341},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2343,"path":2344,"stem":2345},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2347,"path":2348,"stem":2349},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2351,"path":2352,"stem":2353},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2355,"path":2356,"stem":2357},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2359,"path":2360,"stem":2361},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2363,"path":2364,"stem":2365},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2367,"icon":2368,"path":2369,"stem":2370,"children":2371,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2372,2375,2378,2382,2386,2390,2394],{"title":162,"path":2373,"stem":2374},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2376,"stem":2377},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2379,"path":2380,"stem":2381},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2383,"path":2384,"stem":2385},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2387,"path":2388,"stem":2389},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2391,"path":2392,"stem":2393},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2395,"path":2396,"stem":2397,"children":2398,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2399,2403,2407,2411,2415,2419,2423,2427,2431,2435,2439,2443,2447,2451,2455,2459,2463,2467,2471,2475,2479,2483,2487,2491,2495,2499,2503,2507,2511,2515,2519,2523,2527,2531,2535,2539,2543],{"title":2400,"path":2401,"stem":2402},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2404,"path":2405,"stem":2406},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2408,"path":2409,"stem":2410},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2412,"path":2413,"stem":2414},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2416,"path":2417,"stem":2418},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2420,"path":2421,"stem":2422},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2424,"path":2425,"stem":2426},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2428,"path":2429,"stem":2430},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2432,"path":2433,"stem":2434},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2436,"path":2437,"stem":2438},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2440,"path":2441,"stem":2442},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2444,"path":2445,"stem":2446},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2448,"path":2449,"stem":2450},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2452,"path":2453,"stem":2454},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2456,"path":2457,"stem":2458},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2460,"path":2461,"stem":2462},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2464,"path":2465,"stem":2466},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2468,"path":2469,"stem":2470},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2472,"path":2473,"stem":2474},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2476,"path":2477,"stem":2478},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2480,"path":2481,"stem":2482},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2484,"path":2485,"stem":2486},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2488,"path":2489,"stem":2490},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2492,"path":2493,"stem":2494},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2496,"path":2497,"stem":2498},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2500,"path":2501,"stem":2502},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2504,"path":2505,"stem":2506},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2508,"path":2509,"stem":2510},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2512,"path":2513,"stem":2514},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2516,"path":2517,"stem":2518},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2520,"path":2521,"stem":2522},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2524,"path":2525,"stem":2526},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2528,"path":2529,"stem":2530},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2532,"path":2533,"stem":2534},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2536,"path":2537,"stem":2538},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2540,"path":2541,"stem":2542},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2544,"path":2545,"stem":2546},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2548,"icon":2549,"path":2550,"stem":2551,"children":2552,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2553,2557,2560,2564,2568,2572,2576,2580,2584,2588,2592,2596,2600,2604,2608,2645],{"title":2554,"path":2555,"stem":2556},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2558,"stem":2559},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2561,"path":2562,"stem":2563},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2565,"path":2566,"stem":2567},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2569,"path":2570,"stem":2571},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2573,"path":2574,"stem":2575},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2577,"path":2578,"stem":2579},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2581,"path":2582,"stem":2583},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2585,"path":2586,"stem":2587},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2589,"path":2590,"stem":2591},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2593,"path":2594,"stem":2595},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2597,"path":2598,"stem":2599},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2601,"path":2602,"stem":2603},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2605,"path":2606,"stem":2607},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2609,"icon":92,"path":2610,"stem":2611,"children":2612,"page":59},"FastAPI","\u002Fpython\u002Ffastapi","05.python\u002Ffastapi",[2613,2617,2621,2625,2629,2633,2637,2641],{"title":2614,"path":2615,"stem":2616},"Глибокий Typing та Pydantic v2 — від анотацій до валідації","\u002Fpython\u002Ffastapi\u002Ftyping-pydantic","05.python\u002Ffastapi\u002F15.typing-pydantic",{"title":2618,"path":2619,"stem":2620},"WSGI, ASGI та Python Web-екосистема","\u002Fpython\u002Ffastapi\u002Fwsgi-asgi-ecosystem","05.python\u002Ffastapi\u002F16.wsgi-asgi-ecosystem",{"title":2622,"path":2623,"stem":2624},"FastAPI: Перший додаток, Uvicorn та OpenAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-intro","05.python\u002Ffastapi\u002F17.fastapi-intro",{"title":2626,"path":2627,"stem":2628},"Маршрутизація, параметри запитів та APIRouter","\u002Fpython\u002Ffastapi\u002Ffastapi-routing-params","05.python\u002Ffastapi\u002F18.fastapi-routing-params",{"title":2630,"path":2631,"stem":2632},"Pydantic v2 у FastAPI — схеми, валідація та серіалізація","\u002Fpython\u002Ffastapi\u002Ffastapi-pydantic-schemas","05.python\u002Ffastapi\u002F19.fastapi-pydantic-schemas",{"title":2634,"path":2635,"stem":2636},"Dependency Injection — серце архітектури FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-dependency-injection","05.python\u002Ffastapi\u002F20.fastapi-dependency-injection",{"title":2638,"path":2639,"stem":2640},"Обробка помилок, Middleware та CORS у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-errors-middleware","05.python\u002Ffastapi\u002F21.fastapi-errors-middleware",{"title":2642,"path":2643,"stem":2644},"SQLAlchemy 2.0 — ORM, Core та Async Engine","\u002Fpython\u002Ffastapi\u002Fsqlalchemy-orm","05.python\u002Ffastapi\u002F22.sqlalchemy-orm",{"title":2646,"path":2647,"stem":2648},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2650,"icon":2651,"path":2652,"stem":2653,"children":2654,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2655,2685,2708,2745,2774,2792,2826,2838,2847],{"title":2656,"icon":2657,"path":2658,"stem":2659,"children":2660,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2661,2665,2669,2673,2677,2681],{"title":2662,"path":2663,"stem":2664},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2666,"path":2667,"stem":2668},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2670,"path":2671,"stem":2672},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2674,"path":2675,"stem":2676},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2678,"path":2679,"stem":2680},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2682,"path":2683,"stem":2684},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2686,"icon":2651,"path":2687,"stem":2688,"children":2689,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2690,2694,2700,2704],{"title":2691,"path":2692,"stem":2693},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2695,"path":2696,"stem":2697,"children":2698},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2699],{"title":2695,"path":2696,"stem":2697},{"title":2701,"path":2702,"stem":2703},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2705,"path":2706,"stem":2707},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2709,"icon":2651,"path":2710,"stem":2711,"children":2712,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2713,2717,2721,2725,2729,2733,2737,2741],{"title":2714,"path":2715,"stem":2716},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2718,"path":2719,"stem":2720},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2722,"path":2723,"stem":2724},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2726,"path":2727,"stem":2728},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2730,"path":2731,"stem":2732},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2734,"path":2735,"stem":2736},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2738,"path":2739,"stem":2740},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2742,"path":2743,"stem":2744},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2746,"icon":2651,"path":2747,"stem":2748,"children":2749,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2750,2754,2758,2762,2766,2770],{"title":2751,"path":2752,"stem":2753},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2755,"path":2756,"stem":2757},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2759,"path":2760,"stem":2761},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2763,"path":2764,"stem":2765},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2767,"path":2768,"stem":2769},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2771,"path":2772,"stem":2773},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2775,"icon":2776,"path":2777,"stem":2778,"children":2779,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2780,2784,2788],{"title":2781,"path":2782,"stem":2783},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2785,"path":2786,"stem":2787},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2789,"path":2790,"stem":2791},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2793,"icon":2794,"path":2795,"stem":2796,"children":2797,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2798,2802,2806,2810,2814,2818,2822],{"title":2799,"path":2800,"stem":2801},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2803,"path":2804,"stem":2805},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2807,"path":2808,"stem":2809},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2811,"path":2812,"stem":2813},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2815,"path":2816,"stem":2817},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2819,"path":2820,"stem":2821},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2823,"path":2824,"stem":2825},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2823,"icon":793,"path":2827,"stem":2828,"children":2829,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[2830,2834],{"title":2831,"path":2832,"stem":2833},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":2835,"path":2836,"stem":2837},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":2839,"icon":2840,"path":2841,"stem":2842,"children":2843,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[2844],{"title":2839,"path":2845,"stem":2846},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":2848,"icon":2849,"path":2850,"stem":2851,"children":2852,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[2853],{"title":2848,"path":2854,"stem":2855},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":2857,"icon":2858,"path":2859,"stem":2860,"children":2861,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[2862,2938],{"title":2863,"icon":2864,"path":2865,"stem":2866,"children":2867},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[2868,2870,2874,2878,2882,2886,2890,2894,2898,2902,2906,2910,2914,2918,2922,2926,2930,2934],{"title":2869,"path":2865,"stem":2866},"Docker: від нуля до production",{"title":2871,"path":2872,"stem":2873},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":2875,"path":2876,"stem":2877},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":2879,"path":2880,"stem":2881},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":2883,"path":2884,"stem":2885},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":2887,"path":2888,"stem":2889},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":2891,"path":2892,"stem":2893},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":2895,"path":2896,"stem":2897},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":2899,"path":2900,"stem":2901},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":2903,"path":2904,"stem":2905},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":2907,"path":2908,"stem":2909},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":2911,"path":2912,"stem":2913},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":2915,"path":2916,"stem":2917},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":2919,"path":2920,"stem":2921},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":2923,"path":2924,"stem":2925},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":2927,"path":2928,"stem":2929},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":2931,"path":2932,"stem":2933},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":2935,"path":2936,"stem":2937},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":2939,"icon":2940,"path":2941,"stem":2942,"children":2943},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[2944,2946,2950,2954,2958,2962,2966,2970,2974],{"title":2945,"path":2941,"stem":2942},"Kubernetes: від розробки до production",{"title":2947,"path":2948,"stem":2949},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":2951,"path":2952,"stem":2953},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":2955,"path":2956,"stem":2957},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":2959,"path":2960,"stem":2961},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":2963,"path":2964,"stem":2965},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":2967,"path":2968,"stem":2969},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":2971,"path":2972,"stem":2973},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":2975,"path":2976,"stem":2977},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":2979,"icon":2980,"path":2981,"stem":2982,"children":2983,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[2984,2988,2992,2996,3000,3004,3008,3012,3016,3020,3024],{"title":2985,"path":2986,"stem":2987},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":2989,"path":2990,"stem":2991},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":2993,"path":2994,"stem":2995},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":2997,"path":2998,"stem":2999},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":3001,"path":3002,"stem":3003},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":3005,"path":3006,"stem":3007},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":3009,"path":3010,"stem":3011},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":3013,"path":3014,"stem":3015},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":3017,"path":3018,"stem":3019},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":3021,"path":3022,"stem":3023},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":3025,"path":3026,"stem":3027},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":3029,"icon":943,"path":3030,"stem":3031,"children":3032,"page":59},"DDD","\u002Fddd","10.ddd",[3033,3037,3041,3045,3049,3053,3057,3061,3065,3069,3073,3077,3081],{"title":3034,"path":3035,"stem":3036},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":3038,"path":3039,"stem":3040},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3042,"path":3043,"stem":3044},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3046,"path":3047,"stem":3048},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3050,"path":3051,"stem":3052},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3054,"path":3055,"stem":3056},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3058,"path":3059,"stem":3060},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3062,"path":3063,"stem":3064},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3066,"path":3067,"stem":3068},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3070,"path":3071,"stem":3072},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3074,"path":3075,"stem":3076},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3078,"path":3079,"stem":3080},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3082,"path":3083,"stem":3084},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3086,"icon":3087,"path":3088,"stem":3089,"children":3090,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3091,3095,3099,3103,3107,3111,3115],{"title":3092,"path":3093,"stem":3094},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3096,"path":3097,"stem":3098},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3100,"path":3101,"stem":3102},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3104,"path":3105,"stem":3106},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3108,"path":3109,"stem":3110},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3112,"path":3113,"stem":3114},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3116,"path":3117,"stem":3118},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3120,"icon":3121,"path":3122,"stem":3123,"children":3124,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3125,3129,3133,3137,3141,3145,3149,3153,3157,3161,3165,3169,3173,3177,3181,3185,3189,3193,3197,3201,3205,3209,3213,3217,3221,3225,3229,3233,3237,3241],{"title":3126,"path":3127,"stem":3128},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3130,"path":3131,"stem":3132},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3134,"path":3135,"stem":3136},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3138,"path":3139,"stem":3140},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3142,"path":3143,"stem":3144},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3146,"path":3147,"stem":3148},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3150,"path":3151,"stem":3152},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3154,"path":3155,"stem":3156},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3158,"path":3159,"stem":3160},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3162,"path":3163,"stem":3164},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3166,"path":3167,"stem":3168},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3170,"path":3171,"stem":3172},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3174,"path":3175,"stem":3176},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3178,"path":3179,"stem":3180},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3182,"path":3183,"stem":3184},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3186,"path":3187,"stem":3188},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3190,"path":3191,"stem":3192},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3194,"path":3195,"stem":3196},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3198,"path":3199,"stem":3200},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3202,"path":3203,"stem":3204},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3206,"path":3207,"stem":3208},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3210,"path":3211,"stem":3212},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3214,"path":3215,"stem":3216},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3218,"path":3219,"stem":3220},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3222,"path":3223,"stem":3224},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3226,"path":3227,"stem":3228},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3230,"path":3231,"stem":3232},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3234,"path":3235,"stem":3236},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3238,"path":3239,"stem":3240},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3242,"path":3243,"stem":3244},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3246,"path":3247,"stem":3248,"children":3249,"page":59},"AWS","\u002Faws","13.aws",[3250,3254,3258,3262,3266,3270,3274,3278,3282,3286,3290,3294,3298,3302,3306,3310,3314,3318],{"title":3251,"path":3252,"stem":3253},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3255,"path":3256,"stem":3257},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3259,"path":3260,"stem":3261},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3263,"path":3264,"stem":3265},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3267,"path":3268,"stem":3269},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3271,"path":3272,"stem":3273},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3275,"path":3276,"stem":3277},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3279,"path":3280,"stem":3281},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3283,"path":3284,"stem":3285},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3287,"path":3288,"stem":3289},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3291,"path":3292,"stem":3293},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3295,"path":3296,"stem":3297},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3299,"path":3300,"stem":3301},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3303,"path":3304,"stem":3305},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3307,"path":3308,"stem":3309},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3311,"path":3312,"stem":3313},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3315,"path":3316,"stem":3317},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3319,"path":3320,"stem":3321},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3323,"path":3324,"stem":3325,"children":3326,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[3327,3331,3335,3339,3343,3347,3351,3355,3359,3363,3367,3371],{"title":3328,"path":3329,"stem":3330},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":3332,"path":3333,"stem":3334},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":3336,"path":3337,"stem":3338},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":3340,"path":3341,"stem":3342},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":3344,"path":3345,"stem":3346},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":3348,"path":3349,"stem":3350},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":3352,"path":3353,"stem":3354},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":3356,"path":3357,"stem":3358},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":3360,"path":3361,"stem":3362},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":3364,"path":3365,"stem":3366},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":3368,"path":3369,"stem":3370},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":3372,"path":3373,"stem":3374},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":3376,"path":3377,"stem":3378},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":3380,"title":2614,"body":3381,"description":14568,"extension":14569,"links":14570,"meta":14571,"navigation":3522,"path":2615,"seo":14572,"stem":2616,"__hash__":14573},"docs\u002F05.python\u002Ffastapi\u002F15.typing-pydantic.md",{"type":3382,"value":3383,"toc":14515},"minimark",[3384,3388,3393,3411,3419,3426,3448,3451,3454,3458,3473,3478,3485,3708,3731,3733,3737,3740,3745,3748,3830,3834,3849,4025,4027,4031,4042,4045,4120,4134,4144,4150,4163,4166,4199,4302,4304,4308,4322,4333,4611,4621,4623,4627,4630,4689,4693,4696,4761,4918,4922,4925,4970,5082,5086,5092,5098,5263,5265,5269,5276,5284,5293,5529,5560,5568,5577,5580,5818,5820,5828,5834,5847,5851,5854,5905,5912,5979,5983,5989,6051,6053,6066,6069,6079,6104,6381,6389,6392,6398,6613,6615,6622,6627,6640,6650,6760,6763,6778,6780,6784,6794,6798,6808,6815,6833,6840,6954,7160,7173,7175,7179,7186,7275,7282,7348,7355,7357,7363,7375,7381,7412,7414,7422,7429,7537,7544,8146,8241,8243,8247,8250,8257,8263,8307,8314,8333,8346,8380,8383,8606,8717,8719,8723,8728,8738,8741,8760,8763,9405,9503,9540,9550,9556,9572,9920,10009,10022,10024,10030,10039,10049,10156,10201,10203,10207,10213,10229,10678,10761,10763,10770,10776,10786,10941,11023,11025,11029,11036,11042,11073,11077,11087,11111,11278,11366,11368,11375,11386,11389,11603,11691,11712,11714,11721,11724,11733,11736,11765,11768,11975,12089,12110,12112,12116,12122,12128,12131,12140,12145,12152,12155,12157,12161,12164,12167,12195,12198,12488,12492,12526,12528,12532,12535,12538,12544,12546,12555,12558,12578,12583,12605,12610,12746,12748,12755,12766,12770,13150,13152,13158,13161,13165,14166,14168,14172,14175,14349,14353,14356,14362,14364,14368,14374,14378,14385,14411,14415,14418,14477,14481,14511],[3385,3386,2614],"h1",{"id":3387},"глибокий-typing-та-pydantic-v2-від-анотацій-до-валідації",[3389,3390,3392],"h2",{"id":3391},"вступ-парадигма-type-driven-development-у-python","Вступ: Парадигма Type-Driven Development у Python",[3394,3395,3396,3397,3401,3402,3406,3407,3410],"p",{},"Протягом тривалого часу Python сприймався виключно як мова з динамічною типізацією, де панував принцип «качиної типізації» (duck typing): ",[3398,3399,3400],"em",{},"\"якщо це ходить як качка і крякає як качка, то це, мабуть, качка\"",". Цей підхід забезпечував колосальну гнучкість та швидкість розробки на початкових етапах. Проте з ростом масштабів кодових баз, появою великих корпоративних систем та переходом до мікросервісної архітектури, динамічна типізація виявила свої критичні слабкості: відсутність автодоповнення в IDE, неможливість надійного статичного аналізу коду та виникнення прихованих runtime-помилок типу ",[3403,3404,3405],"code",{},"AttributeError"," чи ",[3403,3408,3409],{},"TypeError"," у найменш очікувані моменти.",[3394,3412,3413,3414,3418],{},"Для розробників, які переходять в екосистему Python із C# (ASP.NET Core), концепція динамічної типізації спочатку може здатися кроком назад. У C# типи є першокласними громадянами, які контролюються компілятором на етапі збирання проєкту (compile-time). Проте сучасний Python (починаючи з версії 3.5 і особливо в 3.10–3.12) пропонує потужний гібридний підхід: ",[3415,3416,3417],"strong",{},"статичний аналіз типів (через mypy\u002Fpyright) у поєднанні з валідацією даних під час виконання (runtime) за допомогою Pydantic",".",[3394,3420,3421,3422,3425],{},"Це породило нову парадигму — ",[3415,3423,3424],{},"Type-Driven Development (TDD у контексті типів)",". Замість того, щоб сприймати типи лише як документацію, сучасні Python-фреймворки, такі як FastAPI, використовують анотації типів як єдине джерело правди (Single Source of Truth) для:",[3427,3428,3429,3436,3442],"ol",{},[3430,3431,3432,3435],"li",{},[3415,3433,3434],{},"Валідації вхідних та вихідних даних"," (Request\u002FResponse validation).",[3430,3437,3438,3441],{},[3415,3439,3440],{},"Генерації документації"," OpenAPI (Swagger\u002FReDoc).",[3430,3443,3444,3447],{},[3415,3445,3446],{},"Dependency Injection"," (впровадження залежностей).",[3394,3449,3450],{},"Без розуміння того, як працює сучасна система типізації Python, неможливо осягнути «магію» FastAPI. Тому наш шлях до вебфреймворків починається не з роутингу чи HTTP-запитів, а з фундаментальних інструментів типізації та бібліотеки Pydantic v2.",[3452,3453],"hr",{},[3389,3455,3457],{"id":3456},"частина-i-глибоке-занурення-в-type-hints","Частина I: Глибоке занурення в Type Hints",[3394,3459,3460,3461,3464,3465,3468,3469,3472],{},"Система типізації в Python розвивалася поетапно. Важливо розуміти, що сам інтерпретатор Python (CPython) ігнорує анотації типів під час виконання коду — для нього вони залишаються метаданими, доступними через атрибут ",[3403,3462,3463],{},"__annotations__",". Всю роботу з перевірки коректності типів виконують статичні аналізатори (лінтери), такі як ",[3415,3466,3467],{},"mypy",", ",[3415,3470,3471],{},"pyright"," або вбудовані інструменти IDE (PyCharm, VS Code).",[3474,3475,3477],"h3",{"id":3476},"еволюція-системи-типізації-від-pep-484-до-pep-695","Еволюція системи типізації: від PEP 484 до PEP 695",[3394,3479,3480,3481,3484],{},"Давайте простежимо еволюцію анотацій на прикладі визначення типу для функції, яка приймає ідентифікатор користувача (який може бути як цілим числом, так і рядком) та повертає список його ролей або значення ",[3403,3482,3483],{},"None",", якщо користувача не знайдено.",[3486,3487,3488,3581,3639],"code-group",{},[3489,3490,3496],"pre",{"className":3491,"code":3492,"filename":3493,"language":3494,"meta":3495,"style":3495},"language-python shiki shiki-themes light-plus dark-plus dark-plus","from typing import Union, List, Optional\n\n# PEP 484 вимагав використання спеціальних контейнерів з модуля typing\ndef get_user_roles(user_id: Union[int, str]) -> Optional[List[str]]:\n    # Логіка отримання ролей\n    pass\n","Python 3.5 - 3.8 (PEP 484)","python","",[3403,3497,3498,3517,3524,3531,3569,3575],{"__ignoreMap":3495},[3499,3500,3503,3507,3511,3514],"span",{"class":3501,"line":3502},"line",1,[3499,3504,3506],{"class":3505},"s8xlr","from",[3499,3508,3510],{"class":3509},"sHH4Y"," typing ",[3499,3512,3513],{"class":3505},"import",[3499,3515,3516],{"class":3509}," Union, List, Optional\n",[3499,3518,3520],{"class":3501,"line":3519},2,[3499,3521,3523],{"emptyLinePlaceholder":3522},true,"\n",[3499,3525,3527],{"class":3501,"line":3526},3,[3499,3528,3530],{"class":3529},"spJ8K","# PEP 484 вимагав використання спеціальних контейнерів з модуля typing\n",[3499,3532,3534,3538,3542,3545,3549,3552,3556,3558,3561,3564,3566],{"class":3501,"line":3533},4,[3499,3535,3537],{"class":3536},"su1O8","def",[3499,3539,3541],{"class":3540},"s8Opu"," get_user_roles",[3499,3543,3544],{"class":3509},"(",[3499,3546,3548],{"class":3547},"siwwj","user_id",[3499,3550,3551],{"class":3509},": Union[",[3499,3553,3555],{"class":3554},"sN1BT","int",[3499,3557,3468],{"class":3509},[3499,3559,3560],{"class":3554},"str",[3499,3562,3563],{"class":3509},"]) -> Optional[List[",[3499,3565,3560],{"class":3554},[3499,3567,3568],{"class":3509},"]]:\n",[3499,3570,3572],{"class":3501,"line":3571},5,[3499,3573,3574],{"class":3529},"    # Логіка отримання ролей\n",[3499,3576,3578],{"class":3501,"line":3577},6,[3499,3579,3580],{"class":3505},"    pass\n",[3489,3582,3585],{"className":3491,"code":3583,"filename":3584,"language":3494,"meta":3495,"style":3495},"# Починаючи з Python 3.9, вбудовані колекції (list, dict) підтримують generics.\n# Починаючи з Python 3.10 (PEP 604), введено оператор \"|\" для Union\u002FOptional.\ndef get_user_roles(user_id: int | str) -> list[str] | None:\n    # Більш чистий та читабельний синтаксис\n    pass\n","Python 3.9 - 3.10 (PEP 585 & PEP 604)",[3403,3586,3587,3592,3597,3630,3635],{"__ignoreMap":3495},[3499,3588,3589],{"class":3501,"line":3502},[3499,3590,3591],{"class":3529},"# Починаючи з Python 3.9, вбудовані колекції (list, dict) підтримують generics.\n",[3499,3593,3594],{"class":3501,"line":3519},[3499,3595,3596],{"class":3529},"# Починаючи з Python 3.10 (PEP 604), введено оператор \"|\" для Union\u002FOptional.\n",[3499,3598,3599,3601,3603,3605,3607,3610,3612,3615,3617,3620,3622,3625,3627],{"class":3501,"line":3526},[3499,3600,3537],{"class":3536},[3499,3602,3541],{"class":3540},[3499,3604,3544],{"class":3509},[3499,3606,3548],{"class":3547},[3499,3608,3609],{"class":3509},": ",[3499,3611,3555],{"class":3554},[3499,3613,3614],{"class":3509}," | ",[3499,3616,3560],{"class":3554},[3499,3618,3619],{"class":3509},") -> list[",[3499,3621,3560],{"class":3554},[3499,3623,3624],{"class":3509},"] | ",[3499,3626,3483],{"class":3536},[3499,3628,3629],{"class":3509},":\n",[3499,3631,3632],{"class":3501,"line":3533},[3499,3633,3634],{"class":3529},"    # Більш чистий та читабельний синтаксис\n",[3499,3636,3637],{"class":3501,"line":3571},[3499,3638,3580],{"class":3505},[3489,3640,3643],{"className":3491,"code":3641,"filename":3642,"language":3494,"meta":3495,"style":3495},"# PEP 695 представив новий синтаксис для аліасів типів та generic-класів.\ntype UserId = int | str\ntype RoleList = list[str]\n\ndef get_user_roles(user_id: UserId) -> RoleList | None:\n    # Максимальна декларативність та простота підтримки\n    pass\n","Python 3.12 (PEP 695)",[3403,3644,3645,3650,3665,3677,3681,3698,3703],{"__ignoreMap":3495},[3499,3646,3647],{"class":3501,"line":3502},[3499,3648,3649],{"class":3529},"# PEP 695 представив новий синтаксис для аліасів типів та generic-класів.\n",[3499,3651,3652,3655,3658,3660,3662],{"class":3501,"line":3519},[3499,3653,3654],{"class":3554},"type",[3499,3656,3657],{"class":3509}," UserId = ",[3499,3659,3555],{"class":3554},[3499,3661,3614],{"class":3509},[3499,3663,3664],{"class":3554},"str\n",[3499,3666,3667,3669,3672,3674],{"class":3501,"line":3526},[3499,3668,3654],{"class":3554},[3499,3670,3671],{"class":3509}," RoleList = list[",[3499,3673,3560],{"class":3554},[3499,3675,3676],{"class":3509},"]\n",[3499,3678,3679],{"class":3501,"line":3533},[3499,3680,3523],{"emptyLinePlaceholder":3522},[3499,3682,3683,3685,3687,3689,3691,3694,3696],{"class":3501,"line":3571},[3499,3684,3537],{"class":3536},[3499,3686,3541],{"class":3540},[3499,3688,3544],{"class":3509},[3499,3690,3548],{"class":3547},[3499,3692,3693],{"class":3509},": UserId) -> RoleList | ",[3499,3695,3483],{"class":3536},[3499,3697,3629],{"class":3509},[3499,3699,3700],{"class":3501,"line":3577},[3499,3701,3702],{"class":3529},"    # Максимальна декларативність та простота підтримки\n",[3499,3704,3706],{"class":3501,"line":3705},7,[3499,3707,3580],{"class":3505},[3709,3710,3711,3714,3715,3718,3719,3722,3723,3726,3727,3730],"note",{},[3415,3712,3713],{},"Чому це важливо для ASP.NET розробника:","\nУ C# аналогом ",[3403,3716,3717],{},"Union[int, str]"," (або ",[3403,3720,3721],{},"int | str",") є використання ",[3403,3724,3725],{},"oneof"," (у gRPC) або кастомних структур на кшталт ",[3403,3728,3729],{},"OneOf\u003CT0, T1>",". В Python підтримка union-типів є нативною на рівні синтаксису типізації, що робить моделювання гнучких API значно простішим.",[3452,3732],{},[3474,3734,3736],{"id":3735},"базові-правила-та-синтаксичні-конструкції","Базові правила та синтаксичні конструкції",[3394,3738,3739],{},"Перш ніж заглиблюватися в складні структури, розберемо фундаментальні правила синтаксису типізації в Python, які охоплюють змінні, функції та класи.",[3741,3742,3744],"h4",{"id":3743},"_1-анотація-змінних-та-констант","1. Анотація змінних та констант",[3394,3746,3747],{},"Анотація змінної визначається через двокрапку після її імені перед ініціалізацією:",[3489,3749,3752],{"className":3491,"code":3750,"filename":3751,"language":3494,"meta":3495,"style":3495},"# Анотація локальної змінної\ndb_port: int = 5432\n\n# Анотація без ініціалізації (змінна існує лише для лінтера, доки їй не присвоєно значення)\nconnection_string: str\n\n# Константні значення анотуються за допомогою Final (PEP 591)\nfrom typing import Final\nMAX_CONNECTIONS: Final[int] = 100\n# Спроба змінити MAX_CONNECTIONS призведе до помилки статичного аналізатора (але не runtime)\n","main.py",[3403,3753,3754,3759,3773,3777,3782,3789,3793,3798,3810,3824],{"__ignoreMap":3495},[3499,3755,3756],{"class":3501,"line":3502},[3499,3757,3758],{"class":3529},"# Анотація локальної змінної\n",[3499,3760,3761,3764,3766,3769],{"class":3501,"line":3519},[3499,3762,3763],{"class":3509},"db_port: ",[3499,3765,3555],{"class":3554},[3499,3767,3768],{"class":3509}," = ",[3499,3770,3772],{"class":3771},"sJj4R","5432\n",[3499,3774,3775],{"class":3501,"line":3526},[3499,3776,3523],{"emptyLinePlaceholder":3522},[3499,3778,3779],{"class":3501,"line":3533},[3499,3780,3781],{"class":3529},"# Анотація без ініціалізації (змінна існує лише для лінтера, доки їй не присвоєно значення)\n",[3499,3783,3784,3787],{"class":3501,"line":3571},[3499,3785,3786],{"class":3509},"connection_string: ",[3499,3788,3664],{"class":3554},[3499,3790,3791],{"class":3501,"line":3577},[3499,3792,3523],{"emptyLinePlaceholder":3522},[3499,3794,3795],{"class":3501,"line":3705},[3499,3796,3797],{"class":3529},"# Константні значення анотуються за допомогою Final (PEP 591)\n",[3499,3799,3801,3803,3805,3807],{"class":3501,"line":3800},8,[3499,3802,3506],{"class":3505},[3499,3804,3510],{"class":3509},[3499,3806,3513],{"class":3505},[3499,3808,3809],{"class":3509}," Final\n",[3499,3811,3813,3816,3818,3821],{"class":3501,"line":3812},9,[3499,3814,3815],{"class":3509},"MAX_CONNECTIONS: Final[",[3499,3817,3555],{"class":3554},[3499,3819,3820],{"class":3509},"] = ",[3499,3822,3823],{"class":3771},"100\n",[3499,3825,3827],{"class":3501,"line":3826},10,[3499,3828,3829],{"class":3529},"# Спроба змінити MAX_CONNECTIONS призведе до помилки статичного аналізатора (але не runtime)\n",[3741,3831,3833],{"id":3832},"_2-анотація-функцій-та-методів","2. Анотація функцій та методів",[3394,3835,3836,3837,3840,3841,3844,3845,3848],{},"У функціях анотуються як параметри, так і значення, що повертається. Особливу увагу варто приділити методам класів, де перший параметр (",[3403,3838,3839],{},"self"," або ",[3403,3842,3843],{},"cls",") зазвичай ",[3415,3846,3847],{},"не анотується",", оскільки його тип виводиться автоматично:",[3489,3850,3852],{"className":3491,"code":3851,"filename":3751,"language":3494,"meta":3495,"style":3495},"class DatabaseConnector:\n    def __init__(self, dsn: str) -> None:\n        self.dsn: str = dsn  # Анотація атрибута класу\n\n    def connect(self) -> bool:\n        # self не потребує анотування\n        return True\n\n    @classmethod\n    def from_env(cls, env_var: str) -> \"DatabaseConnector\":\n        # Використання рядка-літерала \"DatabaseConnector\" потрібне,\n        # якщо клас ще не повністю визначений у цьому місці файлу (Forward Reference)\n        import os\n        return cls(dsn=os.getenv(env_var, \"\"))\n",[3403,3853,3854,3864,3892,3908,3912,3930,3935,3943,3947,3955,3983,3989,3995,4004],{"__ignoreMap":3495},[3499,3855,3856,3859,3862],{"class":3501,"line":3502},[3499,3857,3858],{"class":3536},"class",[3499,3860,3861],{"class":3554}," DatabaseConnector",[3499,3863,3629],{"class":3509},[3499,3865,3866,3869,3872,3874,3876,3878,3881,3883,3885,3888,3890],{"class":3501,"line":3519},[3499,3867,3868],{"class":3536},"    def",[3499,3870,3871],{"class":3540}," __init__",[3499,3873,3544],{"class":3509},[3499,3875,3839],{"class":3547},[3499,3877,3468],{"class":3509},[3499,3879,3880],{"class":3547},"dsn",[3499,3882,3609],{"class":3509},[3499,3884,3560],{"class":3554},[3499,3886,3887],{"class":3509},") -> ",[3499,3889,3483],{"class":3536},[3499,3891,3629],{"class":3509},[3499,3893,3894,3897,3900,3902,3905],{"class":3501,"line":3526},[3499,3895,3896],{"class":3536},"        self",[3499,3898,3899],{"class":3509},".dsn: ",[3499,3901,3560],{"class":3554},[3499,3903,3904],{"class":3509}," = dsn  ",[3499,3906,3907],{"class":3529},"# Анотація атрибута класу\n",[3499,3909,3910],{"class":3501,"line":3533},[3499,3911,3523],{"emptyLinePlaceholder":3522},[3499,3913,3914,3916,3919,3921,3923,3925,3928],{"class":3501,"line":3571},[3499,3915,3868],{"class":3536},[3499,3917,3918],{"class":3540}," connect",[3499,3920,3544],{"class":3509},[3499,3922,3839],{"class":3547},[3499,3924,3887],{"class":3509},[3499,3926,3927],{"class":3554},"bool",[3499,3929,3629],{"class":3509},[3499,3931,3932],{"class":3501,"line":3577},[3499,3933,3934],{"class":3529},"        # self не потребує анотування\n",[3499,3936,3937,3940],{"class":3501,"line":3705},[3499,3938,3939],{"class":3505},"        return",[3499,3941,3942],{"class":3536}," True\n",[3499,3944,3945],{"class":3501,"line":3800},[3499,3946,3523],{"emptyLinePlaceholder":3522},[3499,3948,3949,3952],{"class":3501,"line":3812},[3499,3950,3951],{"class":3540},"    @",[3499,3953,3954],{"class":3554},"classmethod\n",[3499,3956,3957,3959,3962,3964,3966,3968,3971,3973,3975,3977,3981],{"class":3501,"line":3826},[3499,3958,3868],{"class":3536},[3499,3960,3961],{"class":3540}," from_env",[3499,3963,3544],{"class":3509},[3499,3965,3843],{"class":3547},[3499,3967,3468],{"class":3509},[3499,3969,3970],{"class":3547},"env_var",[3499,3972,3609],{"class":3509},[3499,3974,3560],{"class":3554},[3499,3976,3887],{"class":3509},[3499,3978,3980],{"class":3979},"sbdoH","\"DatabaseConnector\"",[3499,3982,3629],{"class":3509},[3499,3984,3986],{"class":3501,"line":3985},11,[3499,3987,3988],{"class":3529},"        # Використання рядка-літерала \"DatabaseConnector\" потрібне,\n",[3499,3990,3992],{"class":3501,"line":3991},12,[3499,3993,3994],{"class":3529},"        # якщо клас ще не повністю визначений у цьому місці файлу (Forward Reference)\n",[3499,3996,3998,4001],{"class":3501,"line":3997},13,[3499,3999,4000],{"class":3505},"        import",[3499,4002,4003],{"class":3509}," os\n",[3499,4005,4007,4009,4012,4014,4016,4019,4022],{"class":3501,"line":4006},14,[3499,4008,3939],{"class":3505},[3499,4010,4011],{"class":3536}," cls",[3499,4013,3544],{"class":3509},[3499,4015,3880],{"class":3547},[3499,4017,4018],{"class":3509},"=os.getenv(env_var, ",[3499,4020,4021],{"class":3979},"\"\"",[3499,4023,4024],{"class":3509},"))\n",[3452,4026],{},[3474,4028,4030],{"id":4029},"принцип-роботи-під-капотом-runtime-ігнорування-та-type-erasure","Принцип роботи під капотом: Runtime-ігнорування та Type Erasure",[3394,4032,4033,4034,4037,4038,4041],{},"Фундаментальне правило Python полягає в тому, що ",[3415,4035,4036],{},"анотації типів не впливають на виконання програми",". Інтерпретатор Python повністю ігнорує їх під час генерації байт-коду. Це явище схоже на ",[3398,4039,4040],{},"Type Erasure"," в Java або TypeScript, де типи існують лише на етапі розробки\u002Fтранспіляції.",[3394,4043,4044],{},"Спробуємо виконати наступний код:",[3489,4046,4048],{"className":3491,"code":4047,"filename":3751,"language":3494,"meta":3495,"style":3495},"def process_age(age: int) -> str:\n    return f\"Вік: {age * 2}\"\n\n# Передаємо рядок замість очікуваного int\nprint(process_age(\"25\"))\n",[3403,4049,4050,4072,4098,4102,4107],{"__ignoreMap":3495},[3499,4051,4052,4054,4057,4059,4062,4064,4066,4068,4070],{"class":3501,"line":3502},[3499,4053,3537],{"class":3536},[3499,4055,4056],{"class":3540}," process_age",[3499,4058,3544],{"class":3509},[3499,4060,4061],{"class":3547},"age",[3499,4063,3609],{"class":3509},[3499,4065,3555],{"class":3554},[3499,4067,3887],{"class":3509},[3499,4069,3560],{"class":3554},[3499,4071,3629],{"class":3509},[3499,4073,4074,4077,4080,4083,4086,4089,4092,4095],{"class":3501,"line":3519},[3499,4075,4076],{"class":3505},"    return",[3499,4078,4079],{"class":3536}," f",[3499,4081,4082],{"class":3979},"\"Вік: ",[3499,4084,4085],{"class":3536},"{",[3499,4087,4088],{"class":3509},"age * ",[3499,4090,4091],{"class":3771},"2",[3499,4093,4094],{"class":3536},"}",[3499,4096,4097],{"class":3979},"\"\n",[3499,4099,4100],{"class":3501,"line":3526},[3499,4101,3523],{"emptyLinePlaceholder":3522},[3499,4103,4104],{"class":3501,"line":3533},[3499,4105,4106],{"class":3529},"# Передаємо рядок замість очікуваного int\n",[3499,4108,4109,4112,4115,4118],{"class":3501,"line":3571},[3499,4110,4111],{"class":3540},"print",[3499,4113,4114],{"class":3509},"(process_age(",[3499,4116,4117],{"class":3979},"\"25\"",[3499,4119,4024],{"class":3509},[3394,4121,4122,4123,4126,4127,4130,4131,3418],{},"Код виконається без runtime-помилок типізації, але виведе ",[3403,4124,4125],{},"\"Вік: 2525\""," через особливості перевантаження оператора ",[3403,4128,4129],{},"*"," для рядків у Python. Статичний аналізатор (наприклад, mypy) на етапі перевірки підсвітить це як помилку:\n",[3403,4132,4133],{},"error: Argument 1 to \"process_age\" has incompatible type \"str\"; expected \"int\"",[3741,4135,4137,4138,4140,4141],{"id":4136},"метадані-__annotations__-та-get_type_hints","Метадані ",[3403,4139,3463],{}," та ",[3403,4142,4143],{},"get_type_hints()",[3394,4145,4146,4147,4149],{},"Хоча інтерпретатор ігнорує типи для перевірок, він зберігає їх у спеціальному словнику ",[3403,4148,3463],{}," об'єкта (класу чи функції). Це дозволяє бібліотекам на кшталт Pydantic читати ці метадані під час виконання.",[4151,4152,4153,4159,4160,3418],"warning",{},[3415,4154,4155,4156,4158],{},"Ніколи не читайте ",[3403,4157,3463],{}," напряму в продуктивному коді!","\nЗавжди використовуйте ",[3403,4161,4162],{},"typing.get_type_hints()",[3394,4164,4165],{},"Справа у двох важливих аспектах:",[3427,4167,4168,4183],{},[3430,4169,4170,4173,4174,4176,4177,4179,4180,4182],{},[3415,4171,4172],{},"Forward References (Вперед-посилання):"," Якщо тип анотований класом, який визначений нижче у файлі, ",[3403,4175,3463],{}," поверне рядок (наприклад, ",[3403,4178,3980],{},"), тоді як ",[3403,4181,4143],{}," автоматично резолвить його в реальний об'єкт класу, якщо він став доступним.",[3430,4184,4185,4188,4189,4192,4193,4195,4196,4198],{},[3415,4186,4187],{},"PEP 563 (Postponed Evaluation of Annotations):"," Якщо у файлі увімкнено відкладене обчислення (",[3403,4190,4191],{},"from __future__ import annotations","), всі анотації зберігаються в ",[3403,4194,3463],{}," як сирі рядки для оптимізації імпортів та пам'яті. ",[3403,4197,4143],{}," бере на себе завдання з их парсингу в контексті модуля.",[3489,4200,4202],{"className":3491,"code":4201,"filename":3751,"language":3494,"meta":3495,"style":3495},"from __future__ import annotations\nfrom typing import get_type_hints\n\nclass User:\n    manager: User  # Без future-імпорту це викликало б NameError, бо User ще не визначений\n\n# Пряме читання поверне рядок:\nprint(User.__annotations__[\"manager\"])  # Виведе: 'User'\n\n# Використання хелпера поверне посилання на клас:\nprint(get_type_hints(User)[\"manager\"])  # Виведе: \u003Cclass '__main__.User'>\n",[3403,4203,4204,4217,4228,4232,4241,4249,4253,4258,4279,4283,4288],{"__ignoreMap":3495},[3499,4205,4206,4208,4211,4214],{"class":3501,"line":3502},[3499,4207,3506],{"class":3505},[3499,4209,4210],{"class":3547}," __future__",[3499,4212,4213],{"class":3505}," import",[3499,4215,4216],{"class":3509}," annotations\n",[3499,4218,4219,4221,4223,4225],{"class":3501,"line":3519},[3499,4220,3506],{"class":3505},[3499,4222,3510],{"class":3509},[3499,4224,3513],{"class":3505},[3499,4226,4227],{"class":3509}," get_type_hints\n",[3499,4229,4230],{"class":3501,"line":3526},[3499,4231,3523],{"emptyLinePlaceholder":3522},[3499,4233,4234,4236,4239],{"class":3501,"line":3533},[3499,4235,3858],{"class":3536},[3499,4237,4238],{"class":3554}," User",[3499,4240,3629],{"class":3509},[3499,4242,4243,4246],{"class":3501,"line":3571},[3499,4244,4245],{"class":3509},"    manager: User  ",[3499,4247,4248],{"class":3529},"# Без future-імпорту це викликало б NameError, бо User ще не визначений\n",[3499,4250,4251],{"class":3501,"line":3577},[3499,4252,3523],{"emptyLinePlaceholder":3522},[3499,4254,4255],{"class":3501,"line":3705},[3499,4256,4257],{"class":3529},"# Пряме читання поверне рядок:\n",[3499,4259,4260,4262,4265,4267,4270,4273,4276],{"class":3501,"line":3800},[3499,4261,4111],{"class":3540},[3499,4263,4264],{"class":3509},"(User.",[3499,4266,3463],{"class":3547},[3499,4268,4269],{"class":3509},"[",[3499,4271,4272],{"class":3979},"\"manager\"",[3499,4274,4275],{"class":3509},"])  ",[3499,4277,4278],{"class":3529},"# Виведе: 'User'\n",[3499,4280,4281],{"class":3501,"line":3812},[3499,4282,3523],{"emptyLinePlaceholder":3522},[3499,4284,4285],{"class":3501,"line":3826},[3499,4286,4287],{"class":3529},"# Використання хелпера поверне посилання на клас:\n",[3499,4289,4290,4292,4295,4297,4299],{"class":3501,"line":3985},[3499,4291,4111],{"class":3540},[3499,4293,4294],{"class":3509},"(get_type_hints(User)[",[3499,4296,4272],{"class":3979},[3499,4298,4275],{"class":3509},[3499,4300,4301],{"class":3529},"# Виведе: \u003Cclass '__main__.User'>\n",[3452,4303],{},[3474,4305,4307],{"id":4306},"номінальна-проти-структурної-типізації-nominal-vs-structural-typing","Номінальна проти Структурної типізації (Nominal vs Structural Typing)",[3394,4309,4310,4311,4314,4315,4318,4319,3418],{},"Для C# розробника концепція типізації тісно пов'язана з класичним ООП: клас ",[3403,4312,4313],{},"A"," сумісний з класом ",[3403,4316,4317],{},"B"," лише якщо вони мають спільного предка або реалізують один інтерфейс. Це ",[3415,4320,4321],{},"номінальна типізація (Nominal Typing)",[3394,4323,4324,4325,4328,4329,4332],{},"Python за замовчуванням теж підтримує номінальну типізацію при наслідуванні класів. Проте, завдяки своїм динамічним кореням, він має першокласну підтримку ",[3415,4326,4327],{},"структурної типізації (Structural Typing або Static Duck Typing)",", яка реалізована через ",[3403,4330,4331],{},"typing.Protocol"," (PEP 544).",[3486,4334,4335,4459],{},[3489,4336,4339],{"className":3491,"code":4337,"filename":4338,"language":3494,"meta":3495,"style":3495},"class Animal:\n    def speak(self) -> str:\n        raise NotImplementedError\n\nclass Dog(Animal):\n    def speak(self) -> str:\n        return \"Woof\"\n\ndef make_animal_speak(animal: Animal) -> str:\n    return animal.speak()\n\nmake_animal_speak(Dog())  # Працює, бо Dog є Animal (номінально)\n","Номінальна типізація (Наслідування)",[3403,4340,4341,4350,4367,4375,4379,4394,4410,4417,4421,4440,4447,4451],{"__ignoreMap":3495},[3499,4342,4343,4345,4348],{"class":3501,"line":3502},[3499,4344,3858],{"class":3536},[3499,4346,4347],{"class":3554}," Animal",[3499,4349,3629],{"class":3509},[3499,4351,4352,4354,4357,4359,4361,4363,4365],{"class":3501,"line":3519},[3499,4353,3868],{"class":3536},[3499,4355,4356],{"class":3540}," speak",[3499,4358,3544],{"class":3509},[3499,4360,3839],{"class":3547},[3499,4362,3887],{"class":3509},[3499,4364,3560],{"class":3554},[3499,4366,3629],{"class":3509},[3499,4368,4369,4372],{"class":3501,"line":3526},[3499,4370,4371],{"class":3505},"        raise",[3499,4373,4374],{"class":3554}," NotImplementedError\n",[3499,4376,4377],{"class":3501,"line":3533},[3499,4378,3523],{"emptyLinePlaceholder":3522},[3499,4380,4381,4383,4386,4388,4391],{"class":3501,"line":3571},[3499,4382,3858],{"class":3536},[3499,4384,4385],{"class":3554}," Dog",[3499,4387,3544],{"class":3509},[3499,4389,4390],{"class":3554},"Animal",[3499,4392,4393],{"class":3509},"):\n",[3499,4395,4396,4398,4400,4402,4404,4406,4408],{"class":3501,"line":3577},[3499,4397,3868],{"class":3536},[3499,4399,4356],{"class":3540},[3499,4401,3544],{"class":3509},[3499,4403,3839],{"class":3547},[3499,4405,3887],{"class":3509},[3499,4407,3560],{"class":3554},[3499,4409,3629],{"class":3509},[3499,4411,4412,4414],{"class":3501,"line":3705},[3499,4413,3939],{"class":3505},[3499,4415,4416],{"class":3979}," \"Woof\"\n",[3499,4418,4419],{"class":3501,"line":3800},[3499,4420,3523],{"emptyLinePlaceholder":3522},[3499,4422,4423,4425,4428,4430,4433,4436,4438],{"class":3501,"line":3812},[3499,4424,3537],{"class":3536},[3499,4426,4427],{"class":3540}," make_animal_speak",[3499,4429,3544],{"class":3509},[3499,4431,4432],{"class":3547},"animal",[3499,4434,4435],{"class":3509},": Animal) -> ",[3499,4437,3560],{"class":3554},[3499,4439,3629],{"class":3509},[3499,4441,4442,4444],{"class":3501,"line":3826},[3499,4443,4076],{"class":3505},[3499,4445,4446],{"class":3509}," animal.speak()\n",[3499,4448,4449],{"class":3501,"line":3985},[3499,4450,3523],{"emptyLinePlaceholder":3522},[3499,4452,4453,4456],{"class":3501,"line":3991},[3499,4454,4455],{"class":3509},"make_animal_speak(Dog())  ",[3499,4457,4458],{"class":3529},"# Працює, бо Dog є Animal (номінально)\n",[3489,4460,4463],{"className":3491,"code":4461,"filename":4462,"language":3494,"meta":3495,"style":3495},"from typing import Protocol\n\n# Визначаємо контракт\nclass Speaker(Protocol):\n    def speak(self) -> str:\n        ...\n\n# Клас НЕ знає про існування Speaker і НЕ наслідується від нього\nclass Cat:\n    def speak(self) -> str:\n        return \"Meow\"\n\ndef make_speaker_speak(speaker: Speaker) -> str:\n    return speaker.speak()\n\n# mypy вважає це абсолютно коректним!\nmake_speaker_speak(Cat())  # Static Duck Typing у дії\n","Структурна типізація (Protocol)",[3403,4464,4465,4476,4480,4485,4499,4515,4520,4524,4529,4538,4554,4561,4565,4584,4591,4596,4602],{"__ignoreMap":3495},[3499,4466,4467,4469,4471,4473],{"class":3501,"line":3502},[3499,4468,3506],{"class":3505},[3499,4470,3510],{"class":3509},[3499,4472,3513],{"class":3505},[3499,4474,4475],{"class":3509}," Protocol\n",[3499,4477,4478],{"class":3501,"line":3519},[3499,4479,3523],{"emptyLinePlaceholder":3522},[3499,4481,4482],{"class":3501,"line":3526},[3499,4483,4484],{"class":3529},"# Визначаємо контракт\n",[3499,4486,4487,4489,4492,4494,4497],{"class":3501,"line":3533},[3499,4488,3858],{"class":3536},[3499,4490,4491],{"class":3554}," Speaker",[3499,4493,3544],{"class":3509},[3499,4495,4496],{"class":3554},"Protocol",[3499,4498,4393],{"class":3509},[3499,4500,4501,4503,4505,4507,4509,4511,4513],{"class":3501,"line":3571},[3499,4502,3868],{"class":3536},[3499,4504,4356],{"class":3540},[3499,4506,3544],{"class":3509},[3499,4508,3839],{"class":3547},[3499,4510,3887],{"class":3509},[3499,4512,3560],{"class":3554},[3499,4514,3629],{"class":3509},[3499,4516,4517],{"class":3501,"line":3577},[3499,4518,4519],{"class":3509},"        ...\n",[3499,4521,4522],{"class":3501,"line":3705},[3499,4523,3523],{"emptyLinePlaceholder":3522},[3499,4525,4526],{"class":3501,"line":3800},[3499,4527,4528],{"class":3529},"# Клас НЕ знає про існування Speaker і НЕ наслідується від нього\n",[3499,4530,4531,4533,4536],{"class":3501,"line":3812},[3499,4532,3858],{"class":3536},[3499,4534,4535],{"class":3554}," Cat",[3499,4537,3629],{"class":3509},[3499,4539,4540,4542,4544,4546,4548,4550,4552],{"class":3501,"line":3826},[3499,4541,3868],{"class":3536},[3499,4543,4356],{"class":3540},[3499,4545,3544],{"class":3509},[3499,4547,3839],{"class":3547},[3499,4549,3887],{"class":3509},[3499,4551,3560],{"class":3554},[3499,4553,3629],{"class":3509},[3499,4555,4556,4558],{"class":3501,"line":3985},[3499,4557,3939],{"class":3505},[3499,4559,4560],{"class":3979}," \"Meow\"\n",[3499,4562,4563],{"class":3501,"line":3991},[3499,4564,3523],{"emptyLinePlaceholder":3522},[3499,4566,4567,4569,4572,4574,4577,4580,4582],{"class":3501,"line":3997},[3499,4568,3537],{"class":3536},[3499,4570,4571],{"class":3540}," make_speaker_speak",[3499,4573,3544],{"class":3509},[3499,4575,4576],{"class":3547},"speaker",[3499,4578,4579],{"class":3509},": Speaker) -> ",[3499,4581,3560],{"class":3554},[3499,4583,3629],{"class":3509},[3499,4585,4586,4588],{"class":3501,"line":4006},[3499,4587,4076],{"class":3505},[3499,4589,4590],{"class":3509}," speaker.speak()\n",[3499,4592,4594],{"class":3501,"line":4593},15,[3499,4595,3523],{"emptyLinePlaceholder":3522},[3499,4597,4599],{"class":3501,"line":4598},16,[3499,4600,4601],{"class":3529},"# mypy вважає це абсолютно коректним!\n",[3499,4603,4605,4608],{"class":3501,"line":4604},17,[3499,4606,4607],{"class":3509},"make_speaker_speak(Cat())  ",[3499,4609,4610],{"class":3529},"# Static Duck Typing у дії\n",[4612,4613,4614,4616,4617,4620],"tip",{},[3403,4615,4496],{}," в Python є єквівалентом інтерфейсів у C# (або TypeScript). Головна відмінність — класу в Python не потрібно явно писати ",[3403,4618,4619],{},"implements Interface",". Якщо сигнатури методів та атрибутів збігаються, статичний аналізатор вважає тип сумісним. Це дозволяє створювати надзвичайно слабкопов'язані системи.",[3452,4622],{},[3474,4624,4626],{"id":4625},"вбудовані-колекції-та-спеціальні-типи","Вбудовані колекції та спеціальні типи",[3394,4628,4629],{},"Для побудови надійних інтерфейсів та моделей даних у вебзастосунках важливо точно описувати структури даних. Розглянемо особливості типізації колекцій та спеціальних типів в Python.",[3709,4631,4632,4645,4661,4671],{},[3394,4633,4634],{},[3415,4635,4636,4637,4640,4641,4644],{},"Чому квадратні дужки ",[3403,4638,4639],{},"[ ]"," замість кутових ",[3403,4642,4643],{},"\u003C >","?",[3394,4646,4647,4648,3840,4651,4654,4655,4140,4658,3418],{},"Розробники на C# звикли до запису generic-типів через кутові дужки, наприклад ",[3403,4649,4650],{},"List\u003Cint>",[3403,4652,4653],{},"Dictionary\u003Cstring, string>",". В Python для цього використовуються квадратні дужки — ",[3403,4656,4657],{},"list[int]",[3403,4659,4660],{},"dict[str, str]",[3394,4662,4663,4664,4140,4667,4670],{},"Це зумовлено особливостями синтаксичного аналізатора (parser) Python: використання ",[3403,4665,4666],{},"\u003C",[3403,4668,4669],{},">"," призвело б до граматичних конфліктів із математичними операторами порівняння «менше» та «більше». Крім того, квадратні дужки в Python традиційно відповідають за операцію доступу за індексом (subscription).",[3394,4672,4673,4674,4676,4677,4680,4681,4684,4685,4688],{},"Під капотом цей механізм працює так: коли ми пишемо ",[3403,4675,4657],{},", інтерпретатор викликає спеціальний магічний метод класу ",[3403,4678,4679],{},"__class_getitem__"," (введений у PEP 560). Метод ",[3403,4682,4683],{},"list.__class_getitem__(int)"," створює та повертає спеціальний об'єкт типу ",[3403,4686,4687],{},"types.GenericAlias",". Цей об'єкт зберігає інформацію про базовий клас та типи його параметрів у runtime, що дозволяє статичним аналізаторам та бібліотекам на кшталт Pydantic зчитувати параметризовані типи.",[3741,4690,4692],{"id":4691},"_1-типізація-колекцій-фіксований-vs-динамічний-розмір","1. Типізація колекцій: фіксований vs динамічний розмір",[3394,4694,4695],{},"При типізації колекцій важливо розуміти, як лінтери сприймають довжину структури:",[4697,4698,4699,4712,4721],"ul",{},[3430,4700,4701,4711],{},[3415,4702,4703,4704,3468,4707,4710],{},"Списки та множини (",[3403,4705,4706],{},"list[T]",[3403,4708,4709],{},"set[T]",")"," — це динамічні колекції однорідних елементів. Ми анотуємо лише тип елементів, які в них містяться.",[3430,4713,4714,4720],{},[3415,4715,4716,4717,4710],{},"Словники (",[3403,4718,4719],{},"dict[K, V]"," — анотуються двома типами: тип ключа та тип значення.",[3430,4722,4723,4729,4730],{},[3415,4724,4725,4726,4710],{},"Кортежі (",[3403,4727,4728],{},"tuple"," — мають дві форми анотування:\n",[3427,4731,4732,4745],{},[3430,4733,4734,3609,4737,4740,4741,4744],{},[3398,4735,4736],{},"Фіксована довжина та структура",[3403,4738,4739],{},"tuple[int, str, bool]"," описує кортеж строго з трьох елементів вказаних типів (еквівалент кортежу в C# ",[3403,4742,4743],{},"(int, string, bool)",").",[3430,4746,4747,3609,4750,4753,4754,3840,4757,4760],{},[3398,4748,4749],{},"Довільна довжина",[3403,4751,4752],{},"tuple[int, ...]"," описує кортеж довільної довжини, де всі елементи є цілими числами (еквівалент ",[3403,4755,4756],{},"ReadOnlySpan\u003Cint>",[3403,4758,4759],{},"IEnumerable\u003Cint>"," у C#).",[3489,4762,4764],{"className":3491,"code":4763,"filename":3751,"language":3494,"meta":3495,"style":3495},"# Динамічні колекції\nuser_ids: list[int] = [1, 2, 3]\nunique_emails: set[str] = {\"user@test.com\"}\nheaders: dict[str, str] = {\"Content-Type\": \"application\u002Fjson\"}\n\n# Фіксований кортеж (координати: x, y, назва точки)\npoint: tuple[int, int, str] = (10, 20, \"A\")\n\n# Кортеж довільної довжини (лише числа)\nnumbers: tuple[int, ...] = (1, 2, 3, 4, 5)\n",[3403,4765,4766,4771,4795,4811,4834,4838,4843,4877,4881,4886],{"__ignoreMap":3495},[3499,4767,4768],{"class":3501,"line":3502},[3499,4769,4770],{"class":3529},"# Динамічні колекції\n",[3499,4772,4773,4776,4778,4781,4784,4786,4788,4790,4793],{"class":3501,"line":3519},[3499,4774,4775],{"class":3509},"user_ids: list[",[3499,4777,3555],{"class":3554},[3499,4779,4780],{"class":3509},"] = [",[3499,4782,4783],{"class":3771},"1",[3499,4785,3468],{"class":3509},[3499,4787,4091],{"class":3771},[3499,4789,3468],{"class":3509},[3499,4791,4792],{"class":3771},"3",[3499,4794,3676],{"class":3509},[3499,4796,4797,4800,4802,4805,4808],{"class":3501,"line":3526},[3499,4798,4799],{"class":3509},"unique_emails: set[",[3499,4801,3560],{"class":3554},[3499,4803,4804],{"class":3509},"] = {",[3499,4806,4807],{"class":3979},"\"user@test.com\"",[3499,4809,4810],{"class":3509},"}\n",[3499,4812,4813,4816,4818,4820,4822,4824,4827,4829,4832],{"class":3501,"line":3533},[3499,4814,4815],{"class":3509},"headers: dict[",[3499,4817,3560],{"class":3554},[3499,4819,3468],{"class":3509},[3499,4821,3560],{"class":3554},[3499,4823,4804],{"class":3509},[3499,4825,4826],{"class":3979},"\"Content-Type\"",[3499,4828,3609],{"class":3509},[3499,4830,4831],{"class":3979},"\"application\u002Fjson\"",[3499,4833,4810],{"class":3509},[3499,4835,4836],{"class":3501,"line":3571},[3499,4837,3523],{"emptyLinePlaceholder":3522},[3499,4839,4840],{"class":3501,"line":3577},[3499,4841,4842],{"class":3529},"# Фіксований кортеж (координати: x, y, назва точки)\n",[3499,4844,4845,4848,4850,4852,4854,4856,4858,4861,4864,4866,4869,4871,4874],{"class":3501,"line":3705},[3499,4846,4847],{"class":3509},"point: tuple[",[3499,4849,3555],{"class":3554},[3499,4851,3468],{"class":3509},[3499,4853,3555],{"class":3554},[3499,4855,3468],{"class":3509},[3499,4857,3560],{"class":3554},[3499,4859,4860],{"class":3509},"] = (",[3499,4862,4863],{"class":3771},"10",[3499,4865,3468],{"class":3509},[3499,4867,4868],{"class":3771},"20",[3499,4870,3468],{"class":3509},[3499,4872,4873],{"class":3979},"\"A\"",[3499,4875,4876],{"class":3509},")\n",[3499,4878,4879],{"class":3501,"line":3800},[3499,4880,3523],{"emptyLinePlaceholder":3522},[3499,4882,4883],{"class":3501,"line":3812},[3499,4884,4885],{"class":3529},"# Кортеж довільної довжини (лише числа)\n",[3499,4887,4888,4891,4893,4896,4898,4900,4902,4904,4906,4908,4911,4913,4916],{"class":3501,"line":3826},[3499,4889,4890],{"class":3509},"numbers: tuple[",[3499,4892,3555],{"class":3554},[3499,4894,4895],{"class":3509},", ...] = (",[3499,4897,4783],{"class":3771},[3499,4899,3468],{"class":3509},[3499,4901,4091],{"class":3771},[3499,4903,3468],{"class":3509},[3499,4905,4792],{"class":3771},[3499,4907,3468],{"class":3509},[3499,4909,4910],{"class":3771},"4",[3499,4912,3468],{"class":3509},[3499,4914,4915],{"class":3771},"5",[3499,4917,4876],{"class":3509},[3741,4919,4921],{"id":4920},"_2-any-проти-object-границя-безпеки","2. Any проти object: границя безпеки",[3394,4923,4924],{},"В системі типізації Python є два типи, які виглядають схоже, але несуть діаметрально протилежний зміст для статичного аналізу:",[4697,4926,4927,4946],{},[3430,4928,4929,4938,4939,4941,4942,4945],{},[3415,4930,4931,4934,4935,4710],{},[3403,4932,4933],{},"Any"," (з модуля ",[3403,4936,4937],{},"typing"," — вимикає перевірку типів для змінної. Лінтер дозволяє викликати на об'єкті з типом ",[3403,4940,4933],{}," будь-які методи та звертатися до будь-каких полів. Це єквівалент ключового слова ",[3403,4943,4944],{},"dynamic"," у C#.",[3430,4947,4948,4953,4954,4956,4957,4960,4961,4963,4964,4967,4968,4945],{},[3415,4949,4950],{},[3403,4951,4952],{},"object"," — є базовим класом для всіх об'єктів у Python. Якщо змінна анотована як ",[3403,4955,4952],{},", лінтер ",[3415,4958,4959],{},"забороняє"," викликати на ній будь-які методи (окрім базових для ",[3403,4962,4952],{}," на кшталт ",[3403,4965,4966],{},"__str__","), доки ви явно не приведете тип за допомогою звуження. Це єквівалент типу ",[3403,4969,4952],{},[3489,4971,4973],{"className":3491,"code":4972,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import Any\n\ndef process_dynamic(data: Any) -> None:\n    # Лінтер мовчить, але в runtime може впасти AttributeError, якщо методу немає\n    data.send_request()\n\ndef process_safe(data: object) -> None:\n    # Помилка лінтера: \"object\" has no attribute \"send_request\"\n    # data.send_request()\n\n    # Правильний безпечний шлях (звуження типу):\n    if isinstance(data, DatabaseConnector):\n        data.connect() # Тепер лінтер знає, що це DatabaseConnector\n",[3403,4974,4975,4986,4990,5009,5014,5019,5023,5044,5049,5054,5058,5063,5074],{"__ignoreMap":3495},[3499,4976,4977,4979,4981,4983],{"class":3501,"line":3502},[3499,4978,3506],{"class":3505},[3499,4980,3510],{"class":3509},[3499,4982,3513],{"class":3505},[3499,4984,4985],{"class":3509}," Any\n",[3499,4987,4988],{"class":3501,"line":3519},[3499,4989,3523],{"emptyLinePlaceholder":3522},[3499,4991,4992,4994,4997,4999,5002,5005,5007],{"class":3501,"line":3526},[3499,4993,3537],{"class":3536},[3499,4995,4996],{"class":3540}," process_dynamic",[3499,4998,3544],{"class":3509},[3499,5000,5001],{"class":3547},"data",[3499,5003,5004],{"class":3509},": Any) -> ",[3499,5006,3483],{"class":3536},[3499,5008,3629],{"class":3509},[3499,5010,5011],{"class":3501,"line":3533},[3499,5012,5013],{"class":3529},"    # Лінтер мовчить, але в runtime може впасти AttributeError, якщо методу немає\n",[3499,5015,5016],{"class":3501,"line":3571},[3499,5017,5018],{"class":3509},"    data.send_request()\n",[3499,5020,5021],{"class":3501,"line":3577},[3499,5022,3523],{"emptyLinePlaceholder":3522},[3499,5024,5025,5027,5030,5032,5034,5036,5038,5040,5042],{"class":3501,"line":3705},[3499,5026,3537],{"class":3536},[3499,5028,5029],{"class":3540}," process_safe",[3499,5031,3544],{"class":3509},[3499,5033,5001],{"class":3547},[3499,5035,3609],{"class":3509},[3499,5037,4952],{"class":3554},[3499,5039,3887],{"class":3509},[3499,5041,3483],{"class":3536},[3499,5043,3629],{"class":3509},[3499,5045,5046],{"class":3501,"line":3800},[3499,5047,5048],{"class":3529},"    # Помилка лінтера: \"object\" has no attribute \"send_request\"\n",[3499,5050,5051],{"class":3501,"line":3812},[3499,5052,5053],{"class":3529},"    # data.send_request()\n",[3499,5055,5056],{"class":3501,"line":3826},[3499,5057,3523],{"emptyLinePlaceholder":3522},[3499,5059,5060],{"class":3501,"line":3985},[3499,5061,5062],{"class":3529},"    # Правильний безпечний шлях (звуження типу):\n",[3499,5064,5065,5068,5071],{"class":3501,"line":3991},[3499,5066,5067],{"class":3505},"    if",[3499,5069,5070],{"class":3540}," isinstance",[3499,5072,5073],{"class":3509},"(data, DatabaseConnector):\n",[3499,5075,5076,5079],{"class":3501,"line":3997},[3499,5077,5078],{"class":3509},"        data.connect() ",[3499,5080,5081],{"class":3529},"# Тепер лінтер знає, що це DatabaseConnector\n",[3741,5083,5085],{"id":5084},"_3-типізація-зворотних-викликів-callable","3. Типізація зворотних викликів (Callable)",[3394,5087,5088,5089,3418],{},"Для типізації функцій, які передаються як аргументи (callback-функції, обробники подій, middleware), використовується ",[3403,5090,5091],{},"typing.Callable",[3394,5093,5094,5095,3418],{},"Синтаксис виглядає так: ",[3403,5096,5097],{},"Callable[[ТипАргументу1, ТипАргументу2], ТипПовернення]",[3489,5099,5101],{"className":3491,"code":5100,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import Callable\n\n# Функція приймає рядок і число, повертає булеве значення\ntype ValidatorFunc = Callable[[str, int], bool]\n\ndef validate_input(value: str, min_len: int, validator: ValidatorFunc) -> bool:\n    return validator(value, min_len)\n\n# Приклад реалізації валідатора\ndef length_validator(text: str, length: int) -> bool:\n    return len(text) >= length\n\nvalidate_input(\"hello\", 3, length_validator)  # OK\n",[3403,5102,5103,5114,5118,5123,5143,5147,5184,5191,5195,5200,5231,5241,5245],{"__ignoreMap":3495},[3499,5104,5105,5107,5109,5111],{"class":3501,"line":3502},[3499,5106,3506],{"class":3505},[3499,5108,3510],{"class":3509},[3499,5110,3513],{"class":3505},[3499,5112,5113],{"class":3509}," Callable\n",[3499,5115,5116],{"class":3501,"line":3519},[3499,5117,3523],{"emptyLinePlaceholder":3522},[3499,5119,5120],{"class":3501,"line":3526},[3499,5121,5122],{"class":3529},"# Функція приймає рядок і число, повертає булеве значення\n",[3499,5124,5125,5127,5130,5132,5134,5136,5139,5141],{"class":3501,"line":3533},[3499,5126,3654],{"class":3554},[3499,5128,5129],{"class":3509}," ValidatorFunc = Callable[[",[3499,5131,3560],{"class":3554},[3499,5133,3468],{"class":3509},[3499,5135,3555],{"class":3554},[3499,5137,5138],{"class":3509},"], ",[3499,5140,3927],{"class":3554},[3499,5142,3676],{"class":3509},[3499,5144,5145],{"class":3501,"line":3571},[3499,5146,3523],{"emptyLinePlaceholder":3522},[3499,5148,5149,5151,5154,5156,5159,5161,5163,5165,5168,5170,5172,5174,5177,5180,5182],{"class":3501,"line":3577},[3499,5150,3537],{"class":3536},[3499,5152,5153],{"class":3540}," validate_input",[3499,5155,3544],{"class":3509},[3499,5157,5158],{"class":3547},"value",[3499,5160,3609],{"class":3509},[3499,5162,3560],{"class":3554},[3499,5164,3468],{"class":3509},[3499,5166,5167],{"class":3547},"min_len",[3499,5169,3609],{"class":3509},[3499,5171,3555],{"class":3554},[3499,5173,3468],{"class":3509},[3499,5175,5176],{"class":3547},"validator",[3499,5178,5179],{"class":3509},": ValidatorFunc) -> ",[3499,5181,3927],{"class":3554},[3499,5183,3629],{"class":3509},[3499,5185,5186,5188],{"class":3501,"line":3705},[3499,5187,4076],{"class":3505},[3499,5189,5190],{"class":3509}," validator(value, min_len)\n",[3499,5192,5193],{"class":3501,"line":3800},[3499,5194,3523],{"emptyLinePlaceholder":3522},[3499,5196,5197],{"class":3501,"line":3812},[3499,5198,5199],{"class":3529},"# Приклад реалізації валідатора\n",[3499,5201,5202,5204,5207,5209,5212,5214,5216,5218,5221,5223,5225,5227,5229],{"class":3501,"line":3826},[3499,5203,3537],{"class":3536},[3499,5205,5206],{"class":3540}," length_validator",[3499,5208,3544],{"class":3509},[3499,5210,5211],{"class":3547},"text",[3499,5213,3609],{"class":3509},[3499,5215,3560],{"class":3554},[3499,5217,3468],{"class":3509},[3499,5219,5220],{"class":3547},"length",[3499,5222,3609],{"class":3509},[3499,5224,3555],{"class":3554},[3499,5226,3887],{"class":3509},[3499,5228,3927],{"class":3554},[3499,5230,3629],{"class":3509},[3499,5232,5233,5235,5238],{"class":3501,"line":3985},[3499,5234,4076],{"class":3505},[3499,5236,5237],{"class":3540}," len",[3499,5239,5240],{"class":3509},"(text) >= length\n",[3499,5242,5243],{"class":3501,"line":3991},[3499,5244,3523],{"emptyLinePlaceholder":3522},[3499,5246,5247,5250,5253,5255,5257,5260],{"class":3501,"line":3997},[3499,5248,5249],{"class":3509},"validate_input(",[3499,5251,5252],{"class":3979},"\"hello\"",[3499,5254,3468],{"class":3509},[3499,5256,4792],{"class":3771},[3499,5258,5259],{"class":3509},", length_validator)  ",[3499,5261,5262],{"class":3529},"# OK\n",[3452,5264],{},[3474,5266,5268],{"id":5267},"параметричний-поліморфізм-generics-в-python","Параметричний поліморфізм: Generics в Python",[3394,5270,5271,5272,5275],{},"Як і в C# (",[3403,5273,5274],{},"List\u003CT>","), в Python ви можете створювати узагальнені класи та функції, які працюють з різними типами даних, зберігаючи при цьому строгість типізації.",[3741,5277,5279,5280,5283],{"id":5278},"_1-еволюція-generics-typevar-проти-синтаксису-python-312-pep-695","1. Еволюція Generics: ",[3403,5281,5282],{},"TypeVar"," проти синтаксису Python 3.12 (PEP 695)",[3394,5285,5286,5287,5289,5290,3418],{},"До версії Python 3.12 створення узагальненого класу вимагало явного оголошення змінної типу за допомогою ",[3403,5288,5282],{}," та наслідування від ",[3403,5291,5292],{},"Generic[T]",[3486,5294,5295,5430],{},[3489,5296,5299],{"className":3491,"code":5297,"filename":5298,"language":3494,"meta":3495,"style":3495},"from typing import TypeVar, Generic\n\n# Створюємо змінну типу\nT = TypeVar(\"T\")\n\nclass Repository(Generic[T]):\n    def __init__(self) -> None:\n        self._items: list[T] = []\n\n    def add(self, item: T) -> None:\n        self._items.append(item)\n\n    def get_all(self) -> list[T]:\n        return self._items\n","Python \u003C 3.12 (Класичний стиль)",[3403,5300,5301,5312,5316,5321,5331,5335,5345,5361,5368,5372,5395,5402,5406,5420],{"__ignoreMap":3495},[3499,5302,5303,5305,5307,5309],{"class":3501,"line":3502},[3499,5304,3506],{"class":3505},[3499,5306,3510],{"class":3509},[3499,5308,3513],{"class":3505},[3499,5310,5311],{"class":3509}," TypeVar, Generic\n",[3499,5313,5314],{"class":3501,"line":3519},[3499,5315,3523],{"emptyLinePlaceholder":3522},[3499,5317,5318],{"class":3501,"line":3526},[3499,5319,5320],{"class":3529},"# Створюємо змінну типу\n",[3499,5322,5323,5326,5329],{"class":3501,"line":3533},[3499,5324,5325],{"class":3509},"T = TypeVar(",[3499,5327,5328],{"class":3979},"\"T\"",[3499,5330,4876],{"class":3509},[3499,5332,5333],{"class":3501,"line":3571},[3499,5334,3523],{"emptyLinePlaceholder":3522},[3499,5336,5337,5339,5342],{"class":3501,"line":3577},[3499,5338,3858],{"class":3536},[3499,5340,5341],{"class":3554}," Repository",[3499,5343,5344],{"class":3509},"(Generic[T]):\n",[3499,5346,5347,5349,5351,5353,5355,5357,5359],{"class":3501,"line":3705},[3499,5348,3868],{"class":3536},[3499,5350,3871],{"class":3540},[3499,5352,3544],{"class":3509},[3499,5354,3839],{"class":3547},[3499,5356,3887],{"class":3509},[3499,5358,3483],{"class":3536},[3499,5360,3629],{"class":3509},[3499,5362,5363,5365],{"class":3501,"line":3800},[3499,5364,3896],{"class":3536},[3499,5366,5367],{"class":3509},"._items: list[T] = []\n",[3499,5369,5370],{"class":3501,"line":3812},[3499,5371,3523],{"emptyLinePlaceholder":3522},[3499,5373,5374,5376,5379,5381,5383,5385,5388,5391,5393],{"class":3501,"line":3826},[3499,5375,3868],{"class":3536},[3499,5377,5378],{"class":3540}," add",[3499,5380,3544],{"class":3509},[3499,5382,3839],{"class":3547},[3499,5384,3468],{"class":3509},[3499,5386,5387],{"class":3547},"item",[3499,5389,5390],{"class":3509},": T) -> ",[3499,5392,3483],{"class":3536},[3499,5394,3629],{"class":3509},[3499,5396,5397,5399],{"class":3501,"line":3985},[3499,5398,3896],{"class":3536},[3499,5400,5401],{"class":3509},"._items.append(item)\n",[3499,5403,5404],{"class":3501,"line":3991},[3499,5405,3523],{"emptyLinePlaceholder":3522},[3499,5407,5408,5410,5413,5415,5417],{"class":3501,"line":3997},[3499,5409,3868],{"class":3536},[3499,5411,5412],{"class":3540}," get_all",[3499,5414,3544],{"class":3509},[3499,5416,3839],{"class":3547},[3499,5418,5419],{"class":3509},") -> list[T]:\n",[3499,5421,5422,5424,5427],{"class":3501,"line":4006},[3499,5423,3939],{"class":3505},[3499,5425,5426],{"class":3536}," self",[3499,5428,5429],{"class":3509},"._items\n",[3489,5431,5434],{"className":3491,"code":5432,"filename":5433,"language":3494,"meta":3495,"style":3495},"# Більше не потрібні TypeVar та Generic[T]!\n# Тип параметризації вказується безпосередньо у квадратних дужках після назви класу.\nclass Repository[T]:\n    def __init__(self) -> None:\n        self._items: list[T] = []\n\n    def add(self, item: T) -> None:\n        self._items.append(item)\n\n    def get_all(self) -> list[T]:\n        return self._items\n","Python 3.12+ (PEP 695)",[3403,5435,5436,5441,5446,5453,5469,5475,5479,5499,5505,5509,5521],{"__ignoreMap":3495},[3499,5437,5438],{"class":3501,"line":3502},[3499,5439,5440],{"class":3529},"# Більше не потрібні TypeVar та Generic[T]!\n",[3499,5442,5443],{"class":3501,"line":3519},[3499,5444,5445],{"class":3529},"# Тип параметризації вказується безпосередньо у квадратних дужках після назви класу.\n",[3499,5447,5448,5450],{"class":3501,"line":3526},[3499,5449,3858],{"class":3536},[3499,5451,5452],{"class":3509}," Repository[T]:\n",[3499,5454,5455,5457,5459,5461,5463,5465,5467],{"class":3501,"line":3533},[3499,5456,3868],{"class":3536},[3499,5458,3871],{"class":3540},[3499,5460,3544],{"class":3509},[3499,5462,3839],{"class":3547},[3499,5464,3887],{"class":3509},[3499,5466,3483],{"class":3536},[3499,5468,3629],{"class":3509},[3499,5470,5471,5473],{"class":3501,"line":3571},[3499,5472,3896],{"class":3536},[3499,5474,5367],{"class":3509},[3499,5476,5477],{"class":3501,"line":3577},[3499,5478,3523],{"emptyLinePlaceholder":3522},[3499,5480,5481,5483,5485,5487,5489,5491,5493,5495,5497],{"class":3501,"line":3705},[3499,5482,3868],{"class":3536},[3499,5484,5378],{"class":3540},[3499,5486,3544],{"class":3509},[3499,5488,3839],{"class":3547},[3499,5490,3468],{"class":3509},[3499,5492,5387],{"class":3547},[3499,5494,5390],{"class":3509},[3499,5496,3483],{"class":3536},[3499,5498,3629],{"class":3509},[3499,5500,5501,5503],{"class":3501,"line":3800},[3499,5502,3896],{"class":3536},[3499,5504,5401],{"class":3509},[3499,5506,5507],{"class":3501,"line":3812},[3499,5508,3523],{"emptyLinePlaceholder":3522},[3499,5510,5511,5513,5515,5517,5519],{"class":3501,"line":3826},[3499,5512,3868],{"class":3536},[3499,5514,5412],{"class":3540},[3499,5516,3544],{"class":3509},[3499,5518,3839],{"class":3547},[3499,5520,5419],{"class":3509},[3499,5522,5523,5525,5527],{"class":3501,"line":3985},[3499,5524,3939],{"class":3505},[3499,5526,5426],{"class":3536},[3499,5528,5429],{"class":3509},[4612,5530,5531,5536,5539],{},[3394,5532,5533],{},[3415,5534,5535],{},"Обмеження типу (Type Constraints):",[3394,5537,5538],{},"Ви можете обмежити множину допустимих типів.",[4697,5540,5541,5550],{},[3430,5542,5543,5544,3840,5547,3418],{},"У старому синтаксисі: ",[3403,5545,5546],{},"T = TypeVar(\"T\", int, str)",[3403,5548,5549],{},"T = TypeVar(\"T\", bound=Animal)",[3430,5551,5552,5553,5556,5557,4760],{},"У новому синтаксисі 3.12+: ",[3403,5554,5555],{},"class Repository[T: Animal]"," (еквівалент ",[3403,5558,5559],{},"where T : Animal",[3741,5561,5563,5564,5567],{"id":5562},"_2-типізація-метаданих-функцій-paramspec-pep-612","2. Типізація метаданих функцій: ",[3403,5565,5566],{},"ParamSpec"," (PEP 612)",[3394,5569,5570,5571,5573,5574,5576],{},"При написанні декораторів часто виникає проблема: як зберегти сигнатуру аргументів декорованої функції? Якщо використати звичайний ",[3403,5572,5282],{},", ми втратимо інформацію про назви та типи параметрів. Для цього введено ",[3403,5575,5566],{}," (Parameter Specification).",[3394,5578,5579],{},"Він дозволяє захопити сигнатуру параметрів однієї функції та перенести її на іншу (зазвичай на обгортку всередині декоратора).",[3489,5581,5583],{"className":3491,"code":5582,"filename":3751,"language":3494,"meta":3495,"style":3495},"import time\nfrom typing import Callable, ParamSpec, TypeVar\n\nP = ParamSpec(\"P\")\nR = TypeVar(\"R\")\n\ndef log_execution_time(func: Callable[P, R]) -> Callable[P, R]:\n    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:\n        start_time = time.perf_counter()\n        result = func(*args, **kwargs)\n        end_time = time.perf_counter()\n        print(f\"Функція {func.__name__} виконалася за {end_time - start_time:.4f} сек\")\n        return result\n    return wrapper\n\n@log_execution_time\ndef calculate_sum(a: int, b: int) -> int:\n    return a + b\n\n# Статичний аналізатор тепер знає точні типи аргументів:\ncalculate_sum(10, 20)  # OK\n# calculate_sum(\"10\", 20)  # Помилка типізації!\n",[3403,5584,5585,5592,5603,5607,5617,5627,5631,5646,5668,5673,5678,5683,5722,5729,5736,5740,5745,5776,5784,5789,5795,5812],{"__ignoreMap":3495},[3499,5586,5587,5589],{"class":3501,"line":3502},[3499,5588,3513],{"class":3505},[3499,5590,5591],{"class":3509}," time\n",[3499,5593,5594,5596,5598,5600],{"class":3501,"line":3519},[3499,5595,3506],{"class":3505},[3499,5597,3510],{"class":3509},[3499,5599,3513],{"class":3505},[3499,5601,5602],{"class":3509}," Callable, ParamSpec, TypeVar\n",[3499,5604,5605],{"class":3501,"line":3526},[3499,5606,3523],{"emptyLinePlaceholder":3522},[3499,5608,5609,5612,5615],{"class":3501,"line":3533},[3499,5610,5611],{"class":3509},"P = ParamSpec(",[3499,5613,5614],{"class":3979},"\"P\"",[3499,5616,4876],{"class":3509},[3499,5618,5619,5622,5625],{"class":3501,"line":3571},[3499,5620,5621],{"class":3509},"R = TypeVar(",[3499,5623,5624],{"class":3979},"\"R\"",[3499,5626,4876],{"class":3509},[3499,5628,5629],{"class":3501,"line":3577},[3499,5630,3523],{"emptyLinePlaceholder":3522},[3499,5632,5633,5635,5638,5640,5643],{"class":3501,"line":3705},[3499,5634,3537],{"class":3536},[3499,5636,5637],{"class":3540}," log_execution_time",[3499,5639,3544],{"class":3509},[3499,5641,5642],{"class":3547},"func",[3499,5644,5645],{"class":3509},": Callable[P, R]) -> Callable[P, R]:\n",[3499,5647,5648,5650,5653,5656,5659,5662,5665],{"class":3501,"line":3800},[3499,5649,3868],{"class":3536},[3499,5651,5652],{"class":3540}," wrapper",[3499,5654,5655],{"class":3509},"(*",[3499,5657,5658],{"class":3547},"args",[3499,5660,5661],{"class":3509},": P.args, **",[3499,5663,5664],{"class":3547},"kwargs",[3499,5666,5667],{"class":3509},": P.kwargs) -> R:\n",[3499,5669,5670],{"class":3501,"line":3812},[3499,5671,5672],{"class":3509},"        start_time = time.perf_counter()\n",[3499,5674,5675],{"class":3501,"line":3826},[3499,5676,5677],{"class":3509},"        result = func(*args, **kwargs)\n",[3499,5679,5680],{"class":3501,"line":3985},[3499,5681,5682],{"class":3509},"        end_time = time.perf_counter()\n",[3499,5684,5685,5688,5690,5693,5696,5698,5701,5704,5706,5709,5711,5714,5717,5720],{"class":3501,"line":3991},[3499,5686,5687],{"class":3540},"        print",[3499,5689,3544],{"class":3509},[3499,5691,5692],{"class":3536},"f",[3499,5694,5695],{"class":3979},"\"Функція ",[3499,5697,4085],{"class":3536},[3499,5699,5700],{"class":3509},"func.",[3499,5702,5703],{"class":3547},"__name__",[3499,5705,4094],{"class":3536},[3499,5707,5708],{"class":3979}," виконалася за ",[3499,5710,4085],{"class":3536},[3499,5712,5713],{"class":3509},"end_time - start_time",[3499,5715,5716],{"class":3536},":.4f}",[3499,5718,5719],{"class":3979}," сек\"",[3499,5721,4876],{"class":3509},[3499,5723,5724,5726],{"class":3501,"line":3997},[3499,5725,3939],{"class":3505},[3499,5727,5728],{"class":3509}," result\n",[3499,5730,5731,5733],{"class":3501,"line":4006},[3499,5732,4076],{"class":3505},[3499,5734,5735],{"class":3509}," wrapper\n",[3499,5737,5738],{"class":3501,"line":4593},[3499,5739,3523],{"emptyLinePlaceholder":3522},[3499,5741,5742],{"class":3501,"line":4598},[3499,5743,5744],{"class":3540},"@log_execution_time\n",[3499,5746,5747,5749,5752,5754,5757,5759,5761,5763,5766,5768,5770,5772,5774],{"class":3501,"line":4604},[3499,5748,3537],{"class":3536},[3499,5750,5751],{"class":3540}," calculate_sum",[3499,5753,3544],{"class":3509},[3499,5755,5756],{"class":3547},"a",[3499,5758,3609],{"class":3509},[3499,5760,3555],{"class":3554},[3499,5762,3468],{"class":3509},[3499,5764,5765],{"class":3547},"b",[3499,5767,3609],{"class":3509},[3499,5769,3555],{"class":3554},[3499,5771,3887],{"class":3509},[3499,5773,3555],{"class":3554},[3499,5775,3629],{"class":3509},[3499,5777,5779,5781],{"class":3501,"line":5778},18,[3499,5780,4076],{"class":3505},[3499,5782,5783],{"class":3509}," a + b\n",[3499,5785,5787],{"class":3501,"line":5786},19,[3499,5788,3523],{"emptyLinePlaceholder":3522},[3499,5790,5792],{"class":3501,"line":5791},20,[3499,5793,5794],{"class":3529},"# Статичний аналізатор тепер знає точні типи аргументів:\n",[3499,5796,5798,5801,5803,5805,5807,5810],{"class":3501,"line":5797},21,[3499,5799,5800],{"class":3509},"calculate_sum(",[3499,5802,4863],{"class":3771},[3499,5804,3468],{"class":3509},[3499,5806,4868],{"class":3771},[3499,5808,5809],{"class":3509},")  ",[3499,5811,5262],{"class":3529},[3499,5813,5815],{"class":3501,"line":5814},22,[3499,5816,5817],{"class":3529},"# calculate_sum(\"10\", 20)  # Помилка типізації!\n",[3452,5819],{},[3474,5821,5823,5824,5827],{"id":5822},"анотування-метаданих-типу-annotatedt-metadata-pep-593","Анотування метаданих типу: ",[3403,5825,5826],{},"Annotated[T, metadata]"," (PEP 593)",[3394,5829,5830,5831,3418],{},"Однією з найважливіших інновацій системи типізації Python, яка стала фундаментом для сучасного дизайну FastAPI та Pydantic v2, є ",[3403,5832,5833],{},"typing.Annotated",[3394,5835,5836,5837,5839,5840,5843,5844,5846],{},"Концептуально ",[3403,5838,5826],{}," дозволяє розширити будь-який існуючий тип ",[3403,5841,5842],{},"T"," довільними runtime-метаданими, які ігноруються статичним лінтером (лінтер бачить лише тип ",[3403,5845,5842],{},"), но можуть бути зчитані фреймворком під час виконання.",[3741,5848,5850],{"id":5849},"чому-це-важливо-порівняння-з-атрибутами-в-c","Чому це важливо: порівняння з атрибутами в C#",[3394,5852,5853],{},"У C# для додавання метаданих до параметрів методів або властивостей класів використовуються атрибути:",[3489,5855,5860],{"className":5856,"code":5857,"filename":5858,"language":5859,"meta":3495,"style":3495},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","public IActionResult GetUser([FromRoute][Range(1, 100)] int id) { ... }\n","Program.cs","csharp",[3403,5861,5862],{"__ignoreMap":3495},[3499,5863,5864,5867,5870,5873,5876,5879,5882,5885,5887,5889,5891,5894,5897,5899,5902],{"class":3501,"line":3502},[3499,5865,5866],{"class":3536},"public",[3499,5868,5869],{"class":3554}," IActionResult",[3499,5871,5872],{"class":3540}," GetUser",[3499,5874,5875],{"class":3509},"([",[3499,5877,5878],{"class":3554},"FromRoute",[3499,5880,5881],{"class":3509},"][",[3499,5883,5884],{"class":3554},"Range",[3499,5886,3544],{"class":3509},[3499,5888,4783],{"class":3771},[3499,5890,3468],{"class":3509},[3499,5892,5893],{"class":3771},"100",[3499,5895,5896],{"class":3509},")] ",[3499,5898,3555],{"class":3536},[3499,5900,5901],{"class":3547}," id",[3499,5903,5904],{"class":3509},") { ... }\n",[3394,5906,5907,5908,5911],{},"В Python довгий час не було нативного способу зробити це елегантно. ",[3403,5909,5910],{},"Annotated"," вирішує цю проблему:",[3489,5913,5915],{"className":3491,"code":5914,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import Annotated\nfrom pydantic import Field\n\n# Записуємо еквівалент для FastAPI \u002F Pydantic:\nUserId = Annotated[int, Field(ge=1, le=100)]\n",[3403,5916,5917,5928,5940,5944,5949],{"__ignoreMap":3495},[3499,5918,5919,5921,5923,5925],{"class":3501,"line":3502},[3499,5920,3506],{"class":3505},[3499,5922,3510],{"class":3509},[3499,5924,3513],{"class":3505},[3499,5926,5927],{"class":3509}," Annotated\n",[3499,5929,5930,5932,5935,5937],{"class":3501,"line":3519},[3499,5931,3506],{"class":3505},[3499,5933,5934],{"class":3509}," pydantic ",[3499,5936,3513],{"class":3505},[3499,5938,5939],{"class":3509}," Field\n",[3499,5941,5942],{"class":3501,"line":3526},[3499,5943,3523],{"emptyLinePlaceholder":3522},[3499,5945,5946],{"class":3501,"line":3533},[3499,5947,5948],{"class":3529},"# Записуємо еквівалент для FastAPI \u002F Pydantic:\n",[3499,5950,5951,5954,5956,5959,5962,5965,5967,5969,5972,5974,5976],{"class":3501,"line":3571},[3499,5952,5953],{"class":3509},"UserId = Annotated[",[3499,5955,3555],{"class":3554},[3499,5957,5958],{"class":3509},", Field(",[3499,5960,5961],{"class":3547},"ge",[3499,5963,5964],{"class":3509},"=",[3499,5966,4783],{"class":3771},[3499,5968,3468],{"class":3509},[3499,5970,5971],{"class":3547},"le",[3499,5973,5964],{"class":3509},[3499,5975,5893],{"class":3771},[3499,5977,5978],{"class":3509},")]\n",[3741,5980,5982],{"id":5981},"синтаксис-та-застосування","Синтаксис та застосування",[3394,5984,5985,5986,5988],{},"Метаданих у ",[3403,5987,5910],{}," може бути кілька. Вони передаються як наступні аргументи після основного типу:",[3489,5990,5992],{"className":3491,"code":5991,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import Annotated\n\n# Тип: ціле число, яке додатково містить опис та правила валідації\ntype DatabasePort = Annotated[int, \"Порт підключення до БД\", {\"min_value\": 1024, \"max_value\": 65535}]\n",[3403,5993,5994,6004,6008,6013],{"__ignoreMap":3495},[3499,5995,5996,5998,6000,6002],{"class":3501,"line":3502},[3499,5997,3506],{"class":3505},[3499,5999,3510],{"class":3509},[3499,6001,3513],{"class":3505},[3499,6003,5927],{"class":3509},[3499,6005,6006],{"class":3501,"line":3519},[3499,6007,3523],{"emptyLinePlaceholder":3522},[3499,6009,6010],{"class":3501,"line":3526},[3499,6011,6012],{"class":3529},"# Тип: ціле число, яке додатково містить опис та правила валідації\n",[3499,6014,6015,6017,6020,6022,6024,6027,6030,6033,6035,6038,6040,6043,6045,6048],{"class":3501,"line":3533},[3499,6016,3654],{"class":3554},[3499,6018,6019],{"class":3509}," DatabasePort = Annotated[",[3499,6021,3555],{"class":3554},[3499,6023,3468],{"class":3509},[3499,6025,6026],{"class":3979},"\"Порт підключення до БД\"",[3499,6028,6029],{"class":3509},", {",[3499,6031,6032],{"class":3979},"\"min_value\"",[3499,6034,3609],{"class":3509},[3499,6036,6037],{"class":3771},"1024",[3499,6039,3468],{"class":3509},[3499,6041,6042],{"class":3979},"\"max_value\"",[3499,6044,3609],{"class":3509},[3499,6046,6047],{"class":3771},"65535",[3499,6049,6050],{"class":3509},"}]\n",[3452,6052],{},[3474,6054,6056,6057,3468,6060,4140,6063],{"id":6055},"звуження-типів-та-перевантаження-typeguard-typeis-та-overload","Звуження типів та перевантаження: ",[3403,6058,6059],{},"TypeGuard",[3403,6061,6062],{},"TypeIs",[3403,6064,6065],{},"overload",[3394,6067,6068],{},"При роботі зі складними типами даних часто виникає потреба підказати лінтеру, що після виконання певної перевірки тип змінної змінився (звузився).",[3741,6070,6072,6073,6075,6076,6078],{"id":6071},"_1-typeguard-pep-647-vs-typeis-pep-742","1. ",[3403,6074,6059],{}," (PEP 647) vs ",[3403,6077,6062],{}," (PEP 742)",[3394,6080,6081,6083,6084,6087,6088,6090,6091,6093,6094,6097,6098,6101,6102,3418],{},[3403,6082,6059],{}," дозволяє створювати функції-предикати, які повідомляють лінтеру, що аргумент належить до певного типу, якщо функція повертає ",[3403,6085,6086],{},"True",".\nПроте ",[3403,6089,6059],{}," мав недолік: він звужував тип лише для позитивного сценарію (",[3403,6092,6086],{},"), але не вмів автоматично відкидати цей тип для негативного сценарію (",[3403,6095,6096],{},"False","). Тому в Python 3.13 (але доступний через ",[3403,6099,6100],{},"typing_extensions"," і раніше) з'явився ",[3403,6103,6062],{},[3486,6105,6106,6262],{},[3489,6107,6110],{"className":3491,"code":6108,"filename":6109,"language":3494,"meta":3495,"style":3495},"from typing import TypeGuard\n\ndef is_string_list(val: list[object]) -> TypeGuard[list[str]]:\n    # Якщо повертає True, лінтер вважає, що це list[str]\n    return all(isinstance(x, str) for x in val)\n\nitems: list[object] = [\"a\", \"b\", 1]\n\nif is_string_list(items):\n    # Тут items має тип list[str]\n    print(items[0].upper())\nelse:\n    # Тут items залишається list[object] (лінтер не знає, що там немає рядків)\n    pass\n","Використання TypeGuard",[3403,6111,6112,6123,6127,6151,6156,6188,6192,6215,6219,6227,6232,6246,6253,6258],{"__ignoreMap":3495},[3499,6113,6114,6116,6118,6120],{"class":3501,"line":3502},[3499,6115,3506],{"class":3505},[3499,6117,3510],{"class":3509},[3499,6119,3513],{"class":3505},[3499,6121,6122],{"class":3509}," TypeGuard\n",[3499,6124,6125],{"class":3501,"line":3519},[3499,6126,3523],{"emptyLinePlaceholder":3522},[3499,6128,6129,6131,6134,6136,6139,6142,6144,6147,6149],{"class":3501,"line":3526},[3499,6130,3537],{"class":3536},[3499,6132,6133],{"class":3540}," is_string_list",[3499,6135,3544],{"class":3509},[3499,6137,6138],{"class":3547},"val",[3499,6140,6141],{"class":3509},": list[",[3499,6143,4952],{"class":3554},[3499,6145,6146],{"class":3509},"]) -> TypeGuard[list[",[3499,6148,3560],{"class":3554},[3499,6150,3568],{"class":3509},[3499,6152,6153],{"class":3501,"line":3533},[3499,6154,6155],{"class":3529},"    # Якщо повертає True, лінтер вважає, що це list[str]\n",[3499,6157,6158,6160,6163,6165,6168,6171,6173,6176,6179,6182,6185],{"class":3501,"line":3571},[3499,6159,4076],{"class":3505},[3499,6161,6162],{"class":3540}," all",[3499,6164,3544],{"class":3509},[3499,6166,6167],{"class":3540},"isinstance",[3499,6169,6170],{"class":3509},"(x, ",[3499,6172,3560],{"class":3554},[3499,6174,6175],{"class":3509},") ",[3499,6177,6178],{"class":3505},"for",[3499,6180,6181],{"class":3509}," x ",[3499,6183,6184],{"class":3505},"in",[3499,6186,6187],{"class":3509}," val)\n",[3499,6189,6190],{"class":3501,"line":3577},[3499,6191,3523],{"emptyLinePlaceholder":3522},[3499,6193,6194,6197,6199,6201,6204,6206,6209,6211,6213],{"class":3501,"line":3705},[3499,6195,6196],{"class":3509},"items: list[",[3499,6198,4952],{"class":3554},[3499,6200,4780],{"class":3509},[3499,6202,6203],{"class":3979},"\"a\"",[3499,6205,3468],{"class":3509},[3499,6207,6208],{"class":3979},"\"b\"",[3499,6210,3468],{"class":3509},[3499,6212,4783],{"class":3771},[3499,6214,3676],{"class":3509},[3499,6216,6217],{"class":3501,"line":3800},[3499,6218,3523],{"emptyLinePlaceholder":3522},[3499,6220,6221,6224],{"class":3501,"line":3812},[3499,6222,6223],{"class":3505},"if",[3499,6225,6226],{"class":3509}," is_string_list(items):\n",[3499,6228,6229],{"class":3501,"line":3826},[3499,6230,6231],{"class":3529},"    # Тут items має тип list[str]\n",[3499,6233,6234,6237,6240,6243],{"class":3501,"line":3985},[3499,6235,6236],{"class":3540},"    print",[3499,6238,6239],{"class":3509},"(items[",[3499,6241,6242],{"class":3771},"0",[3499,6244,6245],{"class":3509},"].upper())\n",[3499,6247,6248,6251],{"class":3501,"line":3991},[3499,6249,6250],{"class":3505},"else",[3499,6252,3629],{"class":3509},[3499,6254,6255],{"class":3501,"line":3997},[3499,6256,6257],{"class":3529},"    # Тут items залишається list[object] (лінтер не знає, що там немає рядків)\n",[3499,6259,6260],{"class":3501,"line":4006},[3499,6261,3580],{"class":3505},[3489,6263,6266],{"className":3491,"code":6264,"filename":6265,"language":3494,"meta":3495,"style":3495},"from typing import TypeIs\n\ndef is_str(val: object) -> TypeIs[str]:\n    return isinstance(val, str)\n\nx: str | int = \"hello\"\n\nif is_str(x):\n    # Тут x має тип str\n    reveal_type(x)  # Revealed type is \"str\"\nelse:\n    # Лінтер розуміє негативний сценарій: якщо це не str, то це обов'язково int!\n    reveal_type(x)  # Revealed type is \"int\"\n","Використання TypeIs (PEP 742)",[3403,6267,6268,6279,6283,6306,6319,6323,6339,6343,6350,6355,6363,6369,6374],{"__ignoreMap":3495},[3499,6269,6270,6272,6274,6276],{"class":3501,"line":3502},[3499,6271,3506],{"class":3505},[3499,6273,3510],{"class":3509},[3499,6275,3513],{"class":3505},[3499,6277,6278],{"class":3509}," TypeIs\n",[3499,6280,6281],{"class":3501,"line":3519},[3499,6282,3523],{"emptyLinePlaceholder":3522},[3499,6284,6285,6287,6290,6292,6294,6296,6298,6301,6303],{"class":3501,"line":3526},[3499,6286,3537],{"class":3536},[3499,6288,6289],{"class":3540}," is_str",[3499,6291,3544],{"class":3509},[3499,6293,6138],{"class":3547},[3499,6295,3609],{"class":3509},[3499,6297,4952],{"class":3554},[3499,6299,6300],{"class":3509},") -> TypeIs[",[3499,6302,3560],{"class":3554},[3499,6304,6305],{"class":3509},"]:\n",[3499,6307,6308,6310,6312,6315,6317],{"class":3501,"line":3533},[3499,6309,4076],{"class":3505},[3499,6311,5070],{"class":3540},[3499,6313,6314],{"class":3509},"(val, ",[3499,6316,3560],{"class":3554},[3499,6318,4876],{"class":3509},[3499,6320,6321],{"class":3501,"line":3571},[3499,6322,3523],{"emptyLinePlaceholder":3522},[3499,6324,6325,6328,6330,6332,6334,6336],{"class":3501,"line":3577},[3499,6326,6327],{"class":3509},"x: ",[3499,6329,3560],{"class":3554},[3499,6331,3614],{"class":3509},[3499,6333,3555],{"class":3554},[3499,6335,3768],{"class":3509},[3499,6337,6338],{"class":3979},"\"hello\"\n",[3499,6340,6341],{"class":3501,"line":3705},[3499,6342,3523],{"emptyLinePlaceholder":3522},[3499,6344,6345,6347],{"class":3501,"line":3800},[3499,6346,6223],{"class":3505},[3499,6348,6349],{"class":3509}," is_str(x):\n",[3499,6351,6352],{"class":3501,"line":3812},[3499,6353,6354],{"class":3529},"    # Тут x має тип str\n",[3499,6356,6357,6360],{"class":3501,"line":3826},[3499,6358,6359],{"class":3509},"    reveal_type(x)  ",[3499,6361,6362],{"class":3529},"# Revealed type is \"str\"\n",[3499,6364,6365,6367],{"class":3501,"line":3985},[3499,6366,6250],{"class":3505},[3499,6368,3629],{"class":3509},[3499,6370,6371],{"class":3501,"line":3991},[3499,6372,6373],{"class":3529},"    # Лінтер розуміє негативний сценарій: якщо це не str, то це обов'язково int!\n",[3499,6375,6376,6378],{"class":3501,"line":3997},[3499,6377,6359],{"class":3509},[3499,6379,6380],{"class":3529},"# Revealed type is \"int\"\n",[3741,6382,6384,6385,6388],{"id":6383},"_2-перевантаження-функцій-через-overload-pep-484","2. Перевантаження функцій через ",[3403,6386,6387],{},"@overload"," (PEP 484)",[3394,6390,6391],{},"У C# перевантаження методів є нативним механізмом: ви можете написати кілька методів з однаковим ім'ям, але різними параметрами. В Python ви не можете визначити дві функції з однаковим ім'ям у тій самій області видимості — остання просто перезапише попередню.",[3394,6393,6394,6395,6397],{},"Для типізації сценаріїв, коли функція може приймати різні комбінації типів аргументів та повертати відповідні типи, використовується декоратор ",[3403,6396,6387],{},". Самі декоратори слугують лише для лінтера, а реальна runtime-функція (без декоратора) має однаково обробляти всі варіанти.",[3489,6399,6401],{"className":3491,"code":6400,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import overload\n\n# 1. Специфікація для випадку, коли на вхід іде int\n@overload\ndef double(value: int) -> int: ...\n\n# 2. Специфікація для випадку, коли на вхід іде str\n@overload\ndef double(value: str) -> str: ...\n\n# 3. Реальна runtime-реалізація (без @overload)\ndef double(value: int | str) -> int | str:\n    if isinstance(value, int):\n        return value * 2\n    elif isinstance(value, str):\n        return value + value\n    raise TypeError(\"Непідтримуваний тип\")\n\n# Статичний аналізатор тепер знає точний тип повернення:\nresult_int = double(5)      # Лінтер знає, що result_int: int\nresult_str = double(\"abc\")  # Лінтер знає, що result_str: str\n",[3403,6402,6403,6414,6418,6423,6428,6450,6454,6459,6463,6483,6487,6492,6520,6533,6543,6556,6563,6578,6582,6587,6600],{"__ignoreMap":3495},[3499,6404,6405,6407,6409,6411],{"class":3501,"line":3502},[3499,6406,3506],{"class":3505},[3499,6408,3510],{"class":3509},[3499,6410,3513],{"class":3505},[3499,6412,6413],{"class":3509}," overload\n",[3499,6415,6416],{"class":3501,"line":3519},[3499,6417,3523],{"emptyLinePlaceholder":3522},[3499,6419,6420],{"class":3501,"line":3526},[3499,6421,6422],{"class":3529},"# 1. Специфікація для випадку, коли на вхід іде int\n",[3499,6424,6425],{"class":3501,"line":3533},[3499,6426,6427],{"class":3540},"@overload\n",[3499,6429,6430,6432,6435,6437,6439,6441,6443,6445,6447],{"class":3501,"line":3571},[3499,6431,3537],{"class":3536},[3499,6433,6434],{"class":3540}," double",[3499,6436,3544],{"class":3509},[3499,6438,5158],{"class":3547},[3499,6440,3609],{"class":3509},[3499,6442,3555],{"class":3554},[3499,6444,3887],{"class":3509},[3499,6446,3555],{"class":3554},[3499,6448,6449],{"class":3509},": ...\n",[3499,6451,6452],{"class":3501,"line":3577},[3499,6453,3523],{"emptyLinePlaceholder":3522},[3499,6455,6456],{"class":3501,"line":3705},[3499,6457,6458],{"class":3529},"# 2. Специфікація для випадку, коли на вхід іде str\n",[3499,6460,6461],{"class":3501,"line":3800},[3499,6462,6427],{"class":3540},[3499,6464,6465,6467,6469,6471,6473,6475,6477,6479,6481],{"class":3501,"line":3812},[3499,6466,3537],{"class":3536},[3499,6468,6434],{"class":3540},[3499,6470,3544],{"class":3509},[3499,6472,5158],{"class":3547},[3499,6474,3609],{"class":3509},[3499,6476,3560],{"class":3554},[3499,6478,3887],{"class":3509},[3499,6480,3560],{"class":3554},[3499,6482,6449],{"class":3509},[3499,6484,6485],{"class":3501,"line":3826},[3499,6486,3523],{"emptyLinePlaceholder":3522},[3499,6488,6489],{"class":3501,"line":3985},[3499,6490,6491],{"class":3529},"# 3. Реальна runtime-реалізація (без @overload)\n",[3499,6493,6494,6496,6498,6500,6502,6504,6506,6508,6510,6512,6514,6516,6518],{"class":3501,"line":3991},[3499,6495,3537],{"class":3536},[3499,6497,6434],{"class":3540},[3499,6499,3544],{"class":3509},[3499,6501,5158],{"class":3547},[3499,6503,3609],{"class":3509},[3499,6505,3555],{"class":3554},[3499,6507,3614],{"class":3509},[3499,6509,3560],{"class":3554},[3499,6511,3887],{"class":3509},[3499,6513,3555],{"class":3554},[3499,6515,3614],{"class":3509},[3499,6517,3560],{"class":3554},[3499,6519,3629],{"class":3509},[3499,6521,6522,6524,6526,6529,6531],{"class":3501,"line":3997},[3499,6523,5067],{"class":3505},[3499,6525,5070],{"class":3540},[3499,6527,6528],{"class":3509},"(value, ",[3499,6530,3555],{"class":3554},[3499,6532,4393],{"class":3509},[3499,6534,6535,6537,6540],{"class":3501,"line":4006},[3499,6536,3939],{"class":3505},[3499,6538,6539],{"class":3509}," value * ",[3499,6541,6542],{"class":3771},"2\n",[3499,6544,6545,6548,6550,6552,6554],{"class":3501,"line":4593},[3499,6546,6547],{"class":3505},"    elif",[3499,6549,5070],{"class":3540},[3499,6551,6528],{"class":3509},[3499,6553,3560],{"class":3554},[3499,6555,4393],{"class":3509},[3499,6557,6558,6560],{"class":3501,"line":4598},[3499,6559,3939],{"class":3505},[3499,6561,6562],{"class":3509}," value + value\n",[3499,6564,6565,6568,6571,6573,6576],{"class":3501,"line":4604},[3499,6566,6567],{"class":3505},"    raise",[3499,6569,6570],{"class":3554}," TypeError",[3499,6572,3544],{"class":3509},[3499,6574,6575],{"class":3979},"\"Непідтримуваний тип\"",[3499,6577,4876],{"class":3509},[3499,6579,6580],{"class":3501,"line":5778},[3499,6581,3523],{"emptyLinePlaceholder":3522},[3499,6583,6584],{"class":3501,"line":5786},[3499,6585,6586],{"class":3529},"# Статичний аналізатор тепер знає точний тип повернення:\n",[3499,6588,6589,6592,6594,6597],{"class":3501,"line":5791},[3499,6590,6591],{"class":3509},"result_int = double(",[3499,6593,4915],{"class":3771},[3499,6595,6596],{"class":3509},")      ",[3499,6598,6599],{"class":3529},"# Лінтер знає, що result_int: int\n",[3499,6601,6602,6605,6608,6610],{"class":3501,"line":5797},[3499,6603,6604],{"class":3509},"result_str = double(",[3499,6606,6607],{"class":3979},"\"abc\"",[3499,6609,5809],{"class":3509},[3499,6611,6612],{"class":3529},"# Лінтер знає, що result_str: str\n",[3452,6614],{},[3474,6616,6618,6619,6621],{"id":6617},"налаштування-статичного-аналізу-mypy-у-cicd","Налаштування статичного аналізу: ",[3403,6620,3467],{}," у CI\u002FCD",[3394,6623,6624,6625,3418],{},"Вся потужність системи типізації розкривається лише за умови використання лінтерів. Стандартом де-факто є інструмент ",[3415,6626,3467],{},[3394,6628,6629,6630,6633,6634,6636,6637,6639],{},"Для великих проєктів критично важливо налаштувати mypy у строгому режимі (",[3403,6631,6632],{},"strict","), який забороняє неявний тип ",[3403,6635,4933],{},", вимагає анотацій для всіх функцій та перевіряє безпеку роботи з ",[3403,6638,3483],{}," (аналог nullable reference types у C#).",[3394,6641,6642,6643,6645,6646,6649],{},"Конфігурація ",[3403,6644,3467],{}," зазвичай зберігається у файлі ",[3403,6647,6648],{},"pyproject.toml",":",[3489,6651,6656],{"className":6652,"code":6653,"filename":6654,"language":6655,"meta":3495,"style":3495},"language-toml shiki shiki-themes light-plus dark-plus dark-plus","[tool.mypy]\npython_version = \"3.12\"\nstrict = true\nwarn_unused_configs = true\nwarn_redundant_casts = true\nwarn_unused_ignores = true\nshow_error_codes = true\npretty = true\n\n# Якщо якась бібліотека не має типів, можна вимкнути помилки для неї:\n[[tool.mypy.overrides]]\nmodule = \"third_party_lib.*\"\nignore_missing_imports = true\n","config.toml","toml",[3403,6657,6658,6663,6673,6682,6691,6700,6709,6718,6727,6731,6736,6741,6751],{"__ignoreMap":3495},[3499,6659,6660],{"class":3501,"line":3502},[3499,6661,6662],{"class":3509},"[tool.mypy]\n",[3499,6664,6665,6668,6670],{"class":3501,"line":3519},[3499,6666,6667],{"class":3547},"python_version",[3499,6669,3768],{"class":3509},[3499,6671,6672],{"class":3979},"\"3.12\"\n",[3499,6674,6675,6677,6679],{"class":3501,"line":3526},[3499,6676,6632],{"class":3547},[3499,6678,3768],{"class":3509},[3499,6680,6681],{"class":3536},"true\n",[3499,6683,6684,6687,6689],{"class":3501,"line":3533},[3499,6685,6686],{"class":3547},"warn_unused_configs",[3499,6688,3768],{"class":3509},[3499,6690,6681],{"class":3536},[3499,6692,6693,6696,6698],{"class":3501,"line":3571},[3499,6694,6695],{"class":3547},"warn_redundant_casts",[3499,6697,3768],{"class":3509},[3499,6699,6681],{"class":3536},[3499,6701,6702,6705,6707],{"class":3501,"line":3577},[3499,6703,6704],{"class":3547},"warn_unused_ignores",[3499,6706,3768],{"class":3509},[3499,6708,6681],{"class":3536},[3499,6710,6711,6714,6716],{"class":3501,"line":3705},[3499,6712,6713],{"class":3547},"show_error_codes",[3499,6715,3768],{"class":3509},[3499,6717,6681],{"class":3536},[3499,6719,6720,6723,6725],{"class":3501,"line":3800},[3499,6721,6722],{"class":3547},"pretty",[3499,6724,3768],{"class":3509},[3499,6726,6681],{"class":3536},[3499,6728,6729],{"class":3501,"line":3812},[3499,6730,3523],{"emptyLinePlaceholder":3522},[3499,6732,6733],{"class":3501,"line":3826},[3499,6734,6735],{"class":3529},"# Якщо якась бібліотека не має типів, можна вимкнути помилки для неї:\n",[3499,6737,6738],{"class":3501,"line":3985},[3499,6739,6740],{"class":3509},"[[tool.mypy.overrides]]\n",[3499,6742,6743,6746,6748],{"class":3501,"line":3991},[3499,6744,6745],{"class":3547},"module",[3499,6747,3768],{"class":3509},[3499,6749,6750],{"class":3979},"\"third_party_lib.*\"\n",[3499,6752,6753,6756,6758],{"class":3501,"line":3997},[3499,6754,6755],{"class":3547},"ignore_missing_imports",[3499,6757,3768],{"class":3509},[3499,6759,6681],{"class":3536},[3394,6761,6762],{},"Команда для запуску локально або в CI pipeline:",[3489,6764,6769],{"className":6765,"code":6766,"filename":6767,"language":6768,"meta":3495,"style":3495},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","mypy app\u002F\n","terminal","bash",[3403,6770,6771],{"__ignoreMap":3495},[3499,6772,6773,6775],{"class":3501,"line":3502},[3499,6774,3467],{"class":3540},[3499,6776,6777],{"class":3979}," app\u002F\n",[3452,6779],{},[3389,6781,6783],{"id":6782},"частина-ii-pydantic-v2-парсинг-та-валідація-даних","Частина II: Pydantic v2 — парсинг та валідація даних",[3394,6785,6786,6787,6790,6791,3418],{},"Якщо перша частина нашої статті була присвячена тому, як описати наміри типів для статичного аналізатора, то в цій частині ми переходимо до того, як змусити ці типи працювати ",[3415,6788,6789],{},"під час виконання (runtime)",". Основним інструментом для цього в сучасному Python є бібліотека ",[3415,6792,6793],{},"Pydantic v2",[3474,6795,6797],{"id":6796},"філософія-parse-dont-validate","Філософія «Parse, don't validate»",[3394,6799,6800,6801,3840,6804,6807],{},"Більшість систем валідації в інших екосистемах (наприклад, FluentValidation або Data Annotations в C#) працюють за принципом простої перевірки: вони отримують об'єкт, перевіряють, чи відповідає він набору правил (чи заповнені обов'язкові поля, чи пошта має коректний формат), і повертають результат: ",[3403,6802,6803],{},"true",[3403,6805,6806],{},"false"," (з переліком помилок).",[3394,6809,6810,6811,6814],{},"Pydantic працює інакше, сповідуючи філософію ",[3415,6812,6813],{},"«Parse, don't validate» (Парси, а не просто валідуй)",", яка прийшла з функціонального програмування.",[4697,6816,6817,6823],{},[3430,6818,6819,6822],{},[3415,6820,6821],{},"Валідація"," — це відповідь на запитання: «Чи є ці дані коректними?».",[3430,6824,6825,6828,6829,6832],{},[3415,6826,6827],{},"Парсинг"," — це процес прийому неструктурованих даних (наприклад, JSON-рядка чи HTTP Query параметрів), їх перевірки та ",[3415,6830,6831],{},"перетворення"," в об'єкти з чітко визначеними типами та структурою.",[3394,6834,6835,6836,6839],{},"Коли Pydantic обробляє вхідні дані, він не просто каже, чи вони правильні. Він ",[3415,6837,6838],{},"гарантує",", що на виході ви отримаєте об'єкт, типи полів якого стовідсотково відповідають анотаціям. Якщо вхідні дані можна безпечно привести до потрібного типу, Pydantic зробить це автоматично (Coercion):",[3489,6841,6843],{"className":3491,"code":6842,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import BaseModel\n\nclass User(BaseModel):\n    id: int\n    is_active: bool\n\n# Передаємо дані з невідповідними типами, але які можна сконвертувати:\nuser = User(id=\"123\", is_active=\"true\")\n\n# Pydantic успішно спарсив та привів типи:\nprint(user.id)         # Виведе: 123 (тип int, а не str)\nprint(user.is_active)  # Виведе: True (тип bool, а не str)\n",[3403,6844,6845,6856,6860,6873,6883,6891,6895,6900,6925,6929,6934,6944],{"__ignoreMap":3495},[3499,6846,6847,6849,6851,6853],{"class":3501,"line":3502},[3499,6848,3506],{"class":3505},[3499,6850,5934],{"class":3509},[3499,6852,3513],{"class":3505},[3499,6854,6855],{"class":3509}," BaseModel\n",[3499,6857,6858],{"class":3501,"line":3519},[3499,6859,3523],{"emptyLinePlaceholder":3522},[3499,6861,6862,6864,6866,6868,6871],{"class":3501,"line":3526},[3499,6863,3858],{"class":3536},[3499,6865,4238],{"class":3554},[3499,6867,3544],{"class":3509},[3499,6869,6870],{"class":3554},"BaseModel",[3499,6872,4393],{"class":3509},[3499,6874,6875,6878,6880],{"class":3501,"line":3533},[3499,6876,6877],{"class":3540},"    id",[3499,6879,3609],{"class":3509},[3499,6881,6882],{"class":3554},"int\n",[3499,6884,6885,6888],{"class":3501,"line":3571},[3499,6886,6887],{"class":3509},"    is_active: ",[3499,6889,6890],{"class":3554},"bool\n",[3499,6892,6893],{"class":3501,"line":3577},[3499,6894,3523],{"emptyLinePlaceholder":3522},[3499,6896,6897],{"class":3501,"line":3705},[3499,6898,6899],{"class":3529},"# Передаємо дані з невідповідними типами, але які можна сконвертувати:\n",[3499,6901,6902,6905,6908,6910,6913,6915,6918,6920,6923],{"class":3501,"line":3800},[3499,6903,6904],{"class":3509},"user = User(",[3499,6906,6907],{"class":3547},"id",[3499,6909,5964],{"class":3509},[3499,6911,6912],{"class":3979},"\"123\"",[3499,6914,3468],{"class":3509},[3499,6916,6917],{"class":3547},"is_active",[3499,6919,5964],{"class":3509},[3499,6921,6922],{"class":3979},"\"true\"",[3499,6924,4876],{"class":3509},[3499,6926,6927],{"class":3501,"line":3812},[3499,6928,3523],{"emptyLinePlaceholder":3522},[3499,6930,6931],{"class":3501,"line":3826},[3499,6932,6933],{"class":3529},"# Pydantic успішно спарсив та привів типи:\n",[3499,6935,6936,6938,6941],{"class":3501,"line":3985},[3499,6937,4111],{"class":3540},[3499,6939,6940],{"class":3509},"(user.id)         ",[3499,6942,6943],{"class":3529},"# Виведе: 123 (тип int, а не str)\n",[3499,6945,6946,6948,6951],{"class":3501,"line":3991},[3499,6947,4111],{"class":3540},[3499,6949,6950],{"class":3509},"(user.is_active)  ",[3499,6952,6953],{"class":3529},"# Виведе: True (тип bool, а не str)\n",[3486,6955,6956,7031,7102],{},[3489,6957,6960],{"className":6765,"code":6958,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Створюємо віртуальне середовище\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate  # Для Linux\u002FmacOS\n# .venv\\Scripts\\activate   # Для Windows\n\n# Встановлюємо Pydantic\npip install pydantic\n\n# Запускаємо скрипт (припустимо, код збережено у файл main.py)\npython main.py\n","pip",[3403,6961,6962,6967,6980,6991,6996,7000,7005,7015,7019,7024],{"__ignoreMap":3495},[3499,6963,6964],{"class":3501,"line":3502},[3499,6965,6966],{"class":3529},"# Створюємо віртуальне середовище\n",[3499,6968,6969,6971,6974,6977],{"class":3501,"line":3519},[3499,6970,3494],{"class":3540},[3499,6972,6973],{"class":3536}," -m",[3499,6975,6976],{"class":3979}," venv",[3499,6978,6979],{"class":3979}," .venv\n",[3499,6981,6982,6985,6988],{"class":3501,"line":3526},[3499,6983,6984],{"class":3540},"source",[3499,6986,6987],{"class":3979}," .venv\u002Fbin\u002Factivate",[3499,6989,6990],{"class":3529},"  # Для Linux\u002FmacOS\n",[3499,6992,6993],{"class":3501,"line":3533},[3499,6994,6995],{"class":3529},"# .venv\\Scripts\\activate   # Для Windows\n",[3499,6997,6998],{"class":3501,"line":3571},[3499,6999,3523],{"emptyLinePlaceholder":3522},[3499,7001,7002],{"class":3501,"line":3577},[3499,7003,7004],{"class":3529},"# Встановлюємо Pydantic\n",[3499,7006,7007,7009,7012],{"class":3501,"line":3705},[3499,7008,6959],{"class":3540},[3499,7010,7011],{"class":3979}," install",[3499,7013,7014],{"class":3979}," pydantic\n",[3499,7016,7017],{"class":3501,"line":3800},[3499,7018,3523],{"emptyLinePlaceholder":3522},[3499,7020,7021],{"class":3501,"line":3812},[3499,7022,7023],{"class":3529},"# Запускаємо скрипт (припустимо, код збережено у файл main.py)\n",[3499,7025,7026,7028],{"class":3501,"line":3826},[3499,7027,3494],{"class":3540},[3499,7029,7030],{"class":3979}," main.py\n",[3489,7032,7035],{"className":6765,"code":7033,"filename":7034,"language":6768,"meta":3495,"style":3495},"# uv — надшвидкий інструмент для керування залежностями (рекомендовано)\n# Миттєвий запуск скрипту з автоматичним встановленням залежностей у тимчасове середовище:\nuv run --with pydantic main.py\n\n# Або класичний варіант через створення віртуального середовища:\nuv venv\nsource .venv\u002Fbin\u002Factivate\nuv pip install pydantic\npython main.py\n","uv",[3403,7036,7037,7042,7047,7062,7066,7071,7078,7085,7096],{"__ignoreMap":3495},[3499,7038,7039],{"class":3501,"line":3502},[3499,7040,7041],{"class":3529},"# uv — надшвидкий інструмент для керування залежностями (рекомендовано)\n",[3499,7043,7044],{"class":3501,"line":3519},[3499,7045,7046],{"class":3529},"# Миттєвий запуск скрипту з автоматичним встановленням залежностей у тимчасове середовище:\n",[3499,7048,7049,7051,7054,7057,7060],{"class":3501,"line":3526},[3499,7050,7034],{"class":3540},[3499,7052,7053],{"class":3979}," run",[3499,7055,7056],{"class":3536}," --with",[3499,7058,7059],{"class":3979}," pydantic",[3499,7061,7030],{"class":3979},[3499,7063,7064],{"class":3501,"line":3533},[3499,7065,3523],{"emptyLinePlaceholder":3522},[3499,7067,7068],{"class":3501,"line":3571},[3499,7069,7070],{"class":3529},"# Або класичний варіант через створення віртуального середовища:\n",[3499,7072,7073,7075],{"class":3501,"line":3577},[3499,7074,7034],{"class":3540},[3499,7076,7077],{"class":3979}," venv\n",[3499,7079,7080,7082],{"class":3501,"line":3705},[3499,7081,6984],{"class":3540},[3499,7083,7084],{"class":3979}," .venv\u002Fbin\u002Factivate\n",[3499,7086,7087,7089,7092,7094],{"class":3501,"line":3800},[3499,7088,7034],{"class":3540},[3499,7090,7091],{"class":3979}," pip",[3499,7093,7011],{"class":3979},[3499,7095,7014],{"class":3979},[3499,7097,7098,7100],{"class":3501,"line":3812},[3499,7099,3494],{"class":3540},[3499,7101,7030],{"class":3979},[3489,7103,7106],{"className":6765,"code":7104,"filename":7105,"language":6768,"meta":3495,"style":3495},"# Ініціалізуємо проєкт (якщо ще не створено)\npoetry init -n\n\n# Додаємо залежність Pydantic\npoetry add pydantic\n\n# Запускаємо скрипт у віртуальному середовищі Poetry\npoetry run python main.py\n","poetry",[3403,7107,7108,7113,7123,7127,7132,7140,7144,7149],{"__ignoreMap":3495},[3499,7109,7110],{"class":3501,"line":3502},[3499,7111,7112],{"class":3529},"# Ініціалізуємо проєкт (якщо ще не створено)\n",[3499,7114,7115,7117,7120],{"class":3501,"line":3519},[3499,7116,7105],{"class":3540},[3499,7118,7119],{"class":3979}," init",[3499,7121,7122],{"class":3536}," -n\n",[3499,7124,7125],{"class":3501,"line":3526},[3499,7126,3523],{"emptyLinePlaceholder":3522},[3499,7128,7129],{"class":3501,"line":3533},[3499,7130,7131],{"class":3529},"# Додаємо залежність Pydantic\n",[3499,7133,7134,7136,7138],{"class":3501,"line":3571},[3499,7135,7105],{"class":3540},[3499,7137,5378],{"class":3979},[3499,7139,7014],{"class":3979},[3499,7141,7142],{"class":3501,"line":3577},[3499,7143,3523],{"emptyLinePlaceholder":3522},[3499,7145,7146],{"class":3501,"line":3705},[3499,7147,7148],{"class":3529},"# Запускаємо скрипт у віртуальному середовищі Poetry\n",[3499,7150,7151,7153,7155,7158],{"class":3501,"line":3800},[3499,7152,7105],{"class":3540},[3499,7154,7053],{"class":3979},[3499,7156,7157],{"class":3979}," python",[3499,7159,7030],{"class":3979},[3709,7161,7162,7165,7166,3406,7169,7172],{},[3415,7163,7164],{},"Чому це важливо для вебAPI:","\nЗапити з вебу завжди приходять у вигляді текстових рядків (Query params, Headers) або нетипізованого JSON. Автоматичне приведення типів (Type Coercion) позбавляє розробника необхідності вручну писати ",[3403,7167,7168],{},"int(request.query['id'])",[3403,7170,7171],{},"request.query['is_active'] == 'true'",", зменшуючи кількість бойлерплейт-коду до нуля.",[3452,7174],{},[3474,7176,7178],{"id":7177},"pydantic-проти-dataclasses-у-чому-різниця","Pydantic проти Dataclasses: у чому різниця?",[3394,7180,7181,7182,7185],{},"У Python є вбудований модуль ",[3403,7183,7184],{},"dataclasses"," (введений у PEP 557, детально розглянутий у статті 09), який теж дозволяє створювати класи для зберігання даних на основі анотацій типів. Чому ж тоді не використовувати їх для вебAPI?",[7187,7188,7189,7206],"table",{},[7190,7191,7192],"thead",{},[7193,7194,7195,7200,7203],"tr",{},[7196,7197,7199],"th",{"align":7198},"left","Характеристика",[7196,7201,7202],{"align":7198},"Dataclasses",[7196,7204,7205],{"align":7198},"Pydantic",[7207,7208,7209,7223,7236,7249,7262],"tbody",{},[7193,7210,7211,7217,7220],{},[7212,7213,7214],"td",{"align":7198},[3415,7215,7216],{},"Походження",[7212,7218,7219],{"align":7198},"✅ Частина стандартної бібліотеки",[7212,7221,7222],{"align":7198},"❌ Зовнішня бібліотека (потребує встановлення)",[7193,7224,7225,7230,7233],{},[7212,7226,7227],{"align":7198},[3415,7228,7229],{},"Runtime-валідація",[7212,7231,7232],{"align":7198},"❌ Не робить перевірок під час виконання",[7212,7234,7235],{"align":7198},"✅ Строга runtime-валідація даних",[7193,7237,7238,7243,7246],{},[7212,7239,7240],{"align":7198},[3415,7241,7242],{},"Приведення типів (Coercion)",[7212,7244,7245],{"align":7198},"❌ Не приводить типи",[7212,7247,7248],{"align":7198},"✅ Автоматично конвертує та парсить типи",[7193,7250,7251,7256,7259],{},[7212,7252,7253],{"align":7198},[3415,7254,7255],{},"Серіалізація (JSON)",[7212,7257,7258],{"align":7198},"❌ Потребує написання кастомних серіалізаторів",[7212,7260,7261],{"align":7198},"✅ Вбудована швидка серіалізація та десеріалізація",[7193,7263,7264,7269,7272],{},[7212,7265,7266],{"align":7198},[3415,7267,7268],{},"Специфікація OpenAPI",[7212,7270,7271],{"align":7198},"❌ Не генерує схеми",[7212,7273,7274],{"align":7198},"✅ Нативна інтеграція та автоматична генерація схем",[3394,7276,7277,7278,7281],{},"Якщо ви створите ",[3403,7279,7280],{},"dataclass"," та передасте туди некоректні типи, Python не видасть жодної помилки під час виконання:",[3489,7283,7285],{"className":3491,"code":7284,"filename":3751,"language":3494,"meta":3495,"style":3495},"from dataclasses import dataclass\n\n@dataclass\nclass UserDC:\n    id: int\n\n# Runtime дозволить це виконати, що призведе до логічних помилок пізніше\nuser = UserDC(id=\"not-an-integer\")\n",[3403,7286,7287,7299,7303,7308,7317,7325,7329,7334],{"__ignoreMap":3495},[3499,7288,7289,7291,7294,7296],{"class":3501,"line":3502},[3499,7290,3506],{"class":3505},[3499,7292,7293],{"class":3509}," dataclasses ",[3499,7295,3513],{"class":3505},[3499,7297,7298],{"class":3509}," dataclass\n",[3499,7300,7301],{"class":3501,"line":3519},[3499,7302,3523],{"emptyLinePlaceholder":3522},[3499,7304,7305],{"class":3501,"line":3526},[3499,7306,7307],{"class":3540},"@dataclass\n",[3499,7309,7310,7312,7315],{"class":3501,"line":3533},[3499,7311,3858],{"class":3536},[3499,7313,7314],{"class":3554}," UserDC",[3499,7316,3629],{"class":3509},[3499,7318,7319,7321,7323],{"class":3501,"line":3571},[3499,7320,6877],{"class":3540},[3499,7322,3609],{"class":3509},[3499,7324,6882],{"class":3554},[3499,7326,7327],{"class":3501,"line":3577},[3499,7328,3523],{"emptyLinePlaceholder":3522},[3499,7330,7331],{"class":3501,"line":3705},[3499,7332,7333],{"class":3529},"# Runtime дозволить це виконати, що призведе до логічних помилок пізніше\n",[3499,7335,7336,7339,7341,7343,7346],{"class":3501,"line":3800},[3499,7337,7338],{"class":3509},"user = UserDC(",[3499,7340,6907],{"class":3547},[3499,7342,5964],{"class":3509},[3499,7344,7345],{"class":3979},"\"not-an-integer\"",[3499,7347,4876],{"class":3509},[3394,7349,7350,7351,7354],{},"Pydantic у цьому випадку відразу викине ",[3403,7352,7353],{},"ValidationError",", захищаючи межі вашого застонку від брудних даних.",[3452,7356],{},[3474,7358,7360,7362],{"id":7359},"basemodel-під-капотом-метакласи-та-дескриптори",[3403,7361,6870],{}," під капотом: Метакласи та Дескриптори",[3394,7364,7365,7366,7368,7369,4140,7372,3418],{},"Щоб зрозуміти, як працює ",[3403,7367,6870],{},", нам потрібно згадати дві важливі теми з нашого курсу: ",[3415,7370,7371],{},"дескриптори (стаття 07)",[3415,7373,7374],{},"метакласи (стаття 08)",[3394,7376,7377,7378,7380],{},"Коли ви наслідуєтесь від ",[3403,7379,6870],{},", відбувається наступне:",[3427,7382,7383,7396,7402],{},[3430,7384,7385,7388,7389,7392,7393,7395],{},[3415,7386,7387],{},"Збір анотацій:"," Метаклас Pydantic перехоплює створення класу ",[3403,7390,7391],{},"User",". Він аналізує словник ",[3403,7394,3463],{}," та виділяє всі визначені поля та їх типи.",[3430,7397,7398,7401],{},[3415,7399,7400],{},"Створення полів:"," Для кожного поля метаклас динамічно створює дескриптор. Ці дескриптори керують читанням та записом значень атрибутів, відповідаючи за перехоплення спроб присвоїти нові значення та запуск валідації.",[3430,7403,7404,7407,7408,7411],{},[3415,7405,7406],{},"Rust-валідатор:"," У Pydantic v2 ядро написано на Rust (",[3403,7409,7410],{},"pydantic-core","). Метаклас під час ініціалізації класу створює компільовану схему валідації Rust для цього класу. Це забезпечує колосальну швидкість роботи, оскільки безпосередній парсинг та перевірка відбуваються на низькому рівні без оверхеду віртуальної машини Python.",[3452,7413],{},[3474,7415,7417,7418,7421],{"id":7416},"анатомія-field-тонке-налаштування-полів","Анатомія ",[3403,7419,7420],{},"Field()",": тонке налаштування полів",[3394,7423,7424,7425,7428],{},"Функція ",[3403,7426,7427],{},"pydantic.Field()"," є основним інструментом для розширеного опису полів моделі. Вона дозволяє додавати метадані, налаштовувати аліаси для серіалізації\u002Fдесеріалізації, а також встановлювати обмеження на значення (валідацію), які будуть перевірятися під час виконання.",[7430,7431,7432,7437,7449,7453,7462,7468,7472,7476,7481,7494,7498,7501,7514,7518,7526,7532],"field-group",{},[7433,7434,7436],"field",{"name":7435,"type":4933,"default":3483},"default","Встановлює фіксоване значення за замовчуванням для поля. Якщо поле не передано під час створення моделі, воно отримає це значення.",[7433,7438,7441,7442,3840,7445,7448],{"name":7439,"type":7440,"default":3483},"default_factory","Callable[[], Any]","Використовується для встановлення динамічних значень за замовчуванням. Наприклад, ",[3403,7443,7444],{},"default_factory=datetime.utcnow",[3403,7446,7447],{},"default_factory=uuid.uuid4",". Це запобігає проблемі «мутабельних дефолтів» (mutable defaults) в Python, коли список або словник ділиться між усіма екземплярами класу.",[7433,7450,7452],{"name":7451,"type":3560,"default":3483},"alias","Альтернативне ім'я поля, яке використовується як для зчитування (десеріалізації), так і для запису (серіалізації) даних.",[7433,7454,7457,7458,7461],{"name":7455,"type":7456,"default":3483},"validation_alias","str | AliasPath | AliasChoices","Конкретний аліас, який буде використовуватися ",[3415,7459,7460],{},"тільки"," для валідації (читання вхідного JSON). Корисно, якщо вхідні дані мають дивну структуру або кілька можливих назв.",[7433,7463,7457,7465,7467],{"name":7464,"type":3560,"default":3483},"serialization_alias",[3415,7466,7460],{}," під час експорту моделі (генерації вихідного JSON).",[7433,7469,7471],{"name":7470,"type":3560,"default":3483},"title","Людська назва поля, яка потрапляє в специфікацію OpenAPI та відображається в Swagger UI.",[7433,7473,7475],{"name":7474,"type":3560,"default":3483},"description","Детальний опис призначення поля, який генерується в документації OpenAPI.",[7433,7477,7480],{"name":7478,"type":7479,"default":3483},"examples","list[Any]","Приклади значень для генерації Swagger-документації.",[7433,7482,7484,7485,7487,7488,3406,7491,3418],{"name":7483,"type":3927,"default":6096},"exclude","Якщо встановлено ",[3403,7486,6086],{},", це поле буде повністю виключене з вихідного словника або JSON під час виклику ",[3403,7489,7490],{},"model_dump()",[3403,7492,7493],{},"model_dump_json()",[7433,7495,7497],{"name":7496,"type":3927,"default":6096},"frozen","Робить конкретне поле незмінним (read-only) після ініціалізації об'єкта, навіть якщо сама модель не є frozen.",[7433,7499,7500],{"name":6632,"type":3927,"default":3483},"Вмикає строгий режим перевірки типу лише для цього поля.",[7433,7502,7505,7506,3468,7508,3468,7511,4744],{"name":7503,"type":7504,"default":3483},"gt \u002F ge","num","Greater Than (строго більше) та Greater or Equal (більше або дорівнює). Обмеження для чисел (",[3403,7507,3555],{},[3403,7509,7510],{},"float",[3403,7512,7513],{},"Decimal",[7433,7515,7517],{"name":7516,"type":7504,"default":3483},"lt \u002F le","Less Than (строго менше) та Less or Equal (менше або дорівнює). Обмеження для чисел.",[7433,7519,7521,7522,7525],{"name":7520,"type":7504,"default":3483},"multiple_of","Вимога, щоб число було кратним вказаному значенню (наприклад, ",[3403,7523,7524],{},"multiple_of=5"," дозволяє 5, 10, 15...).",[7433,7527,7529,7530,4744],{"name":7528,"type":3555,"default":3483},"min_length \u002F max_length","Обмеження на мінімальну та максимальну кількість символів для рядків (",[3403,7531,3560],{},[7433,7533,7536],{"name":7534,"type":7535,"default":3483},"pattern","str | Pattern","Регулярний вираз (Regex), якому має відповідати рядок.",[3394,7538,7539,7540,7543],{},"Приклад застосування ",[3403,7541,7542],{},"Field"," з різними параметрами:",[3489,7545,7547],{"className":3491,"code":7546,"filename":3751,"language":3494,"meta":3495,"style":3495},"import uuid\nfrom datetime import datetime\nfrom pydantic import BaseModel, Field, ValidationError\n\nclass Book(BaseModel):\n    # Динамічний дефолт\n    id: uuid.UUID = Field(default_factory=uuid.uuid4, description=\"Унікальний ID книги\")\n    \n    # Обмеження на довжину та приклад\n    title: str = Field(min_length=1, max_length=100, examples=[\"Чистий код\"])\n    \n    # Обмеження на число\n    price: float = Field(gt=0, le=1000)\n    \n    # Виключення з експорту (наприклад, внутрішні системні прапорці)\n    is_deleted: bool = Field(default=False, exclude=True)\n    \n    # Заборона зміни після ініціалізації\n    created_at: datetime = Field(default_factory=datetime.utcnow, frozen=True)\n\n# Демонстрація використання та валідації:\ntry:\n    # 1. Створення коректного об'єкта (id та created_at згенеруються автоматично)\n    book = Book(title=\"Чистий код\", price=450.00)\n    print(\"Успішно створено книгу:\")\n    print(f\"  ID: {book.id}\")\n    print(f\"  Назва: {book.title}\")\n    print(f\"  Ціна: {book.price}\")\n    print(f\"  Створено: {book.created_at}\\n\")\n    \n    # 2. Перевірка роботи параметра exclude=True (is_deleted не буде у вихідному словнику)\n    print(\"Серіалізація моделі (is_deleted виключено):\")\n    print(f\"  {book.model_dump()}\\n\") \n    \n    # 3. Спроба змінити frozen-поле (викликає ValidationError)\n    print(\"Спроба змінити дату створення (frozen=True):\")\n    book.created_at = datetime.utcnow()\nexcept ValidationError as e:\n    print(f\"  Помилка валідації: {e.errors()[0]['msg']}\\n\")\n\ntry:\n    # 4. Створення об'єкта з некоректними даними (price \u003C= 0)\n    print(\"Створення книги з некоректною ціною (-10.00):\")\n    invalid_book = Book(title=\"Некоректна книга\", price=-10.00)\nexcept ValidationError as e:\n    print(f\"  Помилка валідації: {e.errors()[0]['msg']}\")\n\n",[3403,7548,7549,7556,7568,7579,7583,7596,7601,7622,7627,7632,7671,7675,7680,7707,7711,7716,7741,7745,7750,7768,7772,7777,7784,7790,7814,7826,7850,7873,7896,7923,7928,7934,7946,7972,7977,7983,7995,8001,8016,8051,8056,8063,8069,8081,8106,8117],{"__ignoreMap":3495},[3499,7550,7551,7553],{"class":3501,"line":3502},[3499,7552,3513],{"class":3505},[3499,7554,7555],{"class":3509}," uuid\n",[3499,7557,7558,7560,7563,7565],{"class":3501,"line":3519},[3499,7559,3506],{"class":3505},[3499,7561,7562],{"class":3509}," datetime ",[3499,7564,3513],{"class":3505},[3499,7566,7567],{"class":3509}," datetime\n",[3499,7569,7570,7572,7574,7576],{"class":3501,"line":3526},[3499,7571,3506],{"class":3505},[3499,7573,5934],{"class":3509},[3499,7575,3513],{"class":3505},[3499,7577,7578],{"class":3509}," BaseModel, Field, ValidationError\n",[3499,7580,7581],{"class":3501,"line":3533},[3499,7582,3523],{"emptyLinePlaceholder":3522},[3499,7584,7585,7587,7590,7592,7594],{"class":3501,"line":3571},[3499,7586,3858],{"class":3536},[3499,7588,7589],{"class":3554}," Book",[3499,7591,3544],{"class":3509},[3499,7593,6870],{"class":3554},[3499,7595,4393],{"class":3509},[3499,7597,7598],{"class":3501,"line":3577},[3499,7599,7600],{"class":3529},"    # Динамічний дефолт\n",[3499,7602,7603,7605,7608,7610,7613,7615,7617,7620],{"class":3501,"line":3705},[3499,7604,6877],{"class":3540},[3499,7606,7607],{"class":3509},": uuid.UUID = Field(",[3499,7609,7439],{"class":3547},[3499,7611,7612],{"class":3509},"=uuid.uuid4, ",[3499,7614,7474],{"class":3547},[3499,7616,5964],{"class":3509},[3499,7618,7619],{"class":3979},"\"Унікальний ID книги\"",[3499,7621,4876],{"class":3509},[3499,7623,7624],{"class":3501,"line":3800},[3499,7625,7626],{"class":3509},"    \n",[3499,7628,7629],{"class":3501,"line":3812},[3499,7630,7631],{"class":3529},"    # Обмеження на довжину та приклад\n",[3499,7633,7634,7637,7639,7642,7645,7647,7649,7651,7654,7656,7658,7660,7662,7665,7668],{"class":3501,"line":3826},[3499,7635,7636],{"class":3509},"    title: ",[3499,7638,3560],{"class":3554},[3499,7640,7641],{"class":3509}," = Field(",[3499,7643,7644],{"class":3547},"min_length",[3499,7646,5964],{"class":3509},[3499,7648,4783],{"class":3771},[3499,7650,3468],{"class":3509},[3499,7652,7653],{"class":3547},"max_length",[3499,7655,5964],{"class":3509},[3499,7657,5893],{"class":3771},[3499,7659,3468],{"class":3509},[3499,7661,7478],{"class":3547},[3499,7663,7664],{"class":3509},"=[",[3499,7666,7667],{"class":3979},"\"Чистий код\"",[3499,7669,7670],{"class":3509},"])\n",[3499,7672,7673],{"class":3501,"line":3985},[3499,7674,7626],{"class":3509},[3499,7676,7677],{"class":3501,"line":3991},[3499,7678,7679],{"class":3529},"    # Обмеження на число\n",[3499,7681,7682,7685,7687,7689,7692,7694,7696,7698,7700,7702,7705],{"class":3501,"line":3997},[3499,7683,7684],{"class":3509},"    price: ",[3499,7686,7510],{"class":3554},[3499,7688,7641],{"class":3509},[3499,7690,7691],{"class":3547},"gt",[3499,7693,5964],{"class":3509},[3499,7695,6242],{"class":3771},[3499,7697,3468],{"class":3509},[3499,7699,5971],{"class":3547},[3499,7701,5964],{"class":3509},[3499,7703,7704],{"class":3771},"1000",[3499,7706,4876],{"class":3509},[3499,7708,7709],{"class":3501,"line":4006},[3499,7710,7626],{"class":3509},[3499,7712,7713],{"class":3501,"line":4593},[3499,7714,7715],{"class":3529},"    # Виключення з експорту (наприклад, внутрішні системні прапорці)\n",[3499,7717,7718,7721,7723,7725,7727,7729,7731,7733,7735,7737,7739],{"class":3501,"line":4598},[3499,7719,7720],{"class":3509},"    is_deleted: ",[3499,7722,3927],{"class":3554},[3499,7724,7641],{"class":3509},[3499,7726,7435],{"class":3547},[3499,7728,5964],{"class":3509},[3499,7730,6096],{"class":3536},[3499,7732,3468],{"class":3509},[3499,7734,7483],{"class":3547},[3499,7736,5964],{"class":3509},[3499,7738,6086],{"class":3536},[3499,7740,4876],{"class":3509},[3499,7742,7743],{"class":3501,"line":4604},[3499,7744,7626],{"class":3509},[3499,7746,7747],{"class":3501,"line":5778},[3499,7748,7749],{"class":3529},"    # Заборона зміни після ініціалізації\n",[3499,7751,7752,7755,7757,7760,7762,7764,7766],{"class":3501,"line":5786},[3499,7753,7754],{"class":3509},"    created_at: datetime = Field(",[3499,7756,7439],{"class":3547},[3499,7758,7759],{"class":3509},"=datetime.utcnow, ",[3499,7761,7496],{"class":3547},[3499,7763,5964],{"class":3509},[3499,7765,6086],{"class":3536},[3499,7767,4876],{"class":3509},[3499,7769,7770],{"class":3501,"line":5791},[3499,7771,3523],{"emptyLinePlaceholder":3522},[3499,7773,7774],{"class":3501,"line":5797},[3499,7775,7776],{"class":3529},"# Демонстрація використання та валідації:\n",[3499,7778,7779,7782],{"class":3501,"line":5814},[3499,7780,7781],{"class":3505},"try",[3499,7783,3629],{"class":3509},[3499,7785,7787],{"class":3501,"line":7786},23,[3499,7788,7789],{"class":3529},"    # 1. Створення коректного об'єкта (id та created_at згенеруються автоматично)\n",[3499,7791,7793,7796,7798,7800,7802,7804,7807,7809,7812],{"class":3501,"line":7792},24,[3499,7794,7795],{"class":3509},"    book = Book(",[3499,7797,7470],{"class":3547},[3499,7799,5964],{"class":3509},[3499,7801,7667],{"class":3979},[3499,7803,3468],{"class":3509},[3499,7805,7806],{"class":3547},"price",[3499,7808,5964],{"class":3509},[3499,7810,7811],{"class":3771},"450.00",[3499,7813,4876],{"class":3509},[3499,7815,7817,7819,7821,7824],{"class":3501,"line":7816},25,[3499,7818,6236],{"class":3540},[3499,7820,3544],{"class":3509},[3499,7822,7823],{"class":3979},"\"Успішно створено книгу:\"",[3499,7825,4876],{"class":3509},[3499,7827,7829,7831,7833,7835,7838,7840,7843,7845,7848],{"class":3501,"line":7828},26,[3499,7830,6236],{"class":3540},[3499,7832,3544],{"class":3509},[3499,7834,5692],{"class":3536},[3499,7836,7837],{"class":3979},"\"  ID: ",[3499,7839,4085],{"class":3536},[3499,7841,7842],{"class":3509},"book.id",[3499,7844,4094],{"class":3536},[3499,7846,7847],{"class":3979},"\"",[3499,7849,4876],{"class":3509},[3499,7851,7853,7855,7857,7859,7862,7864,7867,7869,7871],{"class":3501,"line":7852},27,[3499,7854,6236],{"class":3540},[3499,7856,3544],{"class":3509},[3499,7858,5692],{"class":3536},[3499,7860,7861],{"class":3979},"\"  Назва: ",[3499,7863,4085],{"class":3536},[3499,7865,7866],{"class":3509},"book.title",[3499,7868,4094],{"class":3536},[3499,7870,7847],{"class":3979},[3499,7872,4876],{"class":3509},[3499,7874,7876,7878,7880,7882,7885,7887,7890,7892,7894],{"class":3501,"line":7875},28,[3499,7877,6236],{"class":3540},[3499,7879,3544],{"class":3509},[3499,7881,5692],{"class":3536},[3499,7883,7884],{"class":3979},"\"  Ціна: ",[3499,7886,4085],{"class":3536},[3499,7888,7889],{"class":3509},"book.price",[3499,7891,4094],{"class":3536},[3499,7893,7847],{"class":3979},[3499,7895,4876],{"class":3509},[3499,7897,7899,7901,7903,7905,7908,7910,7913,7915,7919,7921],{"class":3501,"line":7898},29,[3499,7900,6236],{"class":3540},[3499,7902,3544],{"class":3509},[3499,7904,5692],{"class":3536},[3499,7906,7907],{"class":3979},"\"  Створено: ",[3499,7909,4085],{"class":3536},[3499,7911,7912],{"class":3509},"book.created_at",[3499,7914,4094],{"class":3536},[3499,7916,7918],{"class":7917},"sjcCO","\\n",[3499,7920,7847],{"class":3979},[3499,7922,4876],{"class":3509},[3499,7924,7926],{"class":3501,"line":7925},30,[3499,7927,7626],{"class":3509},[3499,7929,7931],{"class":3501,"line":7930},31,[3499,7932,7933],{"class":3529},"    # 2. Перевірка роботи параметра exclude=True (is_deleted не буде у вихідному словнику)\n",[3499,7935,7937,7939,7941,7944],{"class":3501,"line":7936},32,[3499,7938,6236],{"class":3540},[3499,7940,3544],{"class":3509},[3499,7942,7943],{"class":3979},"\"Серіалізація моделі (is_deleted виключено):\"",[3499,7945,4876],{"class":3509},[3499,7947,7949,7951,7953,7955,7958,7960,7963,7965,7967,7969],{"class":3501,"line":7948},33,[3499,7950,6236],{"class":3540},[3499,7952,3544],{"class":3509},[3499,7954,5692],{"class":3536},[3499,7956,7957],{"class":3979},"\"  ",[3499,7959,4085],{"class":3536},[3499,7961,7962],{"class":3509},"book.model_dump()",[3499,7964,4094],{"class":3536},[3499,7966,7918],{"class":7917},[3499,7968,7847],{"class":3979},[3499,7970,7971],{"class":3509},") \n",[3499,7973,7975],{"class":3501,"line":7974},34,[3499,7976,7626],{"class":3509},[3499,7978,7980],{"class":3501,"line":7979},35,[3499,7981,7982],{"class":3529},"    # 3. Спроба змінити frozen-поле (викликає ValidationError)\n",[3499,7984,7986,7988,7990,7993],{"class":3501,"line":7985},36,[3499,7987,6236],{"class":3540},[3499,7989,3544],{"class":3509},[3499,7991,7992],{"class":3979},"\"Спроба змінити дату створення (frozen=True):\"",[3499,7994,4876],{"class":3509},[3499,7996,7998],{"class":3501,"line":7997},37,[3499,7999,8000],{"class":3509},"    book.created_at = datetime.utcnow()\n",[3499,8002,8004,8007,8010,8013],{"class":3501,"line":8003},38,[3499,8005,8006],{"class":3505},"except",[3499,8008,8009],{"class":3509}," ValidationError ",[3499,8011,8012],{"class":3505},"as",[3499,8014,8015],{"class":3509}," e:\n",[3499,8017,8019,8021,8023,8025,8028,8030,8033,8035,8037,8040,8043,8045,8047,8049],{"class":3501,"line":8018},39,[3499,8020,6236],{"class":3540},[3499,8022,3544],{"class":3509},[3499,8024,5692],{"class":3536},[3499,8026,8027],{"class":3979},"\"  Помилка валідації: ",[3499,8029,4085],{"class":3536},[3499,8031,8032],{"class":3509},"e.errors()[",[3499,8034,6242],{"class":3771},[3499,8036,5881],{"class":3509},[3499,8038,8039],{"class":3979},"'msg'",[3499,8041,8042],{"class":3509},"]",[3499,8044,4094],{"class":3536},[3499,8046,7918],{"class":7917},[3499,8048,7847],{"class":3979},[3499,8050,4876],{"class":3509},[3499,8052,8054],{"class":3501,"line":8053},40,[3499,8055,3523],{"emptyLinePlaceholder":3522},[3499,8057,8059,8061],{"class":3501,"line":8058},41,[3499,8060,7781],{"class":3505},[3499,8062,3629],{"class":3509},[3499,8064,8066],{"class":3501,"line":8065},42,[3499,8067,8068],{"class":3529},"    # 4. Створення об'єкта з некоректними даними (price \u003C= 0)\n",[3499,8070,8072,8074,8076,8079],{"class":3501,"line":8071},43,[3499,8073,6236],{"class":3540},[3499,8075,3544],{"class":3509},[3499,8077,8078],{"class":3979},"\"Створення книги з некоректною ціною (-10.00):\"",[3499,8080,4876],{"class":3509},[3499,8082,8084,8087,8089,8091,8094,8096,8098,8101,8104],{"class":3501,"line":8083},44,[3499,8085,8086],{"class":3509},"    invalid_book = Book(",[3499,8088,7470],{"class":3547},[3499,8090,5964],{"class":3509},[3499,8092,8093],{"class":3979},"\"Некоректна книга\"",[3499,8095,3468],{"class":3509},[3499,8097,7806],{"class":3547},[3499,8099,8100],{"class":3509},"=-",[3499,8102,8103],{"class":3771},"10.00",[3499,8105,4876],{"class":3509},[3499,8107,8109,8111,8113,8115],{"class":3501,"line":8108},45,[3499,8110,8006],{"class":3505},[3499,8112,8009],{"class":3509},[3499,8114,8012],{"class":3505},[3499,8116,8015],{"class":3509},[3499,8118,8120,8122,8124,8126,8128,8130,8132,8134,8136,8138,8140,8142,8144],{"class":3501,"line":8119},46,[3499,8121,6236],{"class":3540},[3499,8123,3544],{"class":3509},[3499,8125,5692],{"class":3536},[3499,8127,8027],{"class":3979},[3499,8129,4085],{"class":3536},[3499,8131,8032],{"class":3509},[3499,8133,6242],{"class":3771},[3499,8135,5881],{"class":3509},[3499,8137,8039],{"class":3979},[3499,8139,8042],{"class":3509},[3499,8141,4094],{"class":3536},[3499,8143,7847],{"class":3979},[3499,8145,4876],{"class":3509},[3486,8147,8148,8188,8210],{},[3489,8149,8151],{"className":6765,"code":8150,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Створюємо віртуальне середовище та запускаємо\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install pydantic\npython main.py\n",[3403,8152,8153,8158,8168,8174,8182],{"__ignoreMap":3495},[3499,8154,8155],{"class":3501,"line":3502},[3499,8156,8157],{"class":3529},"# Створюємо віртуальне середовище та запускаємо\n",[3499,8159,8160,8162,8164,8166],{"class":3501,"line":3519},[3499,8161,3494],{"class":3540},[3499,8163,6973],{"class":3536},[3499,8165,6976],{"class":3979},[3499,8167,6979],{"class":3979},[3499,8169,8170,8172],{"class":3501,"line":3526},[3499,8171,6984],{"class":3540},[3499,8173,7084],{"class":3979},[3499,8175,8176,8178,8180],{"class":3501,"line":3533},[3499,8177,6959],{"class":3540},[3499,8179,7011],{"class":3979},[3499,8181,7014],{"class":3979},[3499,8183,8184,8186],{"class":3501,"line":3571},[3499,8185,3494],{"class":3540},[3499,8187,7030],{"class":3979},[3489,8189,8191],{"className":6765,"code":8190,"filename":7034,"language":6768,"meta":3495,"style":3495},"# Швидкий запуск\nuv run --with pydantic main.py\n",[3403,8192,8193,8198],{"__ignoreMap":3495},[3499,8194,8195],{"class":3501,"line":3502},[3499,8196,8197],{"class":3529},"# Швидкий запуск\n",[3499,8199,8200,8202,8204,8206,8208],{"class":3501,"line":3519},[3499,8201,7034],{"class":3540},[3499,8203,7053],{"class":3979},[3499,8205,7056],{"class":3536},[3499,8207,7059],{"class":3979},[3499,8209,7030],{"class":3979},[3489,8211,8213],{"className":6765,"code":8212,"filename":7105,"language":6768,"meta":3495,"style":3495},"poetry init -n\npoetry add pydantic\npoetry run python main.py\n",[3403,8214,8215,8223,8231],{"__ignoreMap":3495},[3499,8216,8217,8219,8221],{"class":3501,"line":3502},[3499,8218,7105],{"class":3540},[3499,8220,7119],{"class":3979},[3499,8222,7122],{"class":3536},[3499,8224,8225,8227,8229],{"class":3501,"line":3519},[3499,8226,7105],{"class":3540},[3499,8228,5378],{"class":3979},[3499,8230,7014],{"class":3979},[3499,8232,8233,8235,8237,8239],{"class":3501,"line":3526},[3499,8234,7105],{"class":3540},[3499,8236,7053],{"class":3979},[3499,8238,7157],{"class":3979},[3499,8240,7030],{"class":3979},[3452,8242],{},[3474,8244,8246],{"id":8245},"спеціальні-типи-в-pydantic-від-emailstr-до-мережевих-адрес","Спеціальні типи в Pydantic: від EmailStr до мережевих адрес",[3394,8248,8249],{},"Pydantic містить десятки вбудованих специфічних типів, які суттєво полегшують життя розробнику вебAPI, беручи на себе складну валідацію форматів даних.",[3741,8251,8253,8254],{"id":8252},"_1-валідація-пошти-emailstr","1. Валідація пошти: ",[3403,8255,8256],{},"EmailStr",[3394,8258,8259,8260,8262],{},"Тип ",[3403,8261,8256],{}," призначений для перевірки електронних адрес.",[4697,8264,8265,8278,8298],{},[3430,8266,8267,8270,8271,8274,8275,3418],{},[3415,8268,8269],{},"Звідки береться:"," Він імпортується з головного модуля ",[3403,8272,8273],{},"pydantic",", але під капотом використовує популярну бібліотеку ",[3403,8276,8277],{},"email-validator",[3430,8279,8280,8283,8284,8286,8287,8289,8290,8293,8294,8297],{},[3415,8281,8282],{},"Як працює:"," Якщо ви спробуєте імпортувати ",[3403,8285,8256],{}," або запустити код без встановленої бібліотеки ",[3403,8288,8277],{},", Pydantic викине ",[3403,8291,8292],{},"ImportError",". Тип перевіряє не тільки наявність символу ",[3403,8295,8296],{},"@",", але й коректність структури домену.",[3430,8299,8300,8303,8304,3418],{},[3415,8301,8302],{},"Встановлення:"," Для використання потрібно встановити pydantic із розширенням: ",[3403,8305,8306],{},"pip install \"pydantic[email]\"",[3741,8308,8310,8311],{"id":8309},"_2-захист-конфіденційних-даних-secretstr","2. Захист конфіденційних даних: ",[3403,8312,8313],{},"SecretStr",[3394,8315,8316,8317,8319,8320,8322,8323,8325,8326,8329,8330,3418],{},"У вебзастосунках часто потрібно зберігати паролі, API-ключі або токени підключення. Якщо описати їх як звичайний ",[3403,8318,3560],{},", вони випадково можуть потрапити до логів (через ",[3403,8321,4111],{}," або logging) чи вивестись у HTTP-відповідь.\n",[3403,8324,8313],{}," вирішує цю проблему: він маскує своє значення під час конвертації в рядок або виклику ",[3403,8327,8328],{},"repr()",". Для отримання сирого значення потрібно явно викликати метод ",[3403,8331,8332],{},"get_secret_value()",[3741,8334,8336,8337,3468,8340,3468,8343,4710],{"id":8335},"_3-мережеві-та-dsn-типи-httpurl-postgresdsn-ipvanyaddress","3. Мережеві та DSN типи (",[3403,8338,8339],{},"HttpUrl",[3403,8341,8342],{},"PostgresDsn",[3403,8344,8345],{},"IPvAnyAddress",[4697,8347,8348,8362,8373],{},[3430,8349,8350,8354,8355,3840,8358,8361],{},[3415,8351,8352],{},[3403,8353,8339],{}," — перевіряє схему URL (очікує ",[3403,8356,8357],{},"http",[3403,8359,8360],{},"https","), наявність хоста та правильність структури посилання.",[3430,8363,8364,8372],{},[3415,8365,8366,8368,8369],{},[3403,8367,8342],{}," \u002F ",[3403,8370,8371],{},"RedisDsn"," — валідує рядки підключення (Data Source Name). Розбирає URI на компоненти (user, password, host, port, path).",[3430,8374,8375,8379],{},[3415,8376,8377],{},[3403,8378,8345],{}," — валідує IPv4 або IPv6 адресу.",[3394,8381,8382],{},"Приклад використання спеціальних типів:",[3489,8384,8386],{"className":3491,"code":8385,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import BaseModel, EmailStr, HttpUrl, PostgresDsn, SecretStr\nfrom pydantic.networks import IPvAnyAddress\n\nclass SystemConfig(BaseModel):\n    admin_email: EmailStr\n    database_url: PostgresDsn\n    api_key: SecretStr\n    allowed_ip: IPvAnyAddress\n    project_website: HttpUrl\n\nconfig = SystemConfig(\n    admin_email=\"admin@kostyl.dev\",\n    database_url=\"postgresql:\u002F\u002Fuser:pass@localhost:5432\u002Fdbname\",\n    api_key=\"super-secret-key-123\",\n    allowed_ip=\"192.168.1.1\",\n    project_website=\"https:\u002F\u002Fkostyl.dev\"\n)\n\n# Значення api_key замасковано:\nprint(config.api_key)  # Виведе: SecretStr('**********')\nprint(repr(config.api_key))  # Виведе: SecretStr('**********')\n\n# Отримання реального пароля (робити свідомо!):\nprint(config.api_key.get_secret_value())  # Виведе: \"super-secret-key-123\"\n\n# Звернення до частин URI без регулярних виразів:\nprint(config.database_url.port)  # Виведе: 5432\nprint(config.database_url.host)  # Виведе: \"localhost\"\n",[3403,8387,8388,8399,8411,8415,8428,8433,8438,8443,8448,8453,8457,8462,8475,8487,8499,8511,8521,8525,8529,8534,8544,8558,8562,8567,8577,8581,8586,8596],{"__ignoreMap":3495},[3499,8389,8390,8392,8394,8396],{"class":3501,"line":3502},[3499,8391,3506],{"class":3505},[3499,8393,5934],{"class":3509},[3499,8395,3513],{"class":3505},[3499,8397,8398],{"class":3509}," BaseModel, EmailStr, HttpUrl, PostgresDsn, SecretStr\n",[3499,8400,8401,8403,8406,8408],{"class":3501,"line":3519},[3499,8402,3506],{"class":3505},[3499,8404,8405],{"class":3509}," pydantic.networks ",[3499,8407,3513],{"class":3505},[3499,8409,8410],{"class":3509}," IPvAnyAddress\n",[3499,8412,8413],{"class":3501,"line":3526},[3499,8414,3523],{"emptyLinePlaceholder":3522},[3499,8416,8417,8419,8422,8424,8426],{"class":3501,"line":3533},[3499,8418,3858],{"class":3536},[3499,8420,8421],{"class":3554}," SystemConfig",[3499,8423,3544],{"class":3509},[3499,8425,6870],{"class":3554},[3499,8427,4393],{"class":3509},[3499,8429,8430],{"class":3501,"line":3571},[3499,8431,8432],{"class":3509},"    admin_email: EmailStr\n",[3499,8434,8435],{"class":3501,"line":3577},[3499,8436,8437],{"class":3509},"    database_url: PostgresDsn\n",[3499,8439,8440],{"class":3501,"line":3705},[3499,8441,8442],{"class":3509},"    api_key: SecretStr\n",[3499,8444,8445],{"class":3501,"line":3800},[3499,8446,8447],{"class":3509},"    allowed_ip: IPvAnyAddress\n",[3499,8449,8450],{"class":3501,"line":3812},[3499,8451,8452],{"class":3509},"    project_website: HttpUrl\n",[3499,8454,8455],{"class":3501,"line":3826},[3499,8456,3523],{"emptyLinePlaceholder":3522},[3499,8458,8459],{"class":3501,"line":3985},[3499,8460,8461],{"class":3509},"config = SystemConfig(\n",[3499,8463,8464,8467,8469,8472],{"class":3501,"line":3991},[3499,8465,8466],{"class":3547},"    admin_email",[3499,8468,5964],{"class":3509},[3499,8470,8471],{"class":3979},"\"admin@kostyl.dev\"",[3499,8473,8474],{"class":3509},",\n",[3499,8476,8477,8480,8482,8485],{"class":3501,"line":3997},[3499,8478,8479],{"class":3547},"    database_url",[3499,8481,5964],{"class":3509},[3499,8483,8484],{"class":3979},"\"postgresql:\u002F\u002Fuser:pass@localhost:5432\u002Fdbname\"",[3499,8486,8474],{"class":3509},[3499,8488,8489,8492,8494,8497],{"class":3501,"line":4006},[3499,8490,8491],{"class":3547},"    api_key",[3499,8493,5964],{"class":3509},[3499,8495,8496],{"class":3979},"\"super-secret-key-123\"",[3499,8498,8474],{"class":3509},[3499,8500,8501,8504,8506,8509],{"class":3501,"line":4593},[3499,8502,8503],{"class":3547},"    allowed_ip",[3499,8505,5964],{"class":3509},[3499,8507,8508],{"class":3979},"\"192.168.1.1\"",[3499,8510,8474],{"class":3509},[3499,8512,8513,8516,8518],{"class":3501,"line":4598},[3499,8514,8515],{"class":3547},"    project_website",[3499,8517,5964],{"class":3509},[3499,8519,8520],{"class":3979},"\"https:\u002F\u002Fkostyl.dev\"\n",[3499,8522,8523],{"class":3501,"line":4604},[3499,8524,4876],{"class":3509},[3499,8526,8527],{"class":3501,"line":5778},[3499,8528,3523],{"emptyLinePlaceholder":3522},[3499,8530,8531],{"class":3501,"line":5786},[3499,8532,8533],{"class":3529},"# Значення api_key замасковано:\n",[3499,8535,8536,8538,8541],{"class":3501,"line":5791},[3499,8537,4111],{"class":3540},[3499,8539,8540],{"class":3509},"(config.api_key)  ",[3499,8542,8543],{"class":3529},"# Виведе: SecretStr('**********')\n",[3499,8545,8546,8548,8550,8553,8556],{"class":3501,"line":5797},[3499,8547,4111],{"class":3540},[3499,8549,3544],{"class":3509},[3499,8551,8552],{"class":3540},"repr",[3499,8554,8555],{"class":3509},"(config.api_key))  ",[3499,8557,8543],{"class":3529},[3499,8559,8560],{"class":3501,"line":5814},[3499,8561,3523],{"emptyLinePlaceholder":3522},[3499,8563,8564],{"class":3501,"line":7786},[3499,8565,8566],{"class":3529},"# Отримання реального пароля (робити свідомо!):\n",[3499,8568,8569,8571,8574],{"class":3501,"line":7792},[3499,8570,4111],{"class":3540},[3499,8572,8573],{"class":3509},"(config.api_key.get_secret_value())  ",[3499,8575,8576],{"class":3529},"# Виведе: \"super-secret-key-123\"\n",[3499,8578,8579],{"class":3501,"line":7816},[3499,8580,3523],{"emptyLinePlaceholder":3522},[3499,8582,8583],{"class":3501,"line":7828},[3499,8584,8585],{"class":3529},"# Звернення до частин URI без регулярних виразів:\n",[3499,8587,8588,8590,8593],{"class":3501,"line":7852},[3499,8589,4111],{"class":3540},[3499,8591,8592],{"class":3509},"(config.database_url.port)  ",[3499,8594,8595],{"class":3529},"# Виведе: 5432\n",[3499,8597,8598,8600,8603],{"class":3501,"line":7875},[3499,8599,4111],{"class":3540},[3499,8601,8602],{"class":3509},"(config.database_url.host)  ",[3499,8604,8605],{"class":3529},"# Виведе: \"localhost\"\n",[3486,8607,8608,8658,8681],{},[3489,8609,8611],{"className":6765,"code":8610,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Встановлюємо pydantic разом із підтримкою email-validator\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install \"pydantic[email]\"\n\n# Запускаємо скрипт\npython main.py\n",[3403,8612,8613,8618,8628,8634,8643,8647,8652],{"__ignoreMap":3495},[3499,8614,8615],{"class":3501,"line":3502},[3499,8616,8617],{"class":3529},"# Встановлюємо pydantic разом із підтримкою email-validator\n",[3499,8619,8620,8622,8624,8626],{"class":3501,"line":3519},[3499,8621,3494],{"class":3540},[3499,8623,6973],{"class":3536},[3499,8625,6976],{"class":3979},[3499,8627,6979],{"class":3979},[3499,8629,8630,8632],{"class":3501,"line":3526},[3499,8631,6984],{"class":3540},[3499,8633,7084],{"class":3979},[3499,8635,8636,8638,8640],{"class":3501,"line":3533},[3499,8637,6959],{"class":3540},[3499,8639,7011],{"class":3979},[3499,8641,8642],{"class":3979}," \"pydantic[email]\"\n",[3499,8644,8645],{"class":3501,"line":3571},[3499,8646,3523],{"emptyLinePlaceholder":3522},[3499,8648,8649],{"class":3501,"line":3577},[3499,8650,8651],{"class":3529},"# Запускаємо скрипт\n",[3499,8653,8654,8656],{"class":3501,"line":3705},[3499,8655,3494],{"class":3540},[3499,8657,7030],{"class":3979},[3489,8659,8661],{"className":6765,"code":8660,"filename":7034,"language":6768,"meta":3495,"style":3495},"# uv автоматично завантажить потрібні залежності\nuv run --with \"pydantic[email]\" main.py\n",[3403,8662,8663,8668],{"__ignoreMap":3495},[3499,8664,8665],{"class":3501,"line":3502},[3499,8666,8667],{"class":3529},"# uv автоматично завантажить потрібні залежності\n",[3499,8669,8670,8672,8674,8676,8679],{"class":3501,"line":3519},[3499,8671,7034],{"class":3540},[3499,8673,7053],{"class":3979},[3499,8675,7056],{"class":3536},[3499,8677,8678],{"class":3979}," \"pydantic[email]\"",[3499,8680,7030],{"class":3979},[3489,8682,8684],{"className":6765,"code":8683,"filename":7105,"language":6768,"meta":3495,"style":3495},"poetry init -n\n# Додаємо залежність з extras\npoetry add \"pydantic[email]\"\npoetry run python main.py\n",[3403,8685,8686,8694,8699,8707],{"__ignoreMap":3495},[3499,8687,8688,8690,8692],{"class":3501,"line":3502},[3499,8689,7105],{"class":3540},[3499,8691,7119],{"class":3979},[3499,8693,7122],{"class":3536},[3499,8695,8696],{"class":3501,"line":3519},[3499,8697,8698],{"class":3529},"# Додаємо залежність з extras\n",[3499,8700,8701,8703,8705],{"class":3501,"line":3526},[3499,8702,7105],{"class":3540},[3499,8704,5378],{"class":3979},[3499,8706,8642],{"class":3979},[3499,8708,8709,8711,8713,8715],{"class":3501,"line":3533},[3499,8710,7105],{"class":3540},[3499,8712,7053],{"class":3979},[3499,8714,7157],{"class":3979},[3499,8716,7030],{"class":3979},[3452,8718],{},[3474,8720,8722],{"id":8721},"механізми-валідації-декоратори-та-класи-валідатори","Механізми валідації: Декоратори та класи-валідатори",[3394,8724,8725,8726,4744],{},"Pydantic v2 пропонує два основні підходи для написання кастомних правил валідації: класичний (через декоратори у класі моделі) та сучасний (через класи-валідатори та ",[3403,8727,5910],{},[3741,8729,8731,8732,4140,8735],{"id":8730},"_1-декоратори-field_validator-та-model_validator","1. Декоратори ",[3403,8733,8734],{},"@field_validator",[3403,8736,8737],{},"@model_validator",[3394,8739,8740],{},"Це найпоширеніший спосіб додавання бізнес-правил безпосередньо у модель.",[4697,8742,8743,8753],{},[3430,8744,8745,8749,8750,3418],{},[3415,8746,8747],{},[3403,8748,8734],{}," — перевіряє конкретне поле. Метод обов'язково має бути ",[3403,8751,8752],{},"classmethod",[3430,8754,8755,8759],{},[3415,8756,8757],{},[3403,8758,8737],{}," — перевіряє модель цілкома (корисно для перевірки залежностей між кількома полями, наприклад, коли дата завершення має бути пізнішою за дату початку).",[3394,8761,8762],{},"Приклад реалізації обох валідаторів:",[3489,8764,8766],{"className":3491,"code":8765,"filename":3751,"language":3494,"meta":3495,"style":3495},"from datetime import datetime\nfrom pydantic import BaseModel, Field, field_validator, model_validator, ValidationError\n\nclass ProjectCreate(BaseModel):\n    name: str = Field(min_length=3, max_length=50)\n    start_date: datetime\n    end_date: datetime\n\n    @field_validator(\"name\")\n    @classmethod\n    def validate_name_capitalization(cls, value: str) -> str:\n        # Ім'я проєкту має починатися з великої літери\n        if not value[0].isupper():\n            raise ValueError(\"Ім'я проєкту має починатися з великої літери\")\n        return value\n\n    @model_validator(mode=\"after\")\n    def validate_date_range(self) -> \"ProjectCreate\":\n        # self вже є типізованим об'єктом моделі (бо mode=\"after\")\n        if self.end_date \u003C= self.start_date:\n            raise ValueError(\"Дата завершення має бути пізнішою за дату початку\")\n        return self\n\n# Демонстрація роботи валідаторів:\ntry:\n    # 1. Створення коректного проєкту\n    project = ProjectCreate(\n        name=\"TaskForge\",\n        start_date=datetime(2026, 1, 1),\n        end_date=datetime(2026, 12, 31)\n    )\n    print(\"Успішно створено проєкт:\")\n    print(f\"  Назва: {project.name}\")\n    print(f\"  Період: {project.start_date.date()} -> {project.end_date.date()}\\n\")\n\n    # 2. Некоректне ім'я (починається з маленької літери)\n    print(\"Спроба створити проєкт з маленької літери:\")\n    invalid_name = ProjectCreate(\n        name=\"taskforge\",\n        start_date=datetime(2026, 1, 1),\n        end_date=datetime(2026, 12, 31)\n    )\nexcept ValidationError as e:\n    print(f\"  Помилка валідації: {e.errors()[0]['msg']}\\n\")\n\ntry:\n    # 3. Некоректні дати (дата завершення передує даті початку)\n    print(\"Спроба створити проєкт з неправильним діапазоном дат:\")\n    invalid_dates = ProjectCreate(\n        name=\"TaskForge\",\n        start_date=datetime(2026, 12, 31),\n        end_date=datetime(2026, 1, 1)\n    )\nexcept ValidationError as e:\n    print(f\"  Помилка валідації: {e.errors()[0]['msg']}\")\n\n",[3403,8767,8768,8778,8789,8793,8806,8832,8837,8842,8846,8858,8864,8889,8894,8910,8925,8932,8936,8953,8971,8976,8990,9003,9010,9014,9019,9025,9030,9035,9047,9069,9090,9095,9106,9127,9161,9165,9170,9181,9186,9197,9215,9233,9237,9247,9277,9281,9287,9293,9305,9311,9322,9341,9360,9365,9376],{"__ignoreMap":3495},[3499,8769,8770,8772,8774,8776],{"class":3501,"line":3502},[3499,8771,3506],{"class":3505},[3499,8773,7562],{"class":3509},[3499,8775,3513],{"class":3505},[3499,8777,7567],{"class":3509},[3499,8779,8780,8782,8784,8786],{"class":3501,"line":3519},[3499,8781,3506],{"class":3505},[3499,8783,5934],{"class":3509},[3499,8785,3513],{"class":3505},[3499,8787,8788],{"class":3509}," BaseModel, Field, field_validator, model_validator, ValidationError\n",[3499,8790,8791],{"class":3501,"line":3526},[3499,8792,3523],{"emptyLinePlaceholder":3522},[3499,8794,8795,8797,8800,8802,8804],{"class":3501,"line":3533},[3499,8796,3858],{"class":3536},[3499,8798,8799],{"class":3554}," ProjectCreate",[3499,8801,3544],{"class":3509},[3499,8803,6870],{"class":3554},[3499,8805,4393],{"class":3509},[3499,8807,8808,8811,8813,8815,8817,8819,8821,8823,8825,8827,8830],{"class":3501,"line":3571},[3499,8809,8810],{"class":3509},"    name: ",[3499,8812,3560],{"class":3554},[3499,8814,7641],{"class":3509},[3499,8816,7644],{"class":3547},[3499,8818,5964],{"class":3509},[3499,8820,4792],{"class":3771},[3499,8822,3468],{"class":3509},[3499,8824,7653],{"class":3547},[3499,8826,5964],{"class":3509},[3499,8828,8829],{"class":3771},"50",[3499,8831,4876],{"class":3509},[3499,8833,8834],{"class":3501,"line":3577},[3499,8835,8836],{"class":3509},"    start_date: datetime\n",[3499,8838,8839],{"class":3501,"line":3705},[3499,8840,8841],{"class":3509},"    end_date: datetime\n",[3499,8843,8844],{"class":3501,"line":3800},[3499,8845,3523],{"emptyLinePlaceholder":3522},[3499,8847,8848,8851,8853,8856],{"class":3501,"line":3812},[3499,8849,8850],{"class":3540},"    @field_validator",[3499,8852,3544],{"class":3509},[3499,8854,8855],{"class":3979},"\"name\"",[3499,8857,4876],{"class":3509},[3499,8859,8860,8862],{"class":3501,"line":3826},[3499,8861,3951],{"class":3540},[3499,8863,3954],{"class":3554},[3499,8865,8866,8868,8871,8873,8875,8877,8879,8881,8883,8885,8887],{"class":3501,"line":3985},[3499,8867,3868],{"class":3536},[3499,8869,8870],{"class":3540}," validate_name_capitalization",[3499,8872,3544],{"class":3509},[3499,8874,3843],{"class":3547},[3499,8876,3468],{"class":3509},[3499,8878,5158],{"class":3547},[3499,8880,3609],{"class":3509},[3499,8882,3560],{"class":3554},[3499,8884,3887],{"class":3509},[3499,8886,3560],{"class":3554},[3499,8888,3629],{"class":3509},[3499,8890,8891],{"class":3501,"line":3991},[3499,8892,8893],{"class":3529},"        # Ім'я проєкту має починатися з великої літери\n",[3499,8895,8896,8899,8902,8905,8907],{"class":3501,"line":3997},[3499,8897,8898],{"class":3505},"        if",[3499,8900,8901],{"class":3536}," not",[3499,8903,8904],{"class":3509}," value[",[3499,8906,6242],{"class":3771},[3499,8908,8909],{"class":3509},"].isupper():\n",[3499,8911,8912,8915,8918,8920,8923],{"class":3501,"line":4006},[3499,8913,8914],{"class":3505},"            raise",[3499,8916,8917],{"class":3554}," ValueError",[3499,8919,3544],{"class":3509},[3499,8921,8922],{"class":3979},"\"Ім'я проєкту має починатися з великої літери\"",[3499,8924,4876],{"class":3509},[3499,8926,8927,8929],{"class":3501,"line":4593},[3499,8928,3939],{"class":3505},[3499,8930,8931],{"class":3509}," value\n",[3499,8933,8934],{"class":3501,"line":4598},[3499,8935,3523],{"emptyLinePlaceholder":3522},[3499,8937,8938,8941,8943,8946,8948,8951],{"class":3501,"line":4604},[3499,8939,8940],{"class":3540},"    @model_validator",[3499,8942,3544],{"class":3509},[3499,8944,8945],{"class":3547},"mode",[3499,8947,5964],{"class":3509},[3499,8949,8950],{"class":3979},"\"after\"",[3499,8952,4876],{"class":3509},[3499,8954,8955,8957,8960,8962,8964,8966,8969],{"class":3501,"line":5778},[3499,8956,3868],{"class":3536},[3499,8958,8959],{"class":3540}," validate_date_range",[3499,8961,3544],{"class":3509},[3499,8963,3839],{"class":3547},[3499,8965,3887],{"class":3509},[3499,8967,8968],{"class":3979},"\"ProjectCreate\"",[3499,8970,3629],{"class":3509},[3499,8972,8973],{"class":3501,"line":5786},[3499,8974,8975],{"class":3529},"        # self вже є типізованим об'єктом моделі (бо mode=\"after\")\n",[3499,8977,8978,8980,8982,8985,8987],{"class":3501,"line":5791},[3499,8979,8898],{"class":3505},[3499,8981,5426],{"class":3536},[3499,8983,8984],{"class":3509},".end_date \u003C= ",[3499,8986,3839],{"class":3536},[3499,8988,8989],{"class":3509},".start_date:\n",[3499,8991,8992,8994,8996,8998,9001],{"class":3501,"line":5797},[3499,8993,8914],{"class":3505},[3499,8995,8917],{"class":3554},[3499,8997,3544],{"class":3509},[3499,8999,9000],{"class":3979},"\"Дата завершення має бути пізнішою за дату початку\"",[3499,9002,4876],{"class":3509},[3499,9004,9005,9007],{"class":3501,"line":5814},[3499,9006,3939],{"class":3505},[3499,9008,9009],{"class":3536}," self\n",[3499,9011,9012],{"class":3501,"line":7786},[3499,9013,3523],{"emptyLinePlaceholder":3522},[3499,9015,9016],{"class":3501,"line":7792},[3499,9017,9018],{"class":3529},"# Демонстрація роботи валідаторів:\n",[3499,9020,9021,9023],{"class":3501,"line":7816},[3499,9022,7781],{"class":3505},[3499,9024,3629],{"class":3509},[3499,9026,9027],{"class":3501,"line":7828},[3499,9028,9029],{"class":3529},"    # 1. Створення коректного проєкту\n",[3499,9031,9032],{"class":3501,"line":7852},[3499,9033,9034],{"class":3509},"    project = ProjectCreate(\n",[3499,9036,9037,9040,9042,9045],{"class":3501,"line":7875},[3499,9038,9039],{"class":3547},"        name",[3499,9041,5964],{"class":3509},[3499,9043,9044],{"class":3979},"\"TaskForge\"",[3499,9046,8474],{"class":3509},[3499,9048,9049,9052,9055,9058,9060,9062,9064,9066],{"class":3501,"line":7898},[3499,9050,9051],{"class":3547},"        start_date",[3499,9053,9054],{"class":3509},"=datetime(",[3499,9056,9057],{"class":3771},"2026",[3499,9059,3468],{"class":3509},[3499,9061,4783],{"class":3771},[3499,9063,3468],{"class":3509},[3499,9065,4783],{"class":3771},[3499,9067,9068],{"class":3509},"),\n",[3499,9070,9071,9074,9076,9078,9080,9083,9085,9088],{"class":3501,"line":7925},[3499,9072,9073],{"class":3547},"        end_date",[3499,9075,9054],{"class":3509},[3499,9077,9057],{"class":3771},[3499,9079,3468],{"class":3509},[3499,9081,9082],{"class":3771},"12",[3499,9084,3468],{"class":3509},[3499,9086,9087],{"class":3771},"31",[3499,9089,4876],{"class":3509},[3499,9091,9092],{"class":3501,"line":7930},[3499,9093,9094],{"class":3509},"    )\n",[3499,9096,9097,9099,9101,9104],{"class":3501,"line":7936},[3499,9098,6236],{"class":3540},[3499,9100,3544],{"class":3509},[3499,9102,9103],{"class":3979},"\"Успішно створено проєкт:\"",[3499,9105,4876],{"class":3509},[3499,9107,9108,9110,9112,9114,9116,9118,9121,9123,9125],{"class":3501,"line":7948},[3499,9109,6236],{"class":3540},[3499,9111,3544],{"class":3509},[3499,9113,5692],{"class":3536},[3499,9115,7861],{"class":3979},[3499,9117,4085],{"class":3536},[3499,9119,9120],{"class":3509},"project.name",[3499,9122,4094],{"class":3536},[3499,9124,7847],{"class":3979},[3499,9126,4876],{"class":3509},[3499,9128,9129,9131,9133,9135,9138,9140,9143,9145,9148,9150,9153,9155,9157,9159],{"class":3501,"line":7974},[3499,9130,6236],{"class":3540},[3499,9132,3544],{"class":3509},[3499,9134,5692],{"class":3536},[3499,9136,9137],{"class":3979},"\"  Період: ",[3499,9139,4085],{"class":3536},[3499,9141,9142],{"class":3509},"project.start_date.date()",[3499,9144,4094],{"class":3536},[3499,9146,9147],{"class":3979}," -> ",[3499,9149,4085],{"class":3536},[3499,9151,9152],{"class":3509},"project.end_date.date()",[3499,9154,4094],{"class":3536},[3499,9156,7918],{"class":7917},[3499,9158,7847],{"class":3979},[3499,9160,4876],{"class":3509},[3499,9162,9163],{"class":3501,"line":7979},[3499,9164,3523],{"emptyLinePlaceholder":3522},[3499,9166,9167],{"class":3501,"line":7985},[3499,9168,9169],{"class":3529},"    # 2. Некоректне ім'я (починається з маленької літери)\n",[3499,9171,9172,9174,9176,9179],{"class":3501,"line":7997},[3499,9173,6236],{"class":3540},[3499,9175,3544],{"class":3509},[3499,9177,9178],{"class":3979},"\"Спроба створити проєкт з маленької літери:\"",[3499,9180,4876],{"class":3509},[3499,9182,9183],{"class":3501,"line":8003},[3499,9184,9185],{"class":3509},"    invalid_name = ProjectCreate(\n",[3499,9187,9188,9190,9192,9195],{"class":3501,"line":8018},[3499,9189,9039],{"class":3547},[3499,9191,5964],{"class":3509},[3499,9193,9194],{"class":3979},"\"taskforge\"",[3499,9196,8474],{"class":3509},[3499,9198,9199,9201,9203,9205,9207,9209,9211,9213],{"class":3501,"line":8053},[3499,9200,9051],{"class":3547},[3499,9202,9054],{"class":3509},[3499,9204,9057],{"class":3771},[3499,9206,3468],{"class":3509},[3499,9208,4783],{"class":3771},[3499,9210,3468],{"class":3509},[3499,9212,4783],{"class":3771},[3499,9214,9068],{"class":3509},[3499,9216,9217,9219,9221,9223,9225,9227,9229,9231],{"class":3501,"line":8058},[3499,9218,9073],{"class":3547},[3499,9220,9054],{"class":3509},[3499,9222,9057],{"class":3771},[3499,9224,3468],{"class":3509},[3499,9226,9082],{"class":3771},[3499,9228,3468],{"class":3509},[3499,9230,9087],{"class":3771},[3499,9232,4876],{"class":3509},[3499,9234,9235],{"class":3501,"line":8065},[3499,9236,9094],{"class":3509},[3499,9238,9239,9241,9243,9245],{"class":3501,"line":8071},[3499,9240,8006],{"class":3505},[3499,9242,8009],{"class":3509},[3499,9244,8012],{"class":3505},[3499,9246,8015],{"class":3509},[3499,9248,9249,9251,9253,9255,9257,9259,9261,9263,9265,9267,9269,9271,9273,9275],{"class":3501,"line":8083},[3499,9250,6236],{"class":3540},[3499,9252,3544],{"class":3509},[3499,9254,5692],{"class":3536},[3499,9256,8027],{"class":3979},[3499,9258,4085],{"class":3536},[3499,9260,8032],{"class":3509},[3499,9262,6242],{"class":3771},[3499,9264,5881],{"class":3509},[3499,9266,8039],{"class":3979},[3499,9268,8042],{"class":3509},[3499,9270,4094],{"class":3536},[3499,9272,7918],{"class":7917},[3499,9274,7847],{"class":3979},[3499,9276,4876],{"class":3509},[3499,9278,9279],{"class":3501,"line":8108},[3499,9280,3523],{"emptyLinePlaceholder":3522},[3499,9282,9283,9285],{"class":3501,"line":8119},[3499,9284,7781],{"class":3505},[3499,9286,3629],{"class":3509},[3499,9288,9290],{"class":3501,"line":9289},47,[3499,9291,9292],{"class":3529},"    # 3. Некоректні дати (дата завершення передує даті початку)\n",[3499,9294,9296,9298,9300,9303],{"class":3501,"line":9295},48,[3499,9297,6236],{"class":3540},[3499,9299,3544],{"class":3509},[3499,9301,9302],{"class":3979},"\"Спроба створити проєкт з неправильним діапазоном дат:\"",[3499,9304,4876],{"class":3509},[3499,9306,9308],{"class":3501,"line":9307},49,[3499,9309,9310],{"class":3509},"    invalid_dates = ProjectCreate(\n",[3499,9312,9314,9316,9318,9320],{"class":3501,"line":9313},50,[3499,9315,9039],{"class":3547},[3499,9317,5964],{"class":3509},[3499,9319,9044],{"class":3979},[3499,9321,8474],{"class":3509},[3499,9323,9325,9327,9329,9331,9333,9335,9337,9339],{"class":3501,"line":9324},51,[3499,9326,9051],{"class":3547},[3499,9328,9054],{"class":3509},[3499,9330,9057],{"class":3771},[3499,9332,3468],{"class":3509},[3499,9334,9082],{"class":3771},[3499,9336,3468],{"class":3509},[3499,9338,9087],{"class":3771},[3499,9340,9068],{"class":3509},[3499,9342,9344,9346,9348,9350,9352,9354,9356,9358],{"class":3501,"line":9343},52,[3499,9345,9073],{"class":3547},[3499,9347,9054],{"class":3509},[3499,9349,9057],{"class":3771},[3499,9351,3468],{"class":3509},[3499,9353,4783],{"class":3771},[3499,9355,3468],{"class":3509},[3499,9357,4783],{"class":3771},[3499,9359,4876],{"class":3509},[3499,9361,9363],{"class":3501,"line":9362},53,[3499,9364,9094],{"class":3509},[3499,9366,9368,9370,9372,9374],{"class":3501,"line":9367},54,[3499,9369,8006],{"class":3505},[3499,9371,8009],{"class":3509},[3499,9373,8012],{"class":3505},[3499,9375,8015],{"class":3509},[3499,9377,9379,9381,9383,9385,9387,9389,9391,9393,9395,9397,9399,9401,9403],{"class":3501,"line":9378},55,[3499,9380,6236],{"class":3540},[3499,9382,3544],{"class":3509},[3499,9384,5692],{"class":3536},[3499,9386,8027],{"class":3979},[3499,9388,4085],{"class":3536},[3499,9390,8032],{"class":3509},[3499,9392,6242],{"class":3771},[3499,9394,5881],{"class":3509},[3499,9396,8039],{"class":3979},[3499,9398,8042],{"class":3509},[3499,9400,4094],{"class":3536},[3499,9402,7847],{"class":3979},[3499,9404,4876],{"class":3509},[3486,9406,9407,9445,9467],{},[3489,9408,9409],{"className":6765,"code":8150,"filename":6959,"language":6768,"meta":3495,"style":3495},[3403,9410,9411,9415,9425,9431,9439],{"__ignoreMap":3495},[3499,9412,9413],{"class":3501,"line":3502},[3499,9414,8157],{"class":3529},[3499,9416,9417,9419,9421,9423],{"class":3501,"line":3519},[3499,9418,3494],{"class":3540},[3499,9420,6973],{"class":3536},[3499,9422,6976],{"class":3979},[3499,9424,6979],{"class":3979},[3499,9426,9427,9429],{"class":3501,"line":3526},[3499,9428,6984],{"class":3540},[3499,9430,7084],{"class":3979},[3499,9432,9433,9435,9437],{"class":3501,"line":3533},[3499,9434,6959],{"class":3540},[3499,9436,7011],{"class":3979},[3499,9438,7014],{"class":3979},[3499,9440,9441,9443],{"class":3501,"line":3571},[3499,9442,3494],{"class":3540},[3499,9444,7030],{"class":3979},[3489,9446,9448],{"className":6765,"code":9447,"filename":7034,"language":6768,"meta":3495,"style":3495},"# uv run автоматично запустить файл із встановленим pydantic\nuv run --with pydantic main.py\n",[3403,9449,9450,9455],{"__ignoreMap":3495},[3499,9451,9452],{"class":3501,"line":3502},[3499,9453,9454],{"class":3529},"# uv run автоматично запустить файл із встановленим pydantic\n",[3499,9456,9457,9459,9461,9463,9465],{"class":3501,"line":3519},[3499,9458,7034],{"class":3540},[3499,9460,7053],{"class":3979},[3499,9462,7056],{"class":3536},[3499,9464,7059],{"class":3979},[3499,9466,7030],{"class":3979},[3489,9468,9470],{"className":6765,"code":9469,"filename":7105,"language":6768,"meta":3495,"style":3495},"# Додаємо залежність та запускаємо через poetry\npoetry init -n\npoetry add pydantic\npoetry run python main.py\n",[3403,9471,9472,9477,9485,9493],{"__ignoreMap":3495},[3499,9473,9474],{"class":3501,"line":3502},[3499,9475,9476],{"class":3529},"# Додаємо залежність та запускаємо через poetry\n",[3499,9478,9479,9481,9483],{"class":3501,"line":3519},[3499,9480,7105],{"class":3540},[3499,9482,7119],{"class":3979},[3499,9484,7122],{"class":3536},[3499,9486,9487,9489,9491],{"class":3501,"line":3526},[3499,9488,7105],{"class":3540},[3499,9490,5378],{"class":3979},[3499,9492,7014],{"class":3979},[3499,9494,9495,9497,9499,9501],{"class":3501,"line":3533},[3499,9496,7105],{"class":3540},[3499,9498,7053],{"class":3979},[3499,9500,7157],{"class":3979},[3499,9502,7030],{"class":3979},[4612,9504,9505,9515],{},[3394,9506,9507],{},[3415,9508,9509,9510,9512,9513,6649],{},"Параметр ",[3403,9511,8945],{}," у ",[3403,9514,8737],{},[4697,9516,9517,9531],{},[3430,9518,9519,9522,9523,9526,9527,9530],{},[3403,9520,9521],{},"mode=\"before\"",": метод викликається ",[3415,9524,9525],{},"до"," перевірки типів Pydantic. Він отримує сирий словник (",[3403,9528,9529],{},"dict[str, Any]","), який прийшов від користувача. Це корисно для попередньої обробки ключів (наприклад, якщо назви полів прийшли в CamelCase, а треба PascalCase).",[3430,9532,9533,9522,9536,9539],{},[3403,9534,9535],{},"mode=\"after\"",[3415,9537,9538],{},"після"," успішної перевірки типів. Він працює з готовим екземпляром класу, що гарантує наявність полів правильного типу.",[3741,9541,9543,9544,4140,9547],{"id":9542},"_2-сучасний-підхід-beforevalidator-та-aftervalidator","2. Сучасний підхід: ",[3403,9545,9546],{},"BeforeValidator",[3403,9548,9549],{},"AfterValidator",[3394,9551,9552,9553,9555],{},"У Pydantic v2 з'явилася можливість винести логіку валідації за межі класу моделі за допомогою обгорток типів через ",[3403,9554,5910],{},". Це робить моделі чистішими та дозволяє перевикористовувати валідатори у різних моделях (патерн DRY).",[4697,9557,9558,9565],{},[3430,9559,9560,9564],{},[3415,9561,9562],{},[3403,9563,9546],{},": запускається перед стандартними перевірками Pydantic (працює з сирими даними).",[3430,9566,9567,9571],{},[3415,9568,9569],{},[3403,9570,9549],{},": запускається після стандартних перевінок Pydantic (працює з вже приведеніми типами).",[3489,9573,9575],{"className":3491,"code":9574,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import Annotated\nfrom pydantic import BaseModel, AfterValidator, ValidationError\n\ndef ensure_trimmed(value: str) -> str:\n    # Валідатор, який очищає пробіли по краях\n    return value.strip()\n\ndef ensure_not_empty(value: str) -> str:\n    if not value:\n        raise ValueError(\"Рядок не може бути порожнім\")\n    return value\n\n# Створюємо перевикористовуваний тип\nCleanString = Annotated[str, AfterValidator(ensure_trimmed), AfterValidator(ensure_not_empty)]\n\nclass UserProfile(BaseModel):\n    username: CleanString\n    bio: CleanString\n\n# Демонстрація роботи валідаторів:\ntry:\n    # 1. Створення профілю з пробілами по краях полів\n    profile = UserProfile(username=\"  alex_smith  \", bio=\"  Software Engineer from kostyl.dev  \")\n    print(\"Успішно створено профіль (пробіли автоматично обрізано):\")\n    print(f\"  username: '{profile.username}'\")\n    print(f\"  bio: '{profile.bio}'\\n\")\n    \n    # 2. Спроба передати порожній рядок (або рядок лише з пробілів)\n    print(\"Спроба створити профіль з порожнім username:\")\n    invalid_profile = UserProfile(username=\"   \", bio=\"Developer\")\nexcept ValidationError as e:\n    print(f\"  Помилка валідації: {e.errors()[0]['msg']}\")\n\n",[3403,9576,9577,9587,9598,9602,9623,9628,9635,9639,9660,9669,9682,9688,9692,9697,9707,9711,9724,9729,9734,9738,9742,9748,9753,9778,9789,9812,9839,9843,9848,9859,9882,9892],{"__ignoreMap":3495},[3499,9578,9579,9581,9583,9585],{"class":3501,"line":3502},[3499,9580,3506],{"class":3505},[3499,9582,3510],{"class":3509},[3499,9584,3513],{"class":3505},[3499,9586,5927],{"class":3509},[3499,9588,9589,9591,9593,9595],{"class":3501,"line":3519},[3499,9590,3506],{"class":3505},[3499,9592,5934],{"class":3509},[3499,9594,3513],{"class":3505},[3499,9596,9597],{"class":3509}," BaseModel, AfterValidator, ValidationError\n",[3499,9599,9600],{"class":3501,"line":3526},[3499,9601,3523],{"emptyLinePlaceholder":3522},[3499,9603,9604,9606,9609,9611,9613,9615,9617,9619,9621],{"class":3501,"line":3533},[3499,9605,3537],{"class":3536},[3499,9607,9608],{"class":3540}," ensure_trimmed",[3499,9610,3544],{"class":3509},[3499,9612,5158],{"class":3547},[3499,9614,3609],{"class":3509},[3499,9616,3560],{"class":3554},[3499,9618,3887],{"class":3509},[3499,9620,3560],{"class":3554},[3499,9622,3629],{"class":3509},[3499,9624,9625],{"class":3501,"line":3571},[3499,9626,9627],{"class":3529},"    # Валідатор, який очищає пробіли по краях\n",[3499,9629,9630,9632],{"class":3501,"line":3577},[3499,9631,4076],{"class":3505},[3499,9633,9634],{"class":3509}," value.strip()\n",[3499,9636,9637],{"class":3501,"line":3705},[3499,9638,3523],{"emptyLinePlaceholder":3522},[3499,9640,9641,9643,9646,9648,9650,9652,9654,9656,9658],{"class":3501,"line":3800},[3499,9642,3537],{"class":3536},[3499,9644,9645],{"class":3540}," ensure_not_empty",[3499,9647,3544],{"class":3509},[3499,9649,5158],{"class":3547},[3499,9651,3609],{"class":3509},[3499,9653,3560],{"class":3554},[3499,9655,3887],{"class":3509},[3499,9657,3560],{"class":3554},[3499,9659,3629],{"class":3509},[3499,9661,9662,9664,9666],{"class":3501,"line":3812},[3499,9663,5067],{"class":3505},[3499,9665,8901],{"class":3536},[3499,9667,9668],{"class":3509}," value:\n",[3499,9670,9671,9673,9675,9677,9680],{"class":3501,"line":3826},[3499,9672,4371],{"class":3505},[3499,9674,8917],{"class":3554},[3499,9676,3544],{"class":3509},[3499,9678,9679],{"class":3979},"\"Рядок не може бути порожнім\"",[3499,9681,4876],{"class":3509},[3499,9683,9684,9686],{"class":3501,"line":3985},[3499,9685,4076],{"class":3505},[3499,9687,8931],{"class":3509},[3499,9689,9690],{"class":3501,"line":3991},[3499,9691,3523],{"emptyLinePlaceholder":3522},[3499,9693,9694],{"class":3501,"line":3997},[3499,9695,9696],{"class":3529},"# Створюємо перевикористовуваний тип\n",[3499,9698,9699,9702,9704],{"class":3501,"line":4006},[3499,9700,9701],{"class":3509},"CleanString = Annotated[",[3499,9703,3560],{"class":3554},[3499,9705,9706],{"class":3509},", AfterValidator(ensure_trimmed), AfterValidator(ensure_not_empty)]\n",[3499,9708,9709],{"class":3501,"line":4593},[3499,9710,3523],{"emptyLinePlaceholder":3522},[3499,9712,9713,9715,9718,9720,9722],{"class":3501,"line":4598},[3499,9714,3858],{"class":3536},[3499,9716,9717],{"class":3554}," UserProfile",[3499,9719,3544],{"class":3509},[3499,9721,6870],{"class":3554},[3499,9723,4393],{"class":3509},[3499,9725,9726],{"class":3501,"line":4604},[3499,9727,9728],{"class":3509},"    username: CleanString\n",[3499,9730,9731],{"class":3501,"line":5778},[3499,9732,9733],{"class":3509},"    bio: CleanString\n",[3499,9735,9736],{"class":3501,"line":5786},[3499,9737,3523],{"emptyLinePlaceholder":3522},[3499,9739,9740],{"class":3501,"line":5791},[3499,9741,9018],{"class":3529},[3499,9743,9744,9746],{"class":3501,"line":5797},[3499,9745,7781],{"class":3505},[3499,9747,3629],{"class":3509},[3499,9749,9750],{"class":3501,"line":5814},[3499,9751,9752],{"class":3529},"    # 1. Створення профілю з пробілами по краях полів\n",[3499,9754,9755,9758,9761,9763,9766,9768,9771,9773,9776],{"class":3501,"line":7786},[3499,9756,9757],{"class":3509},"    profile = UserProfile(",[3499,9759,9760],{"class":3547},"username",[3499,9762,5964],{"class":3509},[3499,9764,9765],{"class":3979},"\"  alex_smith  \"",[3499,9767,3468],{"class":3509},[3499,9769,9770],{"class":3547},"bio",[3499,9772,5964],{"class":3509},[3499,9774,9775],{"class":3979},"\"  Software Engineer from kostyl.dev  \"",[3499,9777,4876],{"class":3509},[3499,9779,9780,9782,9784,9787],{"class":3501,"line":7792},[3499,9781,6236],{"class":3540},[3499,9783,3544],{"class":3509},[3499,9785,9786],{"class":3979},"\"Успішно створено профіль (пробіли автоматично обрізано):\"",[3499,9788,4876],{"class":3509},[3499,9790,9791,9793,9795,9797,9800,9802,9805,9807,9810],{"class":3501,"line":7816},[3499,9792,6236],{"class":3540},[3499,9794,3544],{"class":3509},[3499,9796,5692],{"class":3536},[3499,9798,9799],{"class":3979},"\"  username: '",[3499,9801,4085],{"class":3536},[3499,9803,9804],{"class":3509},"profile.username",[3499,9806,4094],{"class":3536},[3499,9808,9809],{"class":3979},"'\"",[3499,9811,4876],{"class":3509},[3499,9813,9814,9816,9818,9820,9823,9825,9828,9830,9833,9835,9837],{"class":3501,"line":7828},[3499,9815,6236],{"class":3540},[3499,9817,3544],{"class":3509},[3499,9819,5692],{"class":3536},[3499,9821,9822],{"class":3979},"\"  bio: '",[3499,9824,4085],{"class":3536},[3499,9826,9827],{"class":3509},"profile.bio",[3499,9829,4094],{"class":3536},[3499,9831,9832],{"class":3979},"'",[3499,9834,7918],{"class":7917},[3499,9836,7847],{"class":3979},[3499,9838,4876],{"class":3509},[3499,9840,9841],{"class":3501,"line":7852},[3499,9842,7626],{"class":3509},[3499,9844,9845],{"class":3501,"line":7875},[3499,9846,9847],{"class":3529},"    # 2. Спроба передати порожній рядок (або рядок лише з пробілів)\n",[3499,9849,9850,9852,9854,9857],{"class":3501,"line":7898},[3499,9851,6236],{"class":3540},[3499,9853,3544],{"class":3509},[3499,9855,9856],{"class":3979},"\"Спроба створити профіль з порожнім username:\"",[3499,9858,4876],{"class":3509},[3499,9860,9861,9864,9866,9868,9871,9873,9875,9877,9880],{"class":3501,"line":7925},[3499,9862,9863],{"class":3509},"    invalid_profile = UserProfile(",[3499,9865,9760],{"class":3547},[3499,9867,5964],{"class":3509},[3499,9869,9870],{"class":3979},"\"   \"",[3499,9872,3468],{"class":3509},[3499,9874,9770],{"class":3547},[3499,9876,5964],{"class":3509},[3499,9878,9879],{"class":3979},"\"Developer\"",[3499,9881,4876],{"class":3509},[3499,9883,9884,9886,9888,9890],{"class":3501,"line":7930},[3499,9885,8006],{"class":3505},[3499,9887,8009],{"class":3509},[3499,9889,8012],{"class":3505},[3499,9891,8015],{"class":3509},[3499,9893,9894,9896,9898,9900,9902,9904,9906,9908,9910,9912,9914,9916,9918],{"class":3501,"line":7936},[3499,9895,6236],{"class":3540},[3499,9897,3544],{"class":3509},[3499,9899,5692],{"class":3536},[3499,9901,8027],{"class":3979},[3499,9903,4085],{"class":3536},[3499,9905,8032],{"class":3509},[3499,9907,6242],{"class":3771},[3499,9909,5881],{"class":3509},[3499,9911,8039],{"class":3979},[3499,9913,8042],{"class":3509},[3499,9915,4094],{"class":3536},[3499,9917,7847],{"class":3979},[3499,9919,4876],{"class":3509},[3486,9921,9922,9962,9979],{},[3489,9923,9925],{"className":6765,"code":9924,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Запуск прикладу з Before\u002FAfter валідаторами\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install pydantic\npython main.py\n",[3403,9926,9927,9932,9942,9948,9956],{"__ignoreMap":3495},[3499,9928,9929],{"class":3501,"line":3502},[3499,9930,9931],{"class":3529},"# Запуск прикладу з Before\u002FAfter валідаторами\n",[3499,9933,9934,9936,9938,9940],{"class":3501,"line":3519},[3499,9935,3494],{"class":3540},[3499,9937,6973],{"class":3536},[3499,9939,6976],{"class":3979},[3499,9941,6979],{"class":3979},[3499,9943,9944,9946],{"class":3501,"line":3526},[3499,9945,6984],{"class":3540},[3499,9947,7084],{"class":3979},[3499,9949,9950,9952,9954],{"class":3501,"line":3533},[3499,9951,6959],{"class":3540},[3499,9953,7011],{"class":3979},[3499,9955,7014],{"class":3979},[3499,9957,9958,9960],{"class":3501,"line":3571},[3499,9959,3494],{"class":3540},[3499,9961,7030],{"class":3979},[3489,9963,9965],{"className":6765,"code":9964,"filename":7034,"language":6768,"meta":3495,"style":3495},"uv run --with pydantic main.py\n",[3403,9966,9967],{"__ignoreMap":3495},[3499,9968,9969,9971,9973,9975,9977],{"class":3501,"line":3502},[3499,9970,7034],{"class":3540},[3499,9972,7053],{"class":3979},[3499,9974,7056],{"class":3536},[3499,9976,7059],{"class":3979},[3499,9978,7030],{"class":3979},[3489,9980,9981],{"className":6765,"code":8212,"filename":7105,"language":6768,"meta":3495,"style":3495},[3403,9982,9983,9991,9999],{"__ignoreMap":3495},[3499,9984,9985,9987,9989],{"class":3501,"line":3502},[3499,9986,7105],{"class":3540},[3499,9988,7119],{"class":3979},[3499,9990,7122],{"class":3536},[3499,9992,9993,9995,9997],{"class":3501,"line":3519},[3499,9994,7105],{"class":3540},[3499,9996,5378],{"class":3979},[3499,9998,7014],{"class":3979},[3499,10000,10001,10003,10005,10007],{"class":3501,"line":3526},[3499,10002,7105],{"class":3540},[3499,10004,7053],{"class":3979},[3499,10006,7157],{"class":3979},[3499,10008,7030],{"class":3979},[3709,10010,10011,10014,10015,10017,10018,10021],{},[3415,10012,10013],{},"Чому це краще за класичний підхід:","\nУ великих проєктах правила на кшталт \"очистити пробіли\" або \"перевірити формат телефону\" потрібні для десятків різних полів у різних моделях. Замість копіювання методів ",[3403,10016,8734],{}," у кожен клас, ми створюємо один типізований аліас (наприклад, ",[3403,10019,10020],{},"PhoneNumber = Annotated[str, AfterValidator(validate_phone)]",") і використовуємо його всюди.",[3452,10023],{},[3474,10025,10027,10028],{"id":10026},"анатомія-помилки-validationerror","Анатомія помилки: ",[3403,10029,7353],{},[3394,10031,10032,10033,10035,10036,10038],{},"Коли вхідні дані не відповідають описаній схемі, Pydantic викидає виняток ",[3403,10034,7353],{},". Для розробників з екосистеми C#, де винятки часто бувають текстовими або містять кастомні структури помилок, structured формат ",[3403,10037,7353],{}," у Pydantic є надзвичайно зручним інструментом.",[3394,10040,10041,10042,10044,10045,10048],{},"Об'єкт ",[3403,10043,7353],{}," містить повну інформацію про всі виявлені проблеми. Викликавши метод ",[3403,10046,10047],{},"e.errors()",", ви отримаєте список словників, кожен з яких описує конкретну помилку:",[3489,10050,10055],{"className":10051,"code":10052,"filename":10053,"language":10054,"meta":3495,"style":3495},"language-json shiki shiki-themes light-plus dark-plus dark-plus","[\n  {\n    \"type\": \"less_than\",\n    \"loc\": [\"price\"],\n    \"msg\": \"Input should be greater than 0\",\n    \"input\": -10.0,\n    \"ctx\": {\"gt\": 0.0},\n    \"url\": \"https:\u002F\u002Ferrors.pydantic.dev\u002F2.10\u002Fv\u002Fless_than\"\n  }\n]\n","data.json","json",[3403,10056,10057,10062,10067,10080,10094,10106,10118,10137,10147,10152],{"__ignoreMap":3495},[3499,10058,10059],{"class":3501,"line":3502},[3499,10060,10061],{"class":3509},"[\n",[3499,10063,10064],{"class":3501,"line":3519},[3499,10065,10066],{"class":3509},"  {\n",[3499,10068,10069,10073,10075,10078],{"class":3501,"line":3526},[3499,10070,10072],{"class":10071},"sLwNe","    \"type\"",[3499,10074,3609],{"class":3509},[3499,10076,10077],{"class":3979},"\"less_than\"",[3499,10079,8474],{"class":3509},[3499,10081,10082,10085,10088,10091],{"class":3501,"line":3533},[3499,10083,10084],{"class":10071},"    \"loc\"",[3499,10086,10087],{"class":3509},": [",[3499,10089,10090],{"class":3979},"\"price\"",[3499,10092,10093],{"class":3509},"],\n",[3499,10095,10096,10099,10101,10104],{"class":3501,"line":3571},[3499,10097,10098],{"class":10071},"    \"msg\"",[3499,10100,3609],{"class":3509},[3499,10102,10103],{"class":3979},"\"Input should be greater than 0\"",[3499,10105,8474],{"class":3509},[3499,10107,10108,10111,10113,10116],{"class":3501,"line":3577},[3499,10109,10110],{"class":10071},"    \"input\"",[3499,10112,3609],{"class":3509},[3499,10114,10115],{"class":3771},"-10.0",[3499,10117,8474],{"class":3509},[3499,10119,10120,10123,10126,10129,10131,10134],{"class":3501,"line":3705},[3499,10121,10122],{"class":10071},"    \"ctx\"",[3499,10124,10125],{"class":3509},": {",[3499,10127,10128],{"class":10071},"\"gt\"",[3499,10130,3609],{"class":3509},[3499,10132,10133],{"class":3771},"0.0",[3499,10135,10136],{"class":3509},"},\n",[3499,10138,10139,10142,10144],{"class":3501,"line":3800},[3499,10140,10141],{"class":10071},"    \"url\"",[3499,10143,3609],{"class":3509},[3499,10145,10146],{"class":3979},"\"https:\u002F\u002Ferrors.pydantic.dev\u002F2.10\u002Fv\u002Fless_than\"\n",[3499,10148,10149],{"class":3501,"line":3812},[3499,10150,10151],{"class":3509},"  }\n",[3499,10153,10154],{"class":3501,"line":3826},[3499,10155,3676],{"class":3509},[7430,10157,10158,10176,10180,10193,10197],{},[7433,10159,10162,10163,10165,10166,10169,10170,10173,10174,3418],{"name":10160,"type":10161},"loc","tuple[str | int, ...]","Шлях до помилкового поля. Якщо помилка сталася у вкладеній структурі, ",[3403,10164,10160],{}," міститиме повний ланцюжок ключів та індексів. Наприклад, ",[3403,10167,10168],{},"(\"items\", 0, \"price\")"," вказує на те, що помилка сталася в першому елементі списку ",[3403,10171,10172],{},"items"," у полі ",[3403,10175,7806],{},[7433,10177,10179],{"name":10178,"type":4933},"input","Значення, яке було передано на вхід і не пройшло валідацію (корисно для логування та дебагу).",[7433,10181,10182,10183,3468,10186,3468,10189,10192],{"name":3654,"type":3560},"Унікальний ідентифікатор типу помилки (наприклад, ",[3403,10184,10185],{},"missing",[3403,10187,10188],{},"int_parsing",[3403,10190,10191],{},"value_error","). Використовується для програмної обробки та локалізації.",[7433,10194,10196],{"name":10195,"type":3560},"msg","Зрозуміле повідомлення про помилку англійською мовою.",[7433,10198,10200],{"name":10199,"type":3560},"url","Посилання на детальну документацію Pydantic з описом цієї конкретної помилки та шляхами її виправлення.",[3452,10202],{},[3474,10204,10206],{"id":10205},"поліморфна-валідація-discriminated-unions","Поліморфна валідація: Discriminated Unions",[3394,10208,10209,10210,4744],{},"У веброзробці часто виникає потреба обробляти поліморфні дані — коли тип об'єкта визначається спеціальним полем-маркером (дискримінатором). У C# для цього зазвичай використовуються атрибути поліморфізму System.Text.Json (наприклад, ",[3403,10211,10212],{},"[JsonPolymorphic]",[3394,10214,10215,10216,10219,10220,3840,10223,10226,10227,6649],{},"У Pydantic v2 це реалізується через ",[3415,10217,10218],{},"Discriminated Unions"," за допомогою об'єднання типів (",[3403,10221,10222],{},"Union",[3403,10224,10225],{},"|",") та вказівки дискримінатора у ",[3403,10228,7420],{},[3489,10230,10232],{"className":3491,"code":10231,"filename":3751,"language":3494,"meta":3495,"style":3495},"from typing import Literal, Union\nfrom pydantic import BaseModel, Field, ValidationError\n\nclass ProgrammingTask(BaseModel):\n    type: Literal[\"code\"] = \"code\" # Поле-дискримінатор\n    language: str\n    repository: str\n\nclass DesignTask(BaseModel):\n    type: Literal[\"design\"] = \"design\" # Поле-дискримінатор\n    tool: str\n    pages_count: int\n\n# Об'єднуємо моделі та вказуємо поле-дискримінатор\nclass TaskForm(BaseModel):\n    task: Union[ProgrammingTask, DesignTask] = Field(..., discriminator=\"type\")\n\n# Демонстрація поліморфного парсингу:\ntry:\n    # 1. Передаємо дані для кодингу\n    form1 = TaskForm(task={\"type\": \"code\", \"language\": \"Python\", \"repository\": \"github.com\"})\n    print(\"Успішно спарсено задачу програмування:\")\n    print(f\"  Тип: {type(form1.task)}\")\n    print(f\"  Мова: {form1.task.language}\\n\")\n\n    # 2. Передаємо дані для дизайну\n    form2 = TaskForm(task={\"type\": \"design\", \"tool\": \"Figma\", \"pages_count\": 5})\n    print(\"Успішно спарсено задачу дизайну:\")\n    print(f\"  Тип: {type(form2.task)}\")\n    print(f\"  Кількість сторінок: {form2.task.pages_count}\\n\")\n\n    # 3. Передаємо некоректний тип\n    form3 = TaskForm(task={\"type\": \"unknown_type\"})\nexcept ValidationError as e:\n    print(f\"Помилка валідації поліморфної форми:\\n  {e.errors()[0]['msg']}\")\n",[3403,10233,10234,10245,10255,10259,10272,10290,10297,10304,10308,10321,10336,10343,10350,10354,10359,10372,10387,10391,10396,10402,10407,10447,10458,10482,10506,10510,10515,10551,10562,10585,10609,10613,10618,10636,10646],{"__ignoreMap":3495},[3499,10235,10236,10238,10240,10242],{"class":3501,"line":3502},[3499,10237,3506],{"class":3505},[3499,10239,3510],{"class":3509},[3499,10241,3513],{"class":3505},[3499,10243,10244],{"class":3509}," Literal, Union\n",[3499,10246,10247,10249,10251,10253],{"class":3501,"line":3519},[3499,10248,3506],{"class":3505},[3499,10250,5934],{"class":3509},[3499,10252,3513],{"class":3505},[3499,10254,7578],{"class":3509},[3499,10256,10257],{"class":3501,"line":3526},[3499,10258,3523],{"emptyLinePlaceholder":3522},[3499,10260,10261,10263,10266,10268,10270],{"class":3501,"line":3533},[3499,10262,3858],{"class":3536},[3499,10264,10265],{"class":3554}," ProgrammingTask",[3499,10267,3544],{"class":3509},[3499,10269,6870],{"class":3554},[3499,10271,4393],{"class":3509},[3499,10273,10274,10277,10280,10283,10285,10287],{"class":3501,"line":3571},[3499,10275,10276],{"class":3554},"    type",[3499,10278,10279],{"class":3509},": Literal[",[3499,10281,10282],{"class":3979},"\"code\"",[3499,10284,3820],{"class":3509},[3499,10286,10282],{"class":3979},[3499,10288,10289],{"class":3529}," # Поле-дискримінатор\n",[3499,10291,10292,10295],{"class":3501,"line":3577},[3499,10293,10294],{"class":3509},"    language: ",[3499,10296,3664],{"class":3554},[3499,10298,10299,10302],{"class":3501,"line":3705},[3499,10300,10301],{"class":3509},"    repository: ",[3499,10303,3664],{"class":3554},[3499,10305,10306],{"class":3501,"line":3800},[3499,10307,3523],{"emptyLinePlaceholder":3522},[3499,10309,10310,10312,10315,10317,10319],{"class":3501,"line":3812},[3499,10311,3858],{"class":3536},[3499,10313,10314],{"class":3554}," DesignTask",[3499,10316,3544],{"class":3509},[3499,10318,6870],{"class":3554},[3499,10320,4393],{"class":3509},[3499,10322,10323,10325,10327,10330,10332,10334],{"class":3501,"line":3826},[3499,10324,10276],{"class":3554},[3499,10326,10279],{"class":3509},[3499,10328,10329],{"class":3979},"\"design\"",[3499,10331,3820],{"class":3509},[3499,10333,10329],{"class":3979},[3499,10335,10289],{"class":3529},[3499,10337,10338,10341],{"class":3501,"line":3985},[3499,10339,10340],{"class":3509},"    tool: ",[3499,10342,3664],{"class":3554},[3499,10344,10345,10348],{"class":3501,"line":3991},[3499,10346,10347],{"class":3509},"    pages_count: ",[3499,10349,6882],{"class":3554},[3499,10351,10352],{"class":3501,"line":3997},[3499,10353,3523],{"emptyLinePlaceholder":3522},[3499,10355,10356],{"class":3501,"line":4006},[3499,10357,10358],{"class":3529},"# Об'єднуємо моделі та вказуємо поле-дискримінатор\n",[3499,10360,10361,10363,10366,10368,10370],{"class":3501,"line":4593},[3499,10362,3858],{"class":3536},[3499,10364,10365],{"class":3554}," TaskForm",[3499,10367,3544],{"class":3509},[3499,10369,6870],{"class":3554},[3499,10371,4393],{"class":3509},[3499,10373,10374,10377,10380,10382,10385],{"class":3501,"line":4598},[3499,10375,10376],{"class":3509},"    task: Union[ProgrammingTask, DesignTask] = Field(..., ",[3499,10378,10379],{"class":3547},"discriminator",[3499,10381,5964],{"class":3509},[3499,10383,10384],{"class":3979},"\"type\"",[3499,10386,4876],{"class":3509},[3499,10388,10389],{"class":3501,"line":4604},[3499,10390,3523],{"emptyLinePlaceholder":3522},[3499,10392,10393],{"class":3501,"line":5778},[3499,10394,10395],{"class":3529},"# Демонстрація поліморфного парсингу:\n",[3499,10397,10398,10400],{"class":3501,"line":5786},[3499,10399,7781],{"class":3505},[3499,10401,3629],{"class":3509},[3499,10403,10404],{"class":3501,"line":5791},[3499,10405,10406],{"class":3529},"    # 1. Передаємо дані для кодингу\n",[3499,10408,10409,10412,10415,10418,10420,10422,10424,10426,10429,10431,10434,10436,10439,10441,10444],{"class":3501,"line":5797},[3499,10410,10411],{"class":3509},"    form1 = TaskForm(",[3499,10413,10414],{"class":3547},"task",[3499,10416,10417],{"class":3509},"={",[3499,10419,10384],{"class":3979},[3499,10421,3609],{"class":3509},[3499,10423,10282],{"class":3979},[3499,10425,3468],{"class":3509},[3499,10427,10428],{"class":3979},"\"language\"",[3499,10430,3609],{"class":3509},[3499,10432,10433],{"class":3979},"\"Python\"",[3499,10435,3468],{"class":3509},[3499,10437,10438],{"class":3979},"\"repository\"",[3499,10440,3609],{"class":3509},[3499,10442,10443],{"class":3979},"\"github.com\"",[3499,10445,10446],{"class":3509},"})\n",[3499,10448,10449,10451,10453,10456],{"class":3501,"line":5814},[3499,10450,6236],{"class":3540},[3499,10452,3544],{"class":3509},[3499,10454,10455],{"class":3979},"\"Успішно спарсено задачу програмування:\"",[3499,10457,4876],{"class":3509},[3499,10459,10460,10462,10464,10466,10469,10471,10473,10476,10478,10480],{"class":3501,"line":7786},[3499,10461,6236],{"class":3540},[3499,10463,3544],{"class":3509},[3499,10465,5692],{"class":3536},[3499,10467,10468],{"class":3979},"\"  Тип: ",[3499,10470,4085],{"class":3536},[3499,10472,3654],{"class":3554},[3499,10474,10475],{"class":3509},"(form1.task)",[3499,10477,4094],{"class":3536},[3499,10479,7847],{"class":3979},[3499,10481,4876],{"class":3509},[3499,10483,10484,10486,10488,10490,10493,10495,10498,10500,10502,10504],{"class":3501,"line":7792},[3499,10485,6236],{"class":3540},[3499,10487,3544],{"class":3509},[3499,10489,5692],{"class":3536},[3499,10491,10492],{"class":3979},"\"  Мова: ",[3499,10494,4085],{"class":3536},[3499,10496,10497],{"class":3509},"form1.task.language",[3499,10499,4094],{"class":3536},[3499,10501,7918],{"class":7917},[3499,10503,7847],{"class":3979},[3499,10505,4876],{"class":3509},[3499,10507,10508],{"class":3501,"line":7816},[3499,10509,3523],{"emptyLinePlaceholder":3522},[3499,10511,10512],{"class":3501,"line":7828},[3499,10513,10514],{"class":3529},"    # 2. Передаємо дані для дизайну\n",[3499,10516,10517,10520,10522,10524,10526,10528,10530,10532,10535,10537,10540,10542,10545,10547,10549],{"class":3501,"line":7852},[3499,10518,10519],{"class":3509},"    form2 = TaskForm(",[3499,10521,10414],{"class":3547},[3499,10523,10417],{"class":3509},[3499,10525,10384],{"class":3979},[3499,10527,3609],{"class":3509},[3499,10529,10329],{"class":3979},[3499,10531,3468],{"class":3509},[3499,10533,10534],{"class":3979},"\"tool\"",[3499,10536,3609],{"class":3509},[3499,10538,10539],{"class":3979},"\"Figma\"",[3499,10541,3468],{"class":3509},[3499,10543,10544],{"class":3979},"\"pages_count\"",[3499,10546,3609],{"class":3509},[3499,10548,4915],{"class":3771},[3499,10550,10446],{"class":3509},[3499,10552,10553,10555,10557,10560],{"class":3501,"line":7875},[3499,10554,6236],{"class":3540},[3499,10556,3544],{"class":3509},[3499,10558,10559],{"class":3979},"\"Успішно спарсено задачу дизайну:\"",[3499,10561,4876],{"class":3509},[3499,10563,10564,10566,10568,10570,10572,10574,10576,10579,10581,10583],{"class":3501,"line":7898},[3499,10565,6236],{"class":3540},[3499,10567,3544],{"class":3509},[3499,10569,5692],{"class":3536},[3499,10571,10468],{"class":3979},[3499,10573,4085],{"class":3536},[3499,10575,3654],{"class":3554},[3499,10577,10578],{"class":3509},"(form2.task)",[3499,10580,4094],{"class":3536},[3499,10582,7847],{"class":3979},[3499,10584,4876],{"class":3509},[3499,10586,10587,10589,10591,10593,10596,10598,10601,10603,10605,10607],{"class":3501,"line":7925},[3499,10588,6236],{"class":3540},[3499,10590,3544],{"class":3509},[3499,10592,5692],{"class":3536},[3499,10594,10595],{"class":3979},"\"  Кількість сторінок: ",[3499,10597,4085],{"class":3536},[3499,10599,10600],{"class":3509},"form2.task.pages_count",[3499,10602,4094],{"class":3536},[3499,10604,7918],{"class":7917},[3499,10606,7847],{"class":3979},[3499,10608,4876],{"class":3509},[3499,10610,10611],{"class":3501,"line":7930},[3499,10612,3523],{"emptyLinePlaceholder":3522},[3499,10614,10615],{"class":3501,"line":7936},[3499,10616,10617],{"class":3529},"    # 3. Передаємо некоректний тип\n",[3499,10619,10620,10623,10625,10627,10629,10631,10634],{"class":3501,"line":7948},[3499,10621,10622],{"class":3509},"    form3 = TaskForm(",[3499,10624,10414],{"class":3547},[3499,10626,10417],{"class":3509},[3499,10628,10384],{"class":3979},[3499,10630,3609],{"class":3509},[3499,10632,10633],{"class":3979},"\"unknown_type\"",[3499,10635,10446],{"class":3509},[3499,10637,10638,10640,10642,10644],{"class":3501,"line":7974},[3499,10639,8006],{"class":3505},[3499,10641,8009],{"class":3509},[3499,10643,8012],{"class":3505},[3499,10645,8015],{"class":3509},[3499,10647,10648,10650,10652,10654,10657,10659,10662,10664,10666,10668,10670,10672,10674,10676],{"class":3501,"line":7979},[3499,10649,6236],{"class":3540},[3499,10651,3544],{"class":3509},[3499,10653,5692],{"class":3536},[3499,10655,10656],{"class":3979},"\"Помилка валідації поліморфної форми:",[3499,10658,7918],{"class":7917},[3499,10660,10661],{"class":3536},"  {",[3499,10663,8032],{"class":3509},[3499,10665,6242],{"class":3771},[3499,10667,5881],{"class":3509},[3499,10669,8039],{"class":3979},[3499,10671,8042],{"class":3509},[3499,10673,4094],{"class":3536},[3499,10675,7847],{"class":3979},[3499,10677,4876],{"class":3509},[3486,10679,10680,10715,10731],{},[3489,10681,10683],{"className":6765,"code":10682,"filename":6959,"language":6768,"meta":3495,"style":3495},"python -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install pydantic\npython main.py\n",[3403,10684,10685,10695,10701,10709],{"__ignoreMap":3495},[3499,10686,10687,10689,10691,10693],{"class":3501,"line":3502},[3499,10688,3494],{"class":3540},[3499,10690,6973],{"class":3536},[3499,10692,6976],{"class":3979},[3499,10694,6979],{"class":3979},[3499,10696,10697,10699],{"class":3501,"line":3519},[3499,10698,6984],{"class":3540},[3499,10700,7084],{"class":3979},[3499,10702,10703,10705,10707],{"class":3501,"line":3526},[3499,10704,6959],{"class":3540},[3499,10706,7011],{"class":3979},[3499,10708,7014],{"class":3979},[3499,10710,10711,10713],{"class":3501,"line":3533},[3499,10712,3494],{"class":3540},[3499,10714,7030],{"class":3979},[3489,10716,10717],{"className":6765,"code":9964,"filename":7034,"language":6768,"meta":3495,"style":3495},[3403,10718,10719],{"__ignoreMap":3495},[3499,10720,10721,10723,10725,10727,10729],{"class":3501,"line":3502},[3499,10722,7034],{"class":3540},[3499,10724,7053],{"class":3979},[3499,10726,7056],{"class":3536},[3499,10728,7059],{"class":3979},[3499,10730,7030],{"class":3979},[3489,10732,10733],{"className":6765,"code":8212,"filename":7105,"language":6768,"meta":3495,"style":3495},[3403,10734,10735,10743,10751],{"__ignoreMap":3495},[3499,10736,10737,10739,10741],{"class":3501,"line":3502},[3499,10738,7105],{"class":3540},[3499,10740,7119],{"class":3979},[3499,10742,7122],{"class":3536},[3499,10744,10745,10747,10749],{"class":3501,"line":3519},[3499,10746,7105],{"class":3540},[3499,10748,5378],{"class":3979},[3499,10750,7014],{"class":3979},[3499,10752,10753,10755,10757,10759],{"class":3501,"line":3526},[3499,10754,7105],{"class":3540},[3499,10756,7053],{"class":3979},[3499,10758,7157],{"class":3979},[3499,10760,7030],{"class":3979},[3452,10762],{},[3474,10764,10766,10767],{"id":10765},"валідація-без-створення-моделей-typeadapter","Валідація без створення моделей: ",[3403,10768,10769],{},"TypeAdapter",[3394,10771,10772,10773,10775],{},"У Pydantic v1 для валідації простих типів або списків безпосередньо (наприклад, перевірити, чи є вхідні дані просто списком цілих чисел ",[3403,10774,4657],{},") доводилося створювати тимчасові «фейкові» моделі.",[3394,10777,10778,10779,10783,10784,3418],{},"У Pydantic v2 з'явився інструмент ",[3415,10780,10781],{},[3403,10782,10769],{},", який дозволяє валідувати та серіалізувати будь-які типи, сумісні з PEP 484, без створення класу ",[3403,10785,6870],{},[3489,10787,10789],{"className":3491,"code":10788,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import TypeAdapter, ValidationError\n\n# Створюємо адаптер для списку цілих чисел\ninteger_list_adapter = TypeAdapter(list[int])\n\ntry:\n    # 1. Успішна валідація та парсинг\n    result = integer_list_adapter.validate_python([\"1\", 2, \"3\"])\n    print(f\"Спарсено список чисел: {result}\") # Виведе: [1, 2, 3]\n\n    # 2. Некоректні дані\n    integer_list_adapter.validate_python([\"one\", 2])\nexcept ValidationError as e:\n    print(f\"Помилка валідації списку: {e.errors()[0]['msg']}\")\n",[3403,10790,10791,10802,10806,10811,10820,10824,10830,10835,10854,10879,10883,10888,10902,10912],{"__ignoreMap":3495},[3499,10792,10793,10795,10797,10799],{"class":3501,"line":3502},[3499,10794,3506],{"class":3505},[3499,10796,5934],{"class":3509},[3499,10798,3513],{"class":3505},[3499,10800,10801],{"class":3509}," TypeAdapter, ValidationError\n",[3499,10803,10804],{"class":3501,"line":3519},[3499,10805,3523],{"emptyLinePlaceholder":3522},[3499,10807,10808],{"class":3501,"line":3526},[3499,10809,10810],{"class":3529},"# Створюємо адаптер для списку цілих чисел\n",[3499,10812,10813,10816,10818],{"class":3501,"line":3533},[3499,10814,10815],{"class":3509},"integer_list_adapter = TypeAdapter(list[",[3499,10817,3555],{"class":3554},[3499,10819,7670],{"class":3509},[3499,10821,10822],{"class":3501,"line":3571},[3499,10823,3523],{"emptyLinePlaceholder":3522},[3499,10825,10826,10828],{"class":3501,"line":3577},[3499,10827,7781],{"class":3505},[3499,10829,3629],{"class":3509},[3499,10831,10832],{"class":3501,"line":3705},[3499,10833,10834],{"class":3529},"    # 1. Успішна валідація та парсинг\n",[3499,10836,10837,10840,10843,10845,10847,10849,10852],{"class":3501,"line":3800},[3499,10838,10839],{"class":3509},"    result = integer_list_adapter.validate_python([",[3499,10841,10842],{"class":3979},"\"1\"",[3499,10844,3468],{"class":3509},[3499,10846,4091],{"class":3771},[3499,10848,3468],{"class":3509},[3499,10850,10851],{"class":3979},"\"3\"",[3499,10853,7670],{"class":3509},[3499,10855,10856,10858,10860,10862,10865,10867,10870,10872,10874,10876],{"class":3501,"line":3812},[3499,10857,6236],{"class":3540},[3499,10859,3544],{"class":3509},[3499,10861,5692],{"class":3536},[3499,10863,10864],{"class":3979},"\"Спарсено список чисел: ",[3499,10866,4085],{"class":3536},[3499,10868,10869],{"class":3509},"result",[3499,10871,4094],{"class":3536},[3499,10873,7847],{"class":3979},[3499,10875,6175],{"class":3509},[3499,10877,10878],{"class":3529},"# Виведе: [1, 2, 3]\n",[3499,10880,10881],{"class":3501,"line":3826},[3499,10882,3523],{"emptyLinePlaceholder":3522},[3499,10884,10885],{"class":3501,"line":3985},[3499,10886,10887],{"class":3529},"    # 2. Некоректні дані\n",[3499,10889,10890,10893,10896,10898,10900],{"class":3501,"line":3991},[3499,10891,10892],{"class":3509},"    integer_list_adapter.validate_python([",[3499,10894,10895],{"class":3979},"\"one\"",[3499,10897,3468],{"class":3509},[3499,10899,4091],{"class":3771},[3499,10901,7670],{"class":3509},[3499,10903,10904,10906,10908,10910],{"class":3501,"line":3997},[3499,10905,8006],{"class":3505},[3499,10907,8009],{"class":3509},[3499,10909,8012],{"class":3505},[3499,10911,8015],{"class":3509},[3499,10913,10914,10916,10918,10920,10923,10925,10927,10929,10931,10933,10935,10937,10939],{"class":3501,"line":4006},[3499,10915,6236],{"class":3540},[3499,10917,3544],{"class":3509},[3499,10919,5692],{"class":3536},[3499,10921,10922],{"class":3979},"\"Помилка валідації списку: ",[3499,10924,4085],{"class":3536},[3499,10926,8032],{"class":3509},[3499,10928,6242],{"class":3771},[3499,10930,5881],{"class":3509},[3499,10932,8039],{"class":3979},[3499,10934,8042],{"class":3509},[3499,10936,4094],{"class":3536},[3499,10938,7847],{"class":3979},[3499,10940,4876],{"class":3509},[3486,10942,10943,10977,10993],{},[3489,10944,10945],{"className":6765,"code":10682,"filename":6959,"language":6768,"meta":3495,"style":3495},[3403,10946,10947,10957,10963,10971],{"__ignoreMap":3495},[3499,10948,10949,10951,10953,10955],{"class":3501,"line":3502},[3499,10950,3494],{"class":3540},[3499,10952,6973],{"class":3536},[3499,10954,6976],{"class":3979},[3499,10956,6979],{"class":3979},[3499,10958,10959,10961],{"class":3501,"line":3519},[3499,10960,6984],{"class":3540},[3499,10962,7084],{"class":3979},[3499,10964,10965,10967,10969],{"class":3501,"line":3526},[3499,10966,6959],{"class":3540},[3499,10968,7011],{"class":3979},[3499,10970,7014],{"class":3979},[3499,10972,10973,10975],{"class":3501,"line":3533},[3499,10974,3494],{"class":3540},[3499,10976,7030],{"class":3979},[3489,10978,10979],{"className":6765,"code":9964,"filename":7034,"language":6768,"meta":3495,"style":3495},[3403,10980,10981],{"__ignoreMap":3495},[3499,10982,10983,10985,10987,10989,10991],{"class":3501,"line":3502},[3499,10984,7034],{"class":3540},[3499,10986,7053],{"class":3979},[3499,10988,7056],{"class":3536},[3499,10990,7059],{"class":3979},[3499,10992,7030],{"class":3979},[3489,10994,10995],{"className":6765,"code":8212,"filename":7105,"language":6768,"meta":3495,"style":3495},[3403,10996,10997,11005,11013],{"__ignoreMap":3495},[3499,10998,10999,11001,11003],{"class":3501,"line":3502},[3499,11000,7105],{"class":3540},[3499,11002,7119],{"class":3979},[3499,11004,7122],{"class":3536},[3499,11006,11007,11009,11011],{"class":3501,"line":3519},[3499,11008,7105],{"class":3540},[3499,11010,5378],{"class":3979},[3499,11012,7014],{"class":3979},[3499,11014,11015,11017,11019,11021],{"class":3501,"line":3526},[3499,11016,7105],{"class":3540},[3499,11018,7053],{"class":3979},[3499,11020,7157],{"class":3979},[3499,11022,7030],{"class":3979},[3452,11024],{},[3474,11026,11028],{"id":11027},"серіалізація-даних-експорт-моделей","Серіалізація даних: Експорт моделей",[3394,11030,11031,11032,11035],{},"Окрім парсингу та валідації вхідних даних, Pydantic відповідає за зворотний процес — ",[3415,11033,11034],{},"серіалізацію (експорт)"," об'єктів у словники чи JSON-рядки. Це життєво необхідно для формування HTTP-відповідей.",[3394,11037,11038,11039,6649],{},"У Pydantic v2 всі експортні методи отримали префікс ",[3403,11040,11041],{},"model_",[4697,11043,11044,11054,11065],{},[3430,11045,11046,11050,11051,4744],{},[3415,11047,11048],{},[3403,11049,7490],{}," — серіалізує модель у звичайний Python-словник (",[3403,11052,11053],{},"dict",[3430,11055,11056,11060,11061,11064],{},[3415,11057,11058],{},[3403,11059,7493],{}," — серіалізує модель безпосередньо в JSON-рядок (працює значно швидше, ніж ",[3403,11062,11063],{},"json.dumps(model.model_dump())",", оскільки конвертація відбувається на рівні Rust).",[3430,11066,11067,11072],{},[3415,11068,11069],{},[3403,11070,11071],{},"model_json_schema()"," — генерує словник, що представляє JSON Schema моделі (основа автодокументації FastAPI).",[3741,11074,11076],{"id":11075},"тонке-керування-серіалізацією","Тонке керування серіалізацією",[3394,11078,11079,11080,4140,11083,11086],{},"Методи ",[3403,11081,11082],{},"model_dump",[3403,11084,11085],{},"model_dump_json"," приймають важливі параметри для фільтрації полів:",[4697,11088,11089,11095,11103],{},[3430,11090,11091,11094],{},[3403,11092,11093],{},"exclude_unset",": експортує лише ті поля, які були явно передані при створенні об'єкта (ігнорує дефолтні значення).",[3430,11096,11097,11100,11101,3418],{},[3403,11098,11099],{},"exclude_none",": виключає зі словника поля, значення яких дорівнюють ",[3403,11102,3483],{},[3430,11104,11105,8368,11108,11110],{},[3403,11106,11107],{},"include",[3403,11109,7483],{},": дозволяє явно вказати набір полів для експорту (наприклад, виключити пароль користувача).",[3489,11112,11114],{"className":3491,"code":11113,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import BaseModel, Field\n\nclass UserResponse(BaseModel):\n    id: int\n    email: str\n    username: str | None = None\n    role: str = \"user\"\n\n# Створюємо об'єкт (передаємо тільки id та email)\nuser = UserResponse(id=1, email=\"admin@test.com\")\n\n# Експорт у словник з ігноруванням None та дефолтних полів\nprint(user.model_dump(exclude_unset=True))\n# Виведе: {'id': 1, 'email': 'admin@test.com'}\n\n# Експорт з виключенням певних полів\nprint(user.model_dump(exclude={\"email\"}))\n# Виведе: {'id': 1, 'username': None, 'role': 'user'}\n",[3403,11115,11116,11127,11131,11144,11152,11159,11175,11187,11191,11196,11219,11223,11228,11243,11248,11252,11257,11273],{"__ignoreMap":3495},[3499,11117,11118,11120,11122,11124],{"class":3501,"line":3502},[3499,11119,3506],{"class":3505},[3499,11121,5934],{"class":3509},[3499,11123,3513],{"class":3505},[3499,11125,11126],{"class":3509}," BaseModel, Field\n",[3499,11128,11129],{"class":3501,"line":3519},[3499,11130,3523],{"emptyLinePlaceholder":3522},[3499,11132,11133,11135,11138,11140,11142],{"class":3501,"line":3526},[3499,11134,3858],{"class":3536},[3499,11136,11137],{"class":3554}," UserResponse",[3499,11139,3544],{"class":3509},[3499,11141,6870],{"class":3554},[3499,11143,4393],{"class":3509},[3499,11145,11146,11148,11150],{"class":3501,"line":3533},[3499,11147,6877],{"class":3540},[3499,11149,3609],{"class":3509},[3499,11151,6882],{"class":3554},[3499,11153,11154,11157],{"class":3501,"line":3571},[3499,11155,11156],{"class":3509},"    email: ",[3499,11158,3664],{"class":3554},[3499,11160,11161,11164,11166,11168,11170,11172],{"class":3501,"line":3577},[3499,11162,11163],{"class":3509},"    username: ",[3499,11165,3560],{"class":3554},[3499,11167,3614],{"class":3509},[3499,11169,3483],{"class":3536},[3499,11171,3768],{"class":3509},[3499,11173,11174],{"class":3536},"None\n",[3499,11176,11177,11180,11182,11184],{"class":3501,"line":3705},[3499,11178,11179],{"class":3509},"    role: ",[3499,11181,3560],{"class":3554},[3499,11183,3768],{"class":3509},[3499,11185,11186],{"class":3979},"\"user\"\n",[3499,11188,11189],{"class":3501,"line":3800},[3499,11190,3523],{"emptyLinePlaceholder":3522},[3499,11192,11193],{"class":3501,"line":3812},[3499,11194,11195],{"class":3529},"# Створюємо об'єкт (передаємо тільки id та email)\n",[3499,11197,11198,11201,11203,11205,11207,11209,11212,11214,11217],{"class":3501,"line":3826},[3499,11199,11200],{"class":3509},"user = UserResponse(",[3499,11202,6907],{"class":3547},[3499,11204,5964],{"class":3509},[3499,11206,4783],{"class":3771},[3499,11208,3468],{"class":3509},[3499,11210,11211],{"class":3547},"email",[3499,11213,5964],{"class":3509},[3499,11215,11216],{"class":3979},"\"admin@test.com\"",[3499,11218,4876],{"class":3509},[3499,11220,11221],{"class":3501,"line":3985},[3499,11222,3523],{"emptyLinePlaceholder":3522},[3499,11224,11225],{"class":3501,"line":3991},[3499,11226,11227],{"class":3529},"# Експорт у словник з ігноруванням None та дефолтних полів\n",[3499,11229,11230,11232,11235,11237,11239,11241],{"class":3501,"line":3997},[3499,11231,4111],{"class":3540},[3499,11233,11234],{"class":3509},"(user.model_dump(",[3499,11236,11093],{"class":3547},[3499,11238,5964],{"class":3509},[3499,11240,6086],{"class":3536},[3499,11242,4024],{"class":3509},[3499,11244,11245],{"class":3501,"line":4006},[3499,11246,11247],{"class":3529},"# Виведе: {'id': 1, 'email': 'admin@test.com'}\n",[3499,11249,11250],{"class":3501,"line":4593},[3499,11251,3523],{"emptyLinePlaceholder":3522},[3499,11253,11254],{"class":3501,"line":4598},[3499,11255,11256],{"class":3529},"# Експорт з виключенням певних полів\n",[3499,11258,11259,11261,11263,11265,11267,11270],{"class":3501,"line":4604},[3499,11260,4111],{"class":3540},[3499,11262,11234],{"class":3509},[3499,11264,7483],{"class":3547},[3499,11266,10417],{"class":3509},[3499,11268,11269],{"class":3979},"\"email\"",[3499,11271,11272],{"class":3509},"}))\n",[3499,11274,11275],{"class":3501,"line":5778},[3499,11276,11277],{"class":3529},"# Виведе: {'id': 1, 'username': None, 'role': 'user'}\n",[3486,11279,11280,11320,11336],{},[3489,11281,11283],{"className":6765,"code":11282,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Запуск прикладу серіалізації\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install pydantic\npython main.py\n",[3403,11284,11285,11290,11300,11306,11314],{"__ignoreMap":3495},[3499,11286,11287],{"class":3501,"line":3502},[3499,11288,11289],{"class":3529},"# Запуск прикладу серіалізації\n",[3499,11291,11292,11294,11296,11298],{"class":3501,"line":3519},[3499,11293,3494],{"class":3540},[3499,11295,6973],{"class":3536},[3499,11297,6976],{"class":3979},[3499,11299,6979],{"class":3979},[3499,11301,11302,11304],{"class":3501,"line":3526},[3499,11303,6984],{"class":3540},[3499,11305,7084],{"class":3979},[3499,11307,11308,11310,11312],{"class":3501,"line":3533},[3499,11309,6959],{"class":3540},[3499,11311,7011],{"class":3979},[3499,11313,7014],{"class":3979},[3499,11315,11316,11318],{"class":3501,"line":3571},[3499,11317,3494],{"class":3540},[3499,11319,7030],{"class":3979},[3489,11321,11322],{"className":6765,"code":9964,"filename":7034,"language":6768,"meta":3495,"style":3495},[3403,11323,11324],{"__ignoreMap":3495},[3499,11325,11326,11328,11330,11332,11334],{"class":3501,"line":3502},[3499,11327,7034],{"class":3540},[3499,11329,7053],{"class":3979},[3499,11331,7056],{"class":3536},[3499,11333,7059],{"class":3979},[3499,11335,7030],{"class":3979},[3489,11337,11338],{"className":6765,"code":8212,"filename":7105,"language":6768,"meta":3495,"style":3495},[3403,11339,11340,11348,11356],{"__ignoreMap":3495},[3499,11341,11342,11344,11346],{"class":3501,"line":3502},[3499,11343,7105],{"class":3540},[3499,11345,7119],{"class":3979},[3499,11347,7122],{"class":3536},[3499,11349,11350,11352,11354],{"class":3501,"line":3519},[3499,11351,7105],{"class":3540},[3499,11353,5378],{"class":3979},[3499,11355,7014],{"class":3979},[3499,11357,11358,11360,11362,11364],{"class":3501,"line":3526},[3499,11359,7105],{"class":3540},[3499,11361,7053],{"class":3979},[3499,11363,7157],{"class":3979},[3499,11365,7030],{"class":3979},[3452,11367],{},[3474,11369,11371,11372],{"id":11370},"налаштування-моделей-configdict","Налаштування моделей: ",[3403,11373,11374],{},"ConfigDict",[3394,11376,11377,11378,11382,11383,4744],{},"Поведінку кожної моделі Pydantic можна гнучко налаштовувати. У Pydantic v2 для цього використовується спеціальний словник конфігурації ",[3415,11379,11380],{},[3403,11381,11374],{}," (у v1 використовувався вкладений клас ",[3403,11384,11385],{},"class Config",[3394,11387,11388],{},"Приклад використання основних параметрів конфігурації:",[3489,11390,11392],{"className":3491,"code":11391,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import BaseModel, ConfigDict, Field\n\nclass UserModel(BaseModel):\n    # Налаштування конфігурації моделі\n    model_config = ConfigDict(\n        strict=True,                  # Вмикає строгий режим (забороняє приведення типів \"123\" -> 123)\n        populate_by_name=True,        # Дозволяє створювати об'єкт як за аліасами, так і за іменами полів\n        extra=\"forbid\",               # Забороняє передачу додаткових невідомих полів (захист від Over-posting)\n        str_strip_whitespace=True     # Автоматично видаляє пробіли по краях у всіх рядках\n    )\n\n    id: int\n    # Аліас дозволяє мапити зовнішні назви полів (наприклад, з JSON в стилі snake_case\u002FcamelCase)\n    first_name: str = Field(alias=\"firstName\")\n\n# Створення об'єкта за допомогою аліасу (наприклад, при отриманні JSON від фронтенду)\nuser_alias = UserModel(id=1, firstName=\"  Іван  \")\nprint(user_alias.first_name)  # Виведе: \"Іван\" (пробіли видалено завдяки str_strip_whitespace)\n\n# Завдяки populate_by_name=True, працює також створення через реальне ім'я атрибута:\nuser_name = UserModel(id=2, first_name=\"Петро\")\n",[3403,11393,11394,11405,11409,11422,11427,11432,11447,11462,11478,11490,11494,11498,11506,11511,11529,11533,11538,11561,11571,11575,11580],{"__ignoreMap":3495},[3499,11395,11396,11398,11400,11402],{"class":3501,"line":3502},[3499,11397,3506],{"class":3505},[3499,11399,5934],{"class":3509},[3499,11401,3513],{"class":3505},[3499,11403,11404],{"class":3509}," BaseModel, ConfigDict, Field\n",[3499,11406,11407],{"class":3501,"line":3519},[3499,11408,3523],{"emptyLinePlaceholder":3522},[3499,11410,11411,11413,11416,11418,11420],{"class":3501,"line":3526},[3499,11412,3858],{"class":3536},[3499,11414,11415],{"class":3554}," UserModel",[3499,11417,3544],{"class":3509},[3499,11419,6870],{"class":3554},[3499,11421,4393],{"class":3509},[3499,11423,11424],{"class":3501,"line":3533},[3499,11425,11426],{"class":3529},"    # Налаштування конфігурації моделі\n",[3499,11428,11429],{"class":3501,"line":3571},[3499,11430,11431],{"class":3509},"    model_config = ConfigDict(\n",[3499,11433,11434,11437,11439,11441,11444],{"class":3501,"line":3577},[3499,11435,11436],{"class":3547},"        strict",[3499,11438,5964],{"class":3509},[3499,11440,6086],{"class":3536},[3499,11442,11443],{"class":3509},",                  ",[3499,11445,11446],{"class":3529},"# Вмикає строгий режим (забороняє приведення типів \"123\" -> 123)\n",[3499,11448,11449,11452,11454,11456,11459],{"class":3501,"line":3705},[3499,11450,11451],{"class":3547},"        populate_by_name",[3499,11453,5964],{"class":3509},[3499,11455,6086],{"class":3536},[3499,11457,11458],{"class":3509},",        ",[3499,11460,11461],{"class":3529},"# Дозволяє створювати об'єкт як за аліасами, так і за іменами полів\n",[3499,11463,11464,11467,11469,11472,11475],{"class":3501,"line":3800},[3499,11465,11466],{"class":3547},"        extra",[3499,11468,5964],{"class":3509},[3499,11470,11471],{"class":3979},"\"forbid\"",[3499,11473,11474],{"class":3509},",               ",[3499,11476,11477],{"class":3529},"# Забороняє передачу додаткових невідомих полів (захист від Over-posting)\n",[3499,11479,11480,11483,11485,11487],{"class":3501,"line":3812},[3499,11481,11482],{"class":3547},"        str_strip_whitespace",[3499,11484,5964],{"class":3509},[3499,11486,6086],{"class":3536},[3499,11488,11489],{"class":3529},"     # Автоматично видаляє пробіли по краях у всіх рядках\n",[3499,11491,11492],{"class":3501,"line":3826},[3499,11493,9094],{"class":3509},[3499,11495,11496],{"class":3501,"line":3985},[3499,11497,3523],{"emptyLinePlaceholder":3522},[3499,11499,11500,11502,11504],{"class":3501,"line":3991},[3499,11501,6877],{"class":3540},[3499,11503,3609],{"class":3509},[3499,11505,6882],{"class":3554},[3499,11507,11508],{"class":3501,"line":3997},[3499,11509,11510],{"class":3529},"    # Аліас дозволяє мапити зовнішні назви полів (наприклад, з JSON в стилі snake_case\u002FcamelCase)\n",[3499,11512,11513,11516,11518,11520,11522,11524,11527],{"class":3501,"line":4006},[3499,11514,11515],{"class":3509},"    first_name: ",[3499,11517,3560],{"class":3554},[3499,11519,7641],{"class":3509},[3499,11521,7451],{"class":3547},[3499,11523,5964],{"class":3509},[3499,11525,11526],{"class":3979},"\"firstName\"",[3499,11528,4876],{"class":3509},[3499,11530,11531],{"class":3501,"line":4593},[3499,11532,3523],{"emptyLinePlaceholder":3522},[3499,11534,11535],{"class":3501,"line":4598},[3499,11536,11537],{"class":3529},"# Створення об'єкта за допомогою аліасу (наприклад, при отриманні JSON від фронтенду)\n",[3499,11539,11540,11543,11545,11547,11549,11551,11554,11556,11559],{"class":3501,"line":4604},[3499,11541,11542],{"class":3509},"user_alias = UserModel(",[3499,11544,6907],{"class":3547},[3499,11546,5964],{"class":3509},[3499,11548,4783],{"class":3771},[3499,11550,3468],{"class":3509},[3499,11552,11553],{"class":3547},"firstName",[3499,11555,5964],{"class":3509},[3499,11557,11558],{"class":3979},"\"  Іван  \"",[3499,11560,4876],{"class":3509},[3499,11562,11563,11565,11568],{"class":3501,"line":5778},[3499,11564,4111],{"class":3540},[3499,11566,11567],{"class":3509},"(user_alias.first_name)  ",[3499,11569,11570],{"class":3529},"# Виведе: \"Іван\" (пробіли видалено завдяки str_strip_whitespace)\n",[3499,11572,11573],{"class":3501,"line":5786},[3499,11574,3523],{"emptyLinePlaceholder":3522},[3499,11576,11577],{"class":3501,"line":5791},[3499,11578,11579],{"class":3529},"# Завдяки populate_by_name=True, працює також створення через реальне ім'я атрибута:\n",[3499,11581,11582,11585,11587,11589,11591,11593,11596,11598,11601],{"class":3501,"line":5797},[3499,11583,11584],{"class":3509},"user_name = UserModel(",[3499,11586,6907],{"class":3547},[3499,11588,5964],{"class":3509},[3499,11590,4091],{"class":3771},[3499,11592,3468],{"class":3509},[3499,11594,11595],{"class":3547},"first_name",[3499,11597,5964],{"class":3509},[3499,11599,11600],{"class":3979},"\"Петро\"",[3499,11602,4876],{"class":3509},[3486,11604,11605,11645,11661],{},[3489,11606,11608],{"className":6765,"code":11607,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Запуск прикладу з ConfigDict\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install pydantic\npython main.py\n",[3403,11609,11610,11615,11625,11631,11639],{"__ignoreMap":3495},[3499,11611,11612],{"class":3501,"line":3502},[3499,11613,11614],{"class":3529},"# Запуск прикладу з ConfigDict\n",[3499,11616,11617,11619,11621,11623],{"class":3501,"line":3519},[3499,11618,3494],{"class":3540},[3499,11620,6973],{"class":3536},[3499,11622,6976],{"class":3979},[3499,11624,6979],{"class":3979},[3499,11626,11627,11629],{"class":3501,"line":3526},[3499,11628,6984],{"class":3540},[3499,11630,7084],{"class":3979},[3499,11632,11633,11635,11637],{"class":3501,"line":3533},[3499,11634,6959],{"class":3540},[3499,11636,7011],{"class":3979},[3499,11638,7014],{"class":3979},[3499,11640,11641,11643],{"class":3501,"line":3571},[3499,11642,3494],{"class":3540},[3499,11644,7030],{"class":3979},[3489,11646,11647],{"className":6765,"code":9964,"filename":7034,"language":6768,"meta":3495,"style":3495},[3403,11648,11649],{"__ignoreMap":3495},[3499,11650,11651,11653,11655,11657,11659],{"class":3501,"line":3502},[3499,11652,7034],{"class":3540},[3499,11654,7053],{"class":3979},[3499,11656,7056],{"class":3536},[3499,11658,7059],{"class":3979},[3499,11660,7030],{"class":3979},[3489,11662,11663],{"className":6765,"code":8212,"filename":7105,"language":6768,"meta":3495,"style":3495},[3403,11664,11665,11673,11681],{"__ignoreMap":3495},[3499,11666,11667,11669,11671],{"class":3501,"line":3502},[3499,11668,7105],{"class":3540},[3499,11670,7119],{"class":3979},[3499,11672,7122],{"class":3536},[3499,11674,11675,11677,11679],{"class":3501,"line":3519},[3499,11676,7105],{"class":3540},[3499,11678,5378],{"class":3979},[3499,11680,7014],{"class":3979},[3499,11682,11683,11685,11687,11689],{"class":3501,"line":3526},[3499,11684,7105],{"class":3540},[3499,11686,7053],{"class":3979},[3499,11688,7157],{"class":3979},[3499,11690,7030],{"class":3979},[4612,11692,11693,11700,11701,11704,11705,11708,11709,11711],{},[3415,11694,11695,11696,11699],{},"Чому ",[3403,11697,11698],{},"extra=\"forbid\""," важливий для безпеки:","\nВ веброзробці існує атака типу ",[3415,11702,11703],{},"Over-posting (або Mass Assignment)",", коли зловмисник передає в запиті додаткові поля, які сервер не очікує (наприклад, ",[3403,11706,11707],{},"is_admin=True"," при реєстрації). Якщо ваша модель просто ігнорує зайві поля, це може призвести до вразливостей, якщо ви потім передаєте весь словник у базу даних. Конфігурація ",[3403,11710,11698],{}," відразу поверне клієнту помилку 422 Unprocessable Entity, заблокувавши атаку.",[3452,11713],{},[3474,11715,11717,11718],{"id":11716},"керування-конфігурацією-застосунку-pydantic-settings","Керування конфігурацією застосунку: ",[3403,11719,11720],{},"Pydantic Settings",[3394,11722,11723],{},"У сучасній розробці (відповідно до методології 12-Factor App) конфігурація застосунку має зберігатися у змінних середовища (environment variables).",[3394,11725,11726,11727,11729,11730,4744],{},"В екосистемі Python стандартом де-факто для зчитування конфігурації, її типізації та валідації є бібліотека ",[3415,11728,11720],{}," (у v2 вона винесена в окремий пакет ",[3403,11731,11732],{},"pydantic-settings",[3394,11734,11735],{},"Вона автоматично:",[3427,11737,11738,11744,11750,11762],{},[3430,11739,11740,11741,4744],{},"Зчитує змінні середовища (наприклад, ",[3403,11742,11743],{},"DATABASE_URL",[3430,11745,11746,11747,3418],{},"Якщо змінних немає в системі, намагається зчитати їх із файлу ",[3403,11748,11749],{},".env",[3430,11751,11752,11753,9147,11756,11759,11760,4744],{},"Парсить і конвертує типи (наприклад, ",[3403,11754,11755],{},"\"5432\"",[3403,11757,11758],{},"5432"," для ",[3403,11761,3555],{},[3430,11763,11764],{},"Робить валідацію (наприклад, перевіряє правильність формату URL).",[3394,11766,11767],{},"Приклад реалізації класу налаштувань:",[3489,11769,11771],{"className":3491,"code":11770,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import PostgresDsn, RedisDsn\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\nclass Settings(BaseSettings):\n    # Вказуємо назву файлу .env для зчитування налаштувань локально\n    model_config = SettingsConfigDict(\n        env_file=\".env\",\n        env_file_encoding=\"utf-8\",\n        extra=\"ignore\" # Ігноруємо зайві змінні в системі\n    )\n\n    db_url: PostgresDsn\n    redis_url: RedisDsn | None = None\n    app_port: int = 8000\n    debug_mode: bool = False\n\n# Створюємо глобальний синглтон налаштувань (ініціалізується один раз)\nsettings = Settings()\n\n# Тепер налаштування доступні по всьому застосунку з автодоповненням в IDE:\nprint(f\"Server starting on port: {settings.app_port}\")\nprint(f\"DB Host: {settings.db_url.host}\")\n",[3403,11772,11773,11784,11796,11800,11814,11819,11824,11836,11848,11860,11864,11868,11873,11884,11896,11908,11912,11917,11922,11926,11931,11953],{"__ignoreMap":3495},[3499,11774,11775,11777,11779,11781],{"class":3501,"line":3502},[3499,11776,3506],{"class":3505},[3499,11778,5934],{"class":3509},[3499,11780,3513],{"class":3505},[3499,11782,11783],{"class":3509}," PostgresDsn, RedisDsn\n",[3499,11785,11786,11788,11791,11793],{"class":3501,"line":3519},[3499,11787,3506],{"class":3505},[3499,11789,11790],{"class":3509}," pydantic_settings ",[3499,11792,3513],{"class":3505},[3499,11794,11795],{"class":3509}," BaseSettings, SettingsConfigDict\n",[3499,11797,11798],{"class":3501,"line":3526},[3499,11799,3523],{"emptyLinePlaceholder":3522},[3499,11801,11802,11804,11807,11809,11812],{"class":3501,"line":3533},[3499,11803,3858],{"class":3536},[3499,11805,11806],{"class":3554}," Settings",[3499,11808,3544],{"class":3509},[3499,11810,11811],{"class":3554},"BaseSettings",[3499,11813,4393],{"class":3509},[3499,11815,11816],{"class":3501,"line":3571},[3499,11817,11818],{"class":3529},"    # Вказуємо назву файлу .env для зчитування налаштувань локально\n",[3499,11820,11821],{"class":3501,"line":3577},[3499,11822,11823],{"class":3509},"    model_config = SettingsConfigDict(\n",[3499,11825,11826,11829,11831,11834],{"class":3501,"line":3705},[3499,11827,11828],{"class":3547},"        env_file",[3499,11830,5964],{"class":3509},[3499,11832,11833],{"class":3979},"\".env\"",[3499,11835,8474],{"class":3509},[3499,11837,11838,11841,11843,11846],{"class":3501,"line":3800},[3499,11839,11840],{"class":3547},"        env_file_encoding",[3499,11842,5964],{"class":3509},[3499,11844,11845],{"class":3979},"\"utf-8\"",[3499,11847,8474],{"class":3509},[3499,11849,11850,11852,11854,11857],{"class":3501,"line":3812},[3499,11851,11466],{"class":3547},[3499,11853,5964],{"class":3509},[3499,11855,11856],{"class":3979},"\"ignore\"",[3499,11858,11859],{"class":3529}," # Ігноруємо зайві змінні в системі\n",[3499,11861,11862],{"class":3501,"line":3826},[3499,11863,9094],{"class":3509},[3499,11865,11866],{"class":3501,"line":3985},[3499,11867,3523],{"emptyLinePlaceholder":3522},[3499,11869,11870],{"class":3501,"line":3991},[3499,11871,11872],{"class":3509},"    db_url: PostgresDsn\n",[3499,11874,11875,11878,11880,11882],{"class":3501,"line":3997},[3499,11876,11877],{"class":3509},"    redis_url: RedisDsn | ",[3499,11879,3483],{"class":3536},[3499,11881,3768],{"class":3509},[3499,11883,11174],{"class":3536},[3499,11885,11886,11889,11891,11893],{"class":3501,"line":4006},[3499,11887,11888],{"class":3509},"    app_port: ",[3499,11890,3555],{"class":3554},[3499,11892,3768],{"class":3509},[3499,11894,11895],{"class":3771},"8000\n",[3499,11897,11898,11901,11903,11905],{"class":3501,"line":4593},[3499,11899,11900],{"class":3509},"    debug_mode: ",[3499,11902,3927],{"class":3554},[3499,11904,3768],{"class":3509},[3499,11906,11907],{"class":3536},"False\n",[3499,11909,11910],{"class":3501,"line":4598},[3499,11911,3523],{"emptyLinePlaceholder":3522},[3499,11913,11914],{"class":3501,"line":4604},[3499,11915,11916],{"class":3529},"# Створюємо глобальний синглтон налаштувань (ініціалізується один раз)\n",[3499,11918,11919],{"class":3501,"line":5778},[3499,11920,11921],{"class":3509},"settings = Settings()\n",[3499,11923,11924],{"class":3501,"line":5786},[3499,11925,3523],{"emptyLinePlaceholder":3522},[3499,11927,11928],{"class":3501,"line":5791},[3499,11929,11930],{"class":3529},"# Тепер налаштування доступні по всьому застосунку з автодоповненням в IDE:\n",[3499,11932,11933,11935,11937,11939,11942,11944,11947,11949,11951],{"class":3501,"line":5797},[3499,11934,4111],{"class":3540},[3499,11936,3544],{"class":3509},[3499,11938,5692],{"class":3536},[3499,11940,11941],{"class":3979},"\"Server starting on port: ",[3499,11943,4085],{"class":3536},[3499,11945,11946],{"class":3509},"settings.app_port",[3499,11948,4094],{"class":3536},[3499,11950,7847],{"class":3979},[3499,11952,4876],{"class":3509},[3499,11954,11955,11957,11959,11961,11964,11966,11969,11971,11973],{"class":3501,"line":5814},[3499,11956,4111],{"class":3540},[3499,11958,3544],{"class":3509},[3499,11960,5692],{"class":3536},[3499,11962,11963],{"class":3979},"\"DB Host: ",[3499,11965,4085],{"class":3536},[3499,11967,11968],{"class":3509},"settings.db_url.host",[3499,11970,4094],{"class":3536},[3499,11972,7847],{"class":3979},[3499,11974,4876],{"class":3509},[3486,11976,11977,12029,12056],{},[3489,11978,11980],{"className":6765,"code":11979,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Для роботи з налаштуваннями потрібен пакет pydantic-settings\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\npip install pydantic pydantic-settings\n\n# Запускаємо (переконайтеся, що файл .env існує або змінні задані в системі)\npython main.py\n",[3403,11981,11982,11987,11997,12003,12014,12018,12023],{"__ignoreMap":3495},[3499,11983,11984],{"class":3501,"line":3502},[3499,11985,11986],{"class":3529},"# Для роботи з налаштуваннями потрібен пакет pydantic-settings\n",[3499,11988,11989,11991,11993,11995],{"class":3501,"line":3519},[3499,11990,3494],{"class":3540},[3499,11992,6973],{"class":3536},[3499,11994,6976],{"class":3979},[3499,11996,6979],{"class":3979},[3499,11998,11999,12001],{"class":3501,"line":3526},[3499,12000,6984],{"class":3540},[3499,12002,7084],{"class":3979},[3499,12004,12005,12007,12009,12011],{"class":3501,"line":3533},[3499,12006,6959],{"class":3540},[3499,12008,7011],{"class":3979},[3499,12010,7059],{"class":3979},[3499,12012,12013],{"class":3979}," pydantic-settings\n",[3499,12015,12016],{"class":3501,"line":3571},[3499,12017,3523],{"emptyLinePlaceholder":3522},[3499,12019,12020],{"class":3501,"line":3577},[3499,12021,12022],{"class":3529},"# Запускаємо (переконайтеся, що файл .env існує або змінні задані в системі)\n",[3499,12024,12025,12027],{"class":3501,"line":3705},[3499,12026,3494],{"class":3540},[3499,12028,7030],{"class":3979},[3489,12030,12032],{"className":6765,"code":12031,"filename":7034,"language":6768,"meta":3495,"style":3495},"# uv автоматично встановить обидва пакети та запустить скрипт\nuv run --with pydantic-settings --with pydantic main.py\n",[3403,12033,12034,12039],{"__ignoreMap":3495},[3499,12035,12036],{"class":3501,"line":3502},[3499,12037,12038],{"class":3529},"# uv автоматично встановить обидва пакети та запустить скрипт\n",[3499,12040,12041,12043,12045,12047,12050,12052,12054],{"class":3501,"line":3519},[3499,12042,7034],{"class":3540},[3499,12044,7053],{"class":3979},[3499,12046,7056],{"class":3536},[3499,12048,12049],{"class":3979}," pydantic-settings",[3499,12051,7056],{"class":3536},[3499,12053,7059],{"class":3979},[3499,12055,7030],{"class":3979},[3489,12057,12059],{"className":6765,"code":12058,"filename":7105,"language":6768,"meta":3495,"style":3495},"poetry init -n\npoetry add pydantic pydantic-settings\npoetry run python main.py\n",[3403,12060,12061,12069,12079],{"__ignoreMap":3495},[3499,12062,12063,12065,12067],{"class":3501,"line":3502},[3499,12064,7105],{"class":3540},[3499,12066,7119],{"class":3979},[3499,12068,7122],{"class":3536},[3499,12070,12071,12073,12075,12077],{"class":3501,"line":3519},[3499,12072,7105],{"class":3540},[3499,12074,5378],{"class":3979},[3499,12076,7059],{"class":3979},[3499,12078,12013],{"class":3979},[3499,12080,12081,12083,12085,12087],{"class":3501,"line":3526},[3499,12082,7105],{"class":3540},[3499,12084,7053],{"class":3979},[3499,12086,7157],{"class":3979},[3499,12088,7030],{"class":3979},[3709,12090,12091,12094,12095,12097,12098,3468,12100,3468,12103,3468,12106,12109],{},[3415,12092,12093],{},"Чому PostgresDsn — це-круто:","\nТип ",[3403,12096,8342],{}," — це спеціальний тип Pydantic для валідації URI підключення до PostgreSQL. Він не просто перевіряє рядок регулярним виразом, а розбирає його на окремі частини. Ви можете звертатися до ",[3403,12099,11968],{},[3403,12101,12102],{},"settings.db_url.username",[3403,12104,12105],{},"settings.db_url.password",[3403,12107,12108],{},"settings.db_url.port"," напряму без додаткового парсингу рядка!",[3452,12111],{},[3474,12113,12115],{"id":12114},"продуктивність-pydantic-v2-революція-під-капотом","Продуктивність Pydantic v2: Революція під капотом",[3394,12117,12118,12119,4644],{},"Чому Pydantic став настільки популярним, що його використовують не тільки у FastAPI, але й у великих LLM фреймворках (LangChain, LlamaIndex), сервісах на кшталт OpenAI SDK та інструментах типу ",[3403,12120,12121],{},"ruff",[3394,12123,12124,12125,3418],{},"Головна причина — ",[3415,12126,12127],{},"швидкість",[3394,12129,12130],{},"У версії v1 Pydantic був написаний на чистому Python. Під час інтенсивної роботи з даними (наприклад, серіалізація тисяч записів з бази даних) це створювало великий I\u002FO-bound оверхед.",[3394,12132,12133,12134,12137,12138,4744],{},"У v2 всю внутрішню логіку валідації та парсингу було повністю переписано на ",[3415,12135,12136],{},"Rust"," (бібліотека ",[3403,12139,7410],{},[3394,12141,12142],{},[3415,12143,12144],{},"Бенчмарки продуктивності (операцій на секунду, більше = краще):",[3489,12146,12150],{"className":12147,"code":12149,"language":5211},[12148],"language-text","Валідація JSON:\n┌───────────────────────────┬──────────────────────────────────────────┐\n│ Pydantic v1 (Pure Python) │ █ 1x (базовий рівень)                     │\n├───────────────────────────┼──────────────────────────────────────────┤\n│ Pydantic v2 (Rust Core)   │ ██████████████ 14x - 17x швидше          │\n└───────────────────────────┴──────────────────────────────────────────┘\n",[3403,12151,12149],{"__ignoreMap":3495},[3394,12153,12154],{},"Це робить FastAPI одним із найшвидших вебфреймворків у динамічних мовах програмування, максимально наближаючи його продуктивність до рішень на Go чи Node.js.",[3452,12156],{},[3474,12158,12160],{"id":12159},"порівняння-валідації-aspnet-core-vs-pydantic","Порівняння валідації: ASP.NET Core vs Pydantic",[3394,12162,12163],{},"Для розробника, який переходить із C#, концепція валідації у Pydantic є більш цілісною, оскільки вона об'єднує синтаксичну простоту декларативного оголошення полів та потужність кастомних бізнес-перевірок.",[3394,12165,12166],{},"У .NET-світі для валідації зазвичай використовуються два підходи:",[3427,12168,12169,12185],{},[3430,12170,12171,12174,12175,3468,12178,3468,12181,12184],{},[3415,12172,12173],{},"Data Annotations"," (атрибути типу ",[3403,12176,12177],{},"[Required]",[3403,12179,12180],{},"[EmailAddress]",[3403,12182,12183],{},"[Range(18, 100)]"," над властивостями класу).",[3430,12186,12187,12190,12191,12194],{},[3415,12188,12189],{},"FluentValidation"," (окремий клас валідатора, який наслідується від ",[3403,12192,12193],{},"AbstractValidator\u003CT>"," та описує правила за допомогою Fluent API).",[3394,12196,12197],{},"Порівняємо опис моделі користувача в обох екосистемах:",[3486,12199,12200,12392],{},[3489,12201,12204],{"className":5856,"code":12202,"filename":12203,"language":5859,"meta":3495,"style":3495},"using System.ComponentModel.DataAnnotations;\n\npublic class UserRegisterModel\n{\n    [Required]\n    [EmailAddress]\n    public string Email { get; set; }\n\n    [Range(18, 120, ErrorMessage = \"Вік має бути від 18 років\")]\n    public int Age { get; set; }\n\n    [StringLength(50, MinimumLength = 3)]\n    public string Username { get; set; }\n}\n","ASP.NET Core (Data Annotations)",[3403,12205,12206,12227,12231,12241,12246,12256,12265,12291,12295,12323,12343,12347,12369,12388],{"__ignoreMap":3495},[3499,12207,12208,12211,12214,12216,12219,12221,12224],{"class":3501,"line":3502},[3499,12209,12210],{"class":3505},"using",[3499,12212,12213],{"class":3554}," System",[3499,12215,3418],{"class":3509},[3499,12217,12218],{"class":3554},"ComponentModel",[3499,12220,3418],{"class":3509},[3499,12222,12223],{"class":3554},"DataAnnotations",[3499,12225,12226],{"class":3509},";\n",[3499,12228,12229],{"class":3501,"line":3519},[3499,12230,3523],{"emptyLinePlaceholder":3522},[3499,12232,12233,12235,12238],{"class":3501,"line":3526},[3499,12234,5866],{"class":3536},[3499,12236,12237],{"class":3536}," class",[3499,12239,12240],{"class":3554}," UserRegisterModel\n",[3499,12242,12243],{"class":3501,"line":3533},[3499,12244,12245],{"class":3509},"{\n",[3499,12247,12248,12251,12254],{"class":3501,"line":3571},[3499,12249,12250],{"class":3509},"    [",[3499,12252,12253],{"class":3554},"Required",[3499,12255,3676],{"class":3509},[3499,12257,12258,12260,12263],{"class":3501,"line":3577},[3499,12259,12250],{"class":3509},[3499,12261,12262],{"class":3554},"EmailAddress",[3499,12264,3676],{"class":3509},[3499,12266,12267,12270,12273,12276,12279,12282,12285,12288],{"class":3501,"line":3705},[3499,12268,12269],{"class":3536},"    public",[3499,12271,12272],{"class":3536}," string",[3499,12274,12275],{"class":3547}," Email",[3499,12277,12278],{"class":3509}," { ",[3499,12280,12281],{"class":3536},"get",[3499,12283,12284],{"class":3509},"; ",[3499,12286,12287],{"class":3536},"set",[3499,12289,12290],{"class":3509},"; }\n",[3499,12292,12293],{"class":3501,"line":3800},[3499,12294,3523],{"emptyLinePlaceholder":3522},[3499,12296,12297,12299,12301,12303,12306,12308,12311,12313,12316,12318,12321],{"class":3501,"line":3812},[3499,12298,12250],{"class":3509},[3499,12300,5884],{"class":3554},[3499,12302,3544],{"class":3509},[3499,12304,12305],{"class":3771},"18",[3499,12307,3468],{"class":3509},[3499,12309,12310],{"class":3771},"120",[3499,12312,3468],{"class":3509},[3499,12314,12315],{"class":3547},"ErrorMessage",[3499,12317,3768],{"class":3509},[3499,12319,12320],{"class":3979},"\"Вік має бути від 18 років\"",[3499,12322,5978],{"class":3509},[3499,12324,12325,12327,12330,12333,12335,12337,12339,12341],{"class":3501,"line":3826},[3499,12326,12269],{"class":3536},[3499,12328,12329],{"class":3536}," int",[3499,12331,12332],{"class":3547}," Age",[3499,12334,12278],{"class":3509},[3499,12336,12281],{"class":3536},[3499,12338,12284],{"class":3509},[3499,12340,12287],{"class":3536},[3499,12342,12290],{"class":3509},[3499,12344,12345],{"class":3501,"line":3985},[3499,12346,3523],{"emptyLinePlaceholder":3522},[3499,12348,12349,12351,12354,12356,12358,12360,12363,12365,12367],{"class":3501,"line":3991},[3499,12350,12250],{"class":3509},[3499,12352,12353],{"class":3554},"StringLength",[3499,12355,3544],{"class":3509},[3499,12357,8829],{"class":3771},[3499,12359,3468],{"class":3509},[3499,12361,12362],{"class":3547},"MinimumLength",[3499,12364,3768],{"class":3509},[3499,12366,4792],{"class":3771},[3499,12368,5978],{"class":3509},[3499,12370,12371,12373,12375,12378,12380,12382,12384,12386],{"class":3501,"line":3997},[3499,12372,12269],{"class":3536},[3499,12374,12272],{"class":3536},[3499,12376,12377],{"class":3547}," Username",[3499,12379,12278],{"class":3509},[3499,12381,12281],{"class":3536},[3499,12383,12284],{"class":3509},[3499,12385,12287],{"class":3536},[3499,12387,12290],{"class":3509},[3499,12389,12390],{"class":3501,"line":4006},[3499,12391,4810],{"class":3509},[3489,12393,12396],{"className":3491,"code":12394,"filename":12395,"language":3494,"meta":3495,"style":3495},"from pydantic import BaseModel, Field, EmailStr\n\nclass UserRegisterModel(BaseModel):\n    email: EmailStr\n    age: int = Field(ge=18, le=120, description=\"Вік має бути від 18 років\")\n    username: str = Field(min_length=3, max_length=50)\n","Python (Pydantic v2)",[3403,12397,12398,12409,12413,12426,12431,12464],{"__ignoreMap":3495},[3499,12399,12400,12402,12404,12406],{"class":3501,"line":3502},[3499,12401,3506],{"class":3505},[3499,12403,5934],{"class":3509},[3499,12405,3513],{"class":3505},[3499,12407,12408],{"class":3509}," BaseModel, Field, EmailStr\n",[3499,12410,12411],{"class":3501,"line":3519},[3499,12412,3523],{"emptyLinePlaceholder":3522},[3499,12414,12415,12417,12420,12422,12424],{"class":3501,"line":3526},[3499,12416,3858],{"class":3536},[3499,12418,12419],{"class":3554}," UserRegisterModel",[3499,12421,3544],{"class":3509},[3499,12423,6870],{"class":3554},[3499,12425,4393],{"class":3509},[3499,12427,12428],{"class":3501,"line":3533},[3499,12429,12430],{"class":3509},"    email: EmailStr\n",[3499,12432,12433,12436,12438,12440,12442,12444,12446,12448,12450,12452,12454,12456,12458,12460,12462],{"class":3501,"line":3571},[3499,12434,12435],{"class":3509},"    age: ",[3499,12437,3555],{"class":3554},[3499,12439,7641],{"class":3509},[3499,12441,5961],{"class":3547},[3499,12443,5964],{"class":3509},[3499,12445,12305],{"class":3771},[3499,12447,3468],{"class":3509},[3499,12449,5971],{"class":3547},[3499,12451,5964],{"class":3509},[3499,12453,12310],{"class":3771},[3499,12455,3468],{"class":3509},[3499,12457,7474],{"class":3547},[3499,12459,5964],{"class":3509},[3499,12461,12320],{"class":3979},[3499,12463,4876],{"class":3509},[3499,12465,12466,12468,12470,12472,12474,12476,12478,12480,12482,12484,12486],{"class":3501,"line":3577},[3499,12467,11163],{"class":3509},[3499,12469,3560],{"class":3554},[3499,12471,7641],{"class":3509},[3499,12473,7644],{"class":3547},[3499,12475,5964],{"class":3509},[3499,12477,4792],{"class":3771},[3499,12479,3468],{"class":3509},[3499,12481,7653],{"class":3547},[3499,12483,5964],{"class":3509},[3499,12485,8829],{"class":3771},[3499,12487,4876],{"class":3509},[3741,12489,12491],{"id":12490},"ключові-відмінності","Ключові відмінності",[4697,12493,12494,12517],{},[3430,12495,12496,12499,12500,12503,12504,12507,12508,12511,12512,12514,12515,3418],{},[3415,12497,12498],{},"Зв'язок із типами:"," У C# атрибути Data Annotations не змінюють тип даних. Якщо ви передасте рядок ",[3403,12501,12502],{},"\"18\""," у поле ",[3403,12505,12506],{},"int Age",", MVC-біндер спробує його конвертувати, але у разі невдачі запише помилку у ",[3403,12509,12510],{},"ModelState",". У Pydantic тип є первинним контрактом: якщо тип поля ",[3403,12513,3555],{},", Pydantic під капотом спробує виконати безпечний парсинг і автоматично приведе типи, або викине структурований ",[3403,12516,7353],{},[3430,12518,12519,12522,12523,12525],{},[3415,12520,12521],{},"Локалізація помилок:"," В ASP.NET Core для кастомізації помилок використовуються параметри атрибутів ",[3403,12524,12315],{},". У Pydantic помилки валідації повертають структурований JSON зі списком полів, типом помилки та повідомленням. Локалізація часто робиться на клієнті або через кастомний обробник виключень (Exception Handler) у FastAPI.",[3452,12527],{},[3389,12529,12531],{"id":12530},"практичний-приклад-система-обробки-платіжних-webhook-ів","Практичний приклад: Система обробки платіжних Webhook-ів",[3394,12533,12534],{},"Для закріплення отриманих знань розберемо створення закінченого мікросервісного рішення для обробки вхідних Webhook-ів від платіжної системи (наприклад, Stripe). Наше завдання — зчитати конфігурацію системи, перевірити вхідні HTTP-payloads, автоматично розпізнати тип події (успішний платіж чи повернення коштів) за допомогою Discriminated Unions та виконати бізнес-логіку.",[3394,12536,12537],{},"Проєкт матиме наступну структуру:",[3489,12539,12542],{"className":12540,"code":12541,"language":5211},[12148],"payment_processor\u002F\n├── .env\n├── config.py\n├── schemas.py\n└── main.py\n",[3403,12543,12541],{"__ignoreMap":3495},[3452,12545],{},[3474,12547,12549,12550,4140,12552],{"id":12548},"крок-1-створення-файлу-конфігурації-env-та-configpy","Крок 1: Створення файлу конфігурації ",[3403,12551,11749],{},[3403,12553,12554],{},"config.py",[3394,12556,12557],{},"Почнемо з налаштування параметрів безпеки. Нам потрібні:",[3427,12559,12560,12566,12572],{},[3430,12561,12562,12565],{},[3403,12563,12564],{},"SIGNING_SECRET"," — ключ, за допомогою якого перевіряється справжність запиту.",[3430,12567,12568,12571],{},[3403,12569,12570],{},"API_URL"," — посилання на бекенд платіжної системи.",[3430,12573,12574,12577],{},[3403,12575,12576],{},"MAX_PAYMENT_LIMIT"," — обмеження суми за одну транзакцію.",[3394,12579,12580,12581,6649],{},"Створіть файл ",[3403,12582,11749],{},[3489,12584,12588],{"className":12585,"code":12586,"language":12587,"meta":3495,"style":3495},"language-env shiki shiki-themes light-plus dark-plus dark-plus","SIGNING_SECRET=\"stripe_whsec_supersecret_key_123\"\nAPI_URL=\"https:\u002F\u002Fapi.stripe.com\u002Fv3\"\nMAX_PAYMENT_LIMIT=50000.00\n","env",[3403,12589,12590,12595,12600],{"__ignoreMap":3495},[3499,12591,12592],{"class":3501,"line":3502},[3499,12593,12594],{},"SIGNING_SECRET=\"stripe_whsec_supersecret_key_123\"\n",[3499,12596,12597],{"class":3501,"line":3519},[3499,12598,12599],{},"API_URL=\"https:\u002F\u002Fapi.stripe.com\u002Fv3\"\n",[3499,12601,12602],{"class":3501,"line":3526},[3499,12603,12604],{},"MAX_PAYMENT_LIMIT=50000.00\n",[3394,12606,12580,12607,12609],{},[3403,12608,12554],{},", що зчитує та валідує ці змінні:",[3489,12611,12613],{"className":3491,"code":12612,"filename":3751,"language":3494,"meta":3495,"style":3495},"from pydantic import HttpUrl, SecretStr, Field\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\nclass Settings(BaseSettings):\n    model_config = SettingsConfigDict(env_file=\".env\", env_file_encoding=\"utf-8\")\n\n    # Зберігаємо секретний ключ захищено\n    signing_secret: SecretStr\n    \n    # Валідуємо правильність формату посилання\n    api_url: HttpUrl\n    \n    # Максимальна сума транзакції\n    max_payment_limit: float = Field(default=10000.00, gt=0)\n\nsettings = Settings()\n",[3403,12614,12615,12626,12636,12640,12652,12675,12679,12684,12689,12693,12698,12703,12707,12712,12738,12742],{"__ignoreMap":3495},[3499,12616,12617,12619,12621,12623],{"class":3501,"line":3502},[3499,12618,3506],{"class":3505},[3499,12620,5934],{"class":3509},[3499,12622,3513],{"class":3505},[3499,12624,12625],{"class":3509}," HttpUrl, SecretStr, Field\n",[3499,12627,12628,12630,12632,12634],{"class":3501,"line":3519},[3499,12629,3506],{"class":3505},[3499,12631,11790],{"class":3509},[3499,12633,3513],{"class":3505},[3499,12635,11795],{"class":3509},[3499,12637,12638],{"class":3501,"line":3526},[3499,12639,3523],{"emptyLinePlaceholder":3522},[3499,12641,12642,12644,12646,12648,12650],{"class":3501,"line":3533},[3499,12643,3858],{"class":3536},[3499,12645,11806],{"class":3554},[3499,12647,3544],{"class":3509},[3499,12649,11811],{"class":3554},[3499,12651,4393],{"class":3509},[3499,12653,12654,12657,12660,12662,12664,12666,12669,12671,12673],{"class":3501,"line":3571},[3499,12655,12656],{"class":3509},"    model_config = SettingsConfigDict(",[3499,12658,12659],{"class":3547},"env_file",[3499,12661,5964],{"class":3509},[3499,12663,11833],{"class":3979},[3499,12665,3468],{"class":3509},[3499,12667,12668],{"class":3547},"env_file_encoding",[3499,12670,5964],{"class":3509},[3499,12672,11845],{"class":3979},[3499,12674,4876],{"class":3509},[3499,12676,12677],{"class":3501,"line":3577},[3499,12678,3523],{"emptyLinePlaceholder":3522},[3499,12680,12681],{"class":3501,"line":3705},[3499,12682,12683],{"class":3529},"    # Зберігаємо секретний ключ захищено\n",[3499,12685,12686],{"class":3501,"line":3800},[3499,12687,12688],{"class":3509},"    signing_secret: SecretStr\n",[3499,12690,12691],{"class":3501,"line":3812},[3499,12692,7626],{"class":3509},[3499,12694,12695],{"class":3501,"line":3826},[3499,12696,12697],{"class":3529},"    # Валідуємо правильність формату посилання\n",[3499,12699,12700],{"class":3501,"line":3985},[3499,12701,12702],{"class":3509},"    api_url: HttpUrl\n",[3499,12704,12705],{"class":3501,"line":3991},[3499,12706,7626],{"class":3509},[3499,12708,12709],{"class":3501,"line":3997},[3499,12710,12711],{"class":3529},"    # Максимальна сума транзакції\n",[3499,12713,12714,12717,12719,12721,12723,12725,12728,12730,12732,12734,12736],{"class":3501,"line":4006},[3499,12715,12716],{"class":3509},"    max_payment_limit: ",[3499,12718,7510],{"class":3554},[3499,12720,7641],{"class":3509},[3499,12722,7435],{"class":3547},[3499,12724,5964],{"class":3509},[3499,12726,12727],{"class":3771},"10000.00",[3499,12729,3468],{"class":3509},[3499,12731,7691],{"class":3547},[3499,12733,5964],{"class":3509},[3499,12735,6242],{"class":3771},[3499,12737,4876],{"class":3509},[3499,12739,12740],{"class":3501,"line":4593},[3499,12741,3523],{"emptyLinePlaceholder":3522},[3499,12743,12744],{"class":3501,"line":4598},[3499,12745,11921],{"class":3509},[3452,12747],{},[3474,12749,12751,12752,4710],{"id":12750},"крок-2-опис-моделей-платіжних-подій-schemaspy","Крок 2: Опис моделей платіжних подій (",[3403,12753,12754],{},"schemas.py",[3394,12756,12757,12758,12761,12762,12765],{},"Наш сервіс обробляє два типи подій від платіжного провайдера: ",[3403,12759,12760],{},"payment.succeeded"," (успішна оплата) та ",[3403,12763,12764],{},"payment.refunded"," (повернення коштів). Вони надходять на один ендпоінт. Опишемо схеми даних.",[3394,12767,12580,12768,6649],{},[3403,12769,12754],{},[3489,12771,12773],{"className":3491,"code":12772,"filename":3751,"language":3494,"meta":3495,"style":3495},"from datetime import datetime\nfrom typing import Literal, Union\nfrom pydantic import BaseModel, Field, EmailStr, field_validator\n\nclass PaymentSucceededData(BaseModel):\n    transaction_id: str = Field(min_length=10, max_length=50)\n    amount: float = Field(gt=0)\n    currency: str = Field(min_length=3, max_length=3)\n    customer_email: EmailStr\n\nclass RefundedData(BaseModel):\n    transaction_id: str\n    original_amount: float\n    refund_reason: str = Field(min_length=5)\n\n# Базова схема платіжного Webhook-а з дискримінатором\nclass PaymentWebhookEvent(BaseModel):\n    # Поле type виступає маркером для розділення схем\n    event_type: Literal[\"payment.succeeded\", \"payment.refunded\"] = Field(..., alias=\"type\")\n    created_at: datetime = Field(default_factory=datetime.utcnow)\n    \n    # Залежно від event_type, Pydantic обере правильну вкладену модель\n    data: Union[PaymentSucceededData, RefundedData]\n\n    @field_validator(\"data\")\n    @classmethod\n    def validate_amount_limit(cls, data_model):\n        # Імпортуємо налаштування для перевірки ліміту\n        from config import settings\n        \n        # Перевіряємо суму, якщо це подія успішного платежу\n        if isinstance(data_model, PaymentSucceededData):\n            if data_model.amount > settings.max_payment_limit:\n                raise ValueError(\n                    f\"Сума транзакції {data_model.amount} перевищує встановлений ліміт {settings.max_payment_limit}\"\n                )\n        return data_model\n",[3403,12774,12775,12785,12795,12806,12810,12823,12848,12865,12890,12895,12899,12912,12918,12926,12943,12947,12952,12965,12970,12994,13003,13007,13012,13017,13021,13032,13038,13056,13061,13074,13079,13084,13093,13101,13111,13138,13143],{"__ignoreMap":3495},[3499,12776,12777,12779,12781,12783],{"class":3501,"line":3502},[3499,12778,3506],{"class":3505},[3499,12780,7562],{"class":3509},[3499,12782,3513],{"class":3505},[3499,12784,7567],{"class":3509},[3499,12786,12787,12789,12791,12793],{"class":3501,"line":3519},[3499,12788,3506],{"class":3505},[3499,12790,3510],{"class":3509},[3499,12792,3513],{"class":3505},[3499,12794,10244],{"class":3509},[3499,12796,12797,12799,12801,12803],{"class":3501,"line":3526},[3499,12798,3506],{"class":3505},[3499,12800,5934],{"class":3509},[3499,12802,3513],{"class":3505},[3499,12804,12805],{"class":3509}," BaseModel, Field, EmailStr, field_validator\n",[3499,12807,12808],{"class":3501,"line":3533},[3499,12809,3523],{"emptyLinePlaceholder":3522},[3499,12811,12812,12814,12817,12819,12821],{"class":3501,"line":3571},[3499,12813,3858],{"class":3536},[3499,12815,12816],{"class":3554}," PaymentSucceededData",[3499,12818,3544],{"class":3509},[3499,12820,6870],{"class":3554},[3499,12822,4393],{"class":3509},[3499,12824,12825,12828,12830,12832,12834,12836,12838,12840,12842,12844,12846],{"class":3501,"line":3577},[3499,12826,12827],{"class":3509},"    transaction_id: ",[3499,12829,3560],{"class":3554},[3499,12831,7641],{"class":3509},[3499,12833,7644],{"class":3547},[3499,12835,5964],{"class":3509},[3499,12837,4863],{"class":3771},[3499,12839,3468],{"class":3509},[3499,12841,7653],{"class":3547},[3499,12843,5964],{"class":3509},[3499,12845,8829],{"class":3771},[3499,12847,4876],{"class":3509},[3499,12849,12850,12853,12855,12857,12859,12861,12863],{"class":3501,"line":3705},[3499,12851,12852],{"class":3509},"    amount: ",[3499,12854,7510],{"class":3554},[3499,12856,7641],{"class":3509},[3499,12858,7691],{"class":3547},[3499,12860,5964],{"class":3509},[3499,12862,6242],{"class":3771},[3499,12864,4876],{"class":3509},[3499,12866,12867,12870,12872,12874,12876,12878,12880,12882,12884,12886,12888],{"class":3501,"line":3800},[3499,12868,12869],{"class":3509},"    currency: ",[3499,12871,3560],{"class":3554},[3499,12873,7641],{"class":3509},[3499,12875,7644],{"class":3547},[3499,12877,5964],{"class":3509},[3499,12879,4792],{"class":3771},[3499,12881,3468],{"class":3509},[3499,12883,7653],{"class":3547},[3499,12885,5964],{"class":3509},[3499,12887,4792],{"class":3771},[3499,12889,4876],{"class":3509},[3499,12891,12892],{"class":3501,"line":3812},[3499,12893,12894],{"class":3509},"    customer_email: EmailStr\n",[3499,12896,12897],{"class":3501,"line":3826},[3499,12898,3523],{"emptyLinePlaceholder":3522},[3499,12900,12901,12903,12906,12908,12910],{"class":3501,"line":3985},[3499,12902,3858],{"class":3536},[3499,12904,12905],{"class":3554}," RefundedData",[3499,12907,3544],{"class":3509},[3499,12909,6870],{"class":3554},[3499,12911,4393],{"class":3509},[3499,12913,12914,12916],{"class":3501,"line":3991},[3499,12915,12827],{"class":3509},[3499,12917,3664],{"class":3554},[3499,12919,12920,12923],{"class":3501,"line":3997},[3499,12921,12922],{"class":3509},"    original_amount: ",[3499,12924,12925],{"class":3554},"float\n",[3499,12927,12928,12931,12933,12935,12937,12939,12941],{"class":3501,"line":4006},[3499,12929,12930],{"class":3509},"    refund_reason: ",[3499,12932,3560],{"class":3554},[3499,12934,7641],{"class":3509},[3499,12936,7644],{"class":3547},[3499,12938,5964],{"class":3509},[3499,12940,4915],{"class":3771},[3499,12942,4876],{"class":3509},[3499,12944,12945],{"class":3501,"line":4593},[3499,12946,3523],{"emptyLinePlaceholder":3522},[3499,12948,12949],{"class":3501,"line":4598},[3499,12950,12951],{"class":3529},"# Базова схема платіжного Webhook-а з дискримінатором\n",[3499,12953,12954,12956,12959,12961,12963],{"class":3501,"line":4604},[3499,12955,3858],{"class":3536},[3499,12957,12958],{"class":3554}," PaymentWebhookEvent",[3499,12960,3544],{"class":3509},[3499,12962,6870],{"class":3554},[3499,12964,4393],{"class":3509},[3499,12966,12967],{"class":3501,"line":5778},[3499,12968,12969],{"class":3529},"    # Поле type виступає маркером для розділення схем\n",[3499,12971,12972,12975,12978,12980,12983,12986,12988,12990,12992],{"class":3501,"line":5786},[3499,12973,12974],{"class":3509},"    event_type: Literal[",[3499,12976,12977],{"class":3979},"\"payment.succeeded\"",[3499,12979,3468],{"class":3509},[3499,12981,12982],{"class":3979},"\"payment.refunded\"",[3499,12984,12985],{"class":3509},"] = Field(..., ",[3499,12987,7451],{"class":3547},[3499,12989,5964],{"class":3509},[3499,12991,10384],{"class":3979},[3499,12993,4876],{"class":3509},[3499,12995,12996,12998,13000],{"class":3501,"line":5791},[3499,12997,7754],{"class":3509},[3499,12999,7439],{"class":3547},[3499,13001,13002],{"class":3509},"=datetime.utcnow)\n",[3499,13004,13005],{"class":3501,"line":5797},[3499,13006,7626],{"class":3509},[3499,13008,13009],{"class":3501,"line":5814},[3499,13010,13011],{"class":3529},"    # Залежно від event_type, Pydantic обере правильну вкладену модель\n",[3499,13013,13014],{"class":3501,"line":7786},[3499,13015,13016],{"class":3509},"    data: Union[PaymentSucceededData, RefundedData]\n",[3499,13018,13019],{"class":3501,"line":7792},[3499,13020,3523],{"emptyLinePlaceholder":3522},[3499,13022,13023,13025,13027,13030],{"class":3501,"line":7816},[3499,13024,8850],{"class":3540},[3499,13026,3544],{"class":3509},[3499,13028,13029],{"class":3979},"\"data\"",[3499,13031,4876],{"class":3509},[3499,13033,13034,13036],{"class":3501,"line":7828},[3499,13035,3951],{"class":3540},[3499,13037,3954],{"class":3554},[3499,13039,13040,13042,13045,13047,13049,13051,13054],{"class":3501,"line":7852},[3499,13041,3868],{"class":3536},[3499,13043,13044],{"class":3540}," validate_amount_limit",[3499,13046,3544],{"class":3509},[3499,13048,3843],{"class":3547},[3499,13050,3468],{"class":3509},[3499,13052,13053],{"class":3547},"data_model",[3499,13055,4393],{"class":3509},[3499,13057,13058],{"class":3501,"line":7875},[3499,13059,13060],{"class":3529},"        # Імпортуємо налаштування для перевірки ліміту\n",[3499,13062,13063,13066,13069,13071],{"class":3501,"line":7898},[3499,13064,13065],{"class":3505},"        from",[3499,13067,13068],{"class":3509}," config ",[3499,13070,3513],{"class":3505},[3499,13072,13073],{"class":3509}," settings\n",[3499,13075,13076],{"class":3501,"line":7925},[3499,13077,13078],{"class":3509},"        \n",[3499,13080,13081],{"class":3501,"line":7930},[3499,13082,13083],{"class":3529},"        # Перевіряємо суму, якщо це подія успішного платежу\n",[3499,13085,13086,13088,13090],{"class":3501,"line":7936},[3499,13087,8898],{"class":3505},[3499,13089,5070],{"class":3540},[3499,13091,13092],{"class":3509},"(data_model, PaymentSucceededData):\n",[3499,13094,13095,13098],{"class":3501,"line":7948},[3499,13096,13097],{"class":3505},"            if",[3499,13099,13100],{"class":3509}," data_model.amount > settings.max_payment_limit:\n",[3499,13102,13103,13106,13108],{"class":3501,"line":7974},[3499,13104,13105],{"class":3505},"                raise",[3499,13107,8917],{"class":3554},[3499,13109,13110],{"class":3509},"(\n",[3499,13112,13113,13116,13119,13121,13124,13126,13129,13131,13134,13136],{"class":3501,"line":7979},[3499,13114,13115],{"class":3536},"                    f",[3499,13117,13118],{"class":3979},"\"Сума транзакції ",[3499,13120,4085],{"class":3536},[3499,13122,13123],{"class":3509},"data_model.amount",[3499,13125,4094],{"class":3536},[3499,13127,13128],{"class":3979}," перевищує встановлений ліміт ",[3499,13130,4085],{"class":3536},[3499,13132,13133],{"class":3509},"settings.max_payment_limit",[3499,13135,4094],{"class":3536},[3499,13137,4097],{"class":3979},[3499,13139,13140],{"class":3501,"line":7985},[3499,13141,13142],{"class":3509},"                )\n",[3499,13144,13145,13147],{"class":3501,"line":7997},[3499,13146,3939],{"class":3505},[3499,13148,13149],{"class":3509}," data_model\n",[3452,13151],{},[3474,13153,13155,13156,4710],{"id":13154},"крок-3-точка-входу-та-логіка-обробки-подій-mainpy","Крок 3: Точка входу та логіка обробки подій (",[3403,13157,3751],{},[3394,13159,13160],{},"Тепер напишемо сервісний шар, який приймає сирі JSON-дані, запускає парсинг Pydantic і обробляє результати.",[3394,13162,12580,13163,6649],{},[3403,13164,3751],{},[3489,13166,13168],{"className":3491,"code":13167,"filename":3751,"language":3494,"meta":3495,"style":3495},"import json\nfrom pydantic import ValidationError\nfrom config import settings\nfrom schemas import PaymentWebhookEvent, PaymentSucceededData, RefundedData\n\ndef handle_incoming_webhook(raw_payload: str, signature: str) -> None:\n    # 1. Перевірка підпису (Імітація захисту)\n    # Зіставляємо сирий signature з налаштуваннями із .env\n    if signature != settings.signing_secret.get_secret_value():\n        print(\"❌ Помилка безпеки: підпис запиту невірний!\")\n        return\n\n    # 2. Валідація та парсинг за допомогою Pydantic\n    try:\n        data_dict = json.loads(raw_payload)\n        # Pydantic автоматично розбере тип data на основі дискримінатора 'type'\n        event = PaymentWebhookEvent(**data_dict)\n        \n        print(f\"✅ Webhook успішно верифіковано. Тип: {event.event_type}\")\n        print(f\"   Дата створення: {event.created_at}\")\n\n        # 3. Обробка бізнес-логіки\n        if isinstance(event.data, PaymentSucceededData):\n            process_payment(event.data)\n        elif isinstance(event.data, RefundedData):\n            process_refund(event.data)\n\n    except ValidationError as e:\n        print(\"❌ Помилка валідації вхідних даних Webhook:\")\n        for error in e.errors():\n            # Виводимо точне місце виникнення помилки\n            path = \" -> \".join(str(p) for p in error[\"loc\"])\n            print(f\"   Поле: [{path}] | Помилка: {error['msg']} | Отримано: {error.get('input')}\")\n\ndef process_payment(data: PaymentSucceededData) -> None:\n    print(f\"💳 [БІЗНЕС-ЛОГІКА] Зараховано платіж {data.amount} {data.currency}\")\n    print(f\"   Клієнт: {data.customer_email} | ID транзакції: {data.transaction_id}\\n\")\n\ndef process_refund(data: RefundedData) -> None:\n    print(f\"🔄 [БІЗНЕС-ЛОГІКА] Оформлено повернення коштів для транзакції {data.transaction_id}\")\n    print(f\"   Причина: {data.refund_reason} | Сума: {data.original_amount}\\n\")\n\n\n# --- Тестові сценарії (симуляція роботи вебсервера) ---\nif __name__ == \"__main__\":\n    correct_signature = \"stripe_whsec_supersecret_key_123\"\n\n    # Сценарій 1: Успішний платіж\n    payload_success = {\n        \"type\": \"payment.succeeded\",\n        \"data\": {\n            \"transaction_id\": \"tx_992019a82bb\",\n            \"amount\": 250.50,\n            \"currency\": \"USD\",\n            \"customer_email\": \"student@kostyl.dev\"\n        }\n    }\n    \n    # Сценарій 2: Повернення коштів\n    payload_refund = {\n        \"type\": \"payment.refunded\",\n        \"data\": {\n            \"transaction_id\": \"tx_992019a82bb\",\n            \"original_amount\": 250.50,\n            \"refund_reason\": \"Клієнт передумав купувати курс\"\n        }\n    }\n\n    # Сценарій 3: Помилка ліміту оплати (amount > max_payment_limit)\n    payload_invalid_limit = {\n        \"type\": \"payment.succeeded\",\n        \"data\": {\n            \"transaction_id\": \"tx_too_large_amount_value\",\n            \"amount\": 999999.00,\n            \"currency\": \"USD\",\n            \"customer_email\": \"rich_hacker@test.com\"\n        }\n    }\n\n    # Сценарій 4: Помилка структури (некоректний email)\n    payload_invalid_email = {\n        \"type\": \"payment.succeeded\",\n        \"data\": {\n            \"transaction_id\": \"tx_short\", # \u003C 10 символів\n            \"amount\": 100.00,\n            \"currency\": \"USD\",\n            \"customer_email\": \"not-an-email\"\n        }\n    }\n\n    print(\"=== ТЕСТ 1: Успішний платіж ===\")\n    handle_incoming_webhook(json.dumps(payload_success), correct_signature)\n\n    print(\"=== ТЕСТ 2: Оформлення повернення ===\")\n    handle_incoming_webhook(json.dumps(payload_refund), correct_signature)\n\n    print(\"=== ТЕСТ 3: Порушення ліміту безпеки ===\")\n    handle_incoming_webhook(json.dumps(payload_invalid_limit), correct_signature)\n\n    print(\"=== ТЕСТ 4: Помилка валідації пошти та довжини ID ===\")\n    handle_incoming_webhook(json.dumps(payload_invalid_email), correct_signature)\n",[3403,13169,13170,13177,13188,13198,13210,13214,13245,13250,13255,13262,13273,13278,13282,13287,13294,13299,13304,13309,13313,13335,13357,13361,13366,13375,13380,13390,13395,13399,13410,13421,13434,13439,13470,13522,13526,13544,13574,13608,13612,13630,13651,13685,13689,13693,13698,13713,13721,13725,13730,13735,13746,13754,13766,13778,13790,13800,13806,13812,13817,13823,13829,13840,13847,13858,13870,13881,13886,13891,13896,13902,13908,13919,13926,13938,13950,13961,13971,13976,13981,13986,13992,13998,14009,14016,14031,14043,14054,14064,14069,14074,14079,14091,14097,14102,14114,14120,14125,14137,14143,14148,14160],{"__ignoreMap":3495},[3499,13171,13172,13174],{"class":3501,"line":3502},[3499,13173,3513],{"class":3505},[3499,13175,13176],{"class":3509}," json\n",[3499,13178,13179,13181,13183,13185],{"class":3501,"line":3519},[3499,13180,3506],{"class":3505},[3499,13182,5934],{"class":3509},[3499,13184,3513],{"class":3505},[3499,13186,13187],{"class":3509}," ValidationError\n",[3499,13189,13190,13192,13194,13196],{"class":3501,"line":3526},[3499,13191,3506],{"class":3505},[3499,13193,13068],{"class":3509},[3499,13195,3513],{"class":3505},[3499,13197,13073],{"class":3509},[3499,13199,13200,13202,13205,13207],{"class":3501,"line":3533},[3499,13201,3506],{"class":3505},[3499,13203,13204],{"class":3509}," schemas ",[3499,13206,3513],{"class":3505},[3499,13208,13209],{"class":3509}," PaymentWebhookEvent, PaymentSucceededData, RefundedData\n",[3499,13211,13212],{"class":3501,"line":3571},[3499,13213,3523],{"emptyLinePlaceholder":3522},[3499,13215,13216,13218,13221,13223,13226,13228,13230,13232,13235,13237,13239,13241,13243],{"class":3501,"line":3577},[3499,13217,3537],{"class":3536},[3499,13219,13220],{"class":3540}," handle_incoming_webhook",[3499,13222,3544],{"class":3509},[3499,13224,13225],{"class":3547},"raw_payload",[3499,13227,3609],{"class":3509},[3499,13229,3560],{"class":3554},[3499,13231,3468],{"class":3509},[3499,13233,13234],{"class":3547},"signature",[3499,13236,3609],{"class":3509},[3499,13238,3560],{"class":3554},[3499,13240,3887],{"class":3509},[3499,13242,3483],{"class":3536},[3499,13244,3629],{"class":3509},[3499,13246,13247],{"class":3501,"line":3705},[3499,13248,13249],{"class":3529},"    # 1. Перевірка підпису (Імітація захисту)\n",[3499,13251,13252],{"class":3501,"line":3800},[3499,13253,13254],{"class":3529},"    # Зіставляємо сирий signature з налаштуваннями із .env\n",[3499,13256,13257,13259],{"class":3501,"line":3812},[3499,13258,5067],{"class":3505},[3499,13260,13261],{"class":3509}," signature != settings.signing_secret.get_secret_value():\n",[3499,13263,13264,13266,13268,13271],{"class":3501,"line":3826},[3499,13265,5687],{"class":3540},[3499,13267,3544],{"class":3509},[3499,13269,13270],{"class":3979},"\"❌ Помилка безпеки: підпис запиту невірний!\"",[3499,13272,4876],{"class":3509},[3499,13274,13275],{"class":3501,"line":3985},[3499,13276,13277],{"class":3505},"        return\n",[3499,13279,13280],{"class":3501,"line":3991},[3499,13281,3523],{"emptyLinePlaceholder":3522},[3499,13283,13284],{"class":3501,"line":3997},[3499,13285,13286],{"class":3529},"    # 2. Валідація та парсинг за допомогою Pydantic\n",[3499,13288,13289,13292],{"class":3501,"line":4006},[3499,13290,13291],{"class":3505},"    try",[3499,13293,3629],{"class":3509},[3499,13295,13296],{"class":3501,"line":4593},[3499,13297,13298],{"class":3509},"        data_dict = json.loads(raw_payload)\n",[3499,13300,13301],{"class":3501,"line":4598},[3499,13302,13303],{"class":3529},"        # Pydantic автоматично розбере тип data на основі дискримінатора 'type'\n",[3499,13305,13306],{"class":3501,"line":4604},[3499,13307,13308],{"class":3509},"        event = PaymentWebhookEvent(**data_dict)\n",[3499,13310,13311],{"class":3501,"line":5778},[3499,13312,13078],{"class":3509},[3499,13314,13315,13317,13319,13321,13324,13326,13329,13331,13333],{"class":3501,"line":5786},[3499,13316,5687],{"class":3540},[3499,13318,3544],{"class":3509},[3499,13320,5692],{"class":3536},[3499,13322,13323],{"class":3979},"\"✅ Webhook успішно верифіковано. Тип: ",[3499,13325,4085],{"class":3536},[3499,13327,13328],{"class":3509},"event.event_type",[3499,13330,4094],{"class":3536},[3499,13332,7847],{"class":3979},[3499,13334,4876],{"class":3509},[3499,13336,13337,13339,13341,13343,13346,13348,13351,13353,13355],{"class":3501,"line":5791},[3499,13338,5687],{"class":3540},[3499,13340,3544],{"class":3509},[3499,13342,5692],{"class":3536},[3499,13344,13345],{"class":3979},"\"   Дата створення: ",[3499,13347,4085],{"class":3536},[3499,13349,13350],{"class":3509},"event.created_at",[3499,13352,4094],{"class":3536},[3499,13354,7847],{"class":3979},[3499,13356,4876],{"class":3509},[3499,13358,13359],{"class":3501,"line":5797},[3499,13360,3523],{"emptyLinePlaceholder":3522},[3499,13362,13363],{"class":3501,"line":5814},[3499,13364,13365],{"class":3529},"        # 3. Обробка бізнес-логіки\n",[3499,13367,13368,13370,13372],{"class":3501,"line":7786},[3499,13369,8898],{"class":3505},[3499,13371,5070],{"class":3540},[3499,13373,13374],{"class":3509},"(event.data, PaymentSucceededData):\n",[3499,13376,13377],{"class":3501,"line":7792},[3499,13378,13379],{"class":3509},"            process_payment(event.data)\n",[3499,13381,13382,13385,13387],{"class":3501,"line":7816},[3499,13383,13384],{"class":3505},"        elif",[3499,13386,5070],{"class":3540},[3499,13388,13389],{"class":3509},"(event.data, RefundedData):\n",[3499,13391,13392],{"class":3501,"line":7828},[3499,13393,13394],{"class":3509},"            process_refund(event.data)\n",[3499,13396,13397],{"class":3501,"line":7852},[3499,13398,3523],{"emptyLinePlaceholder":3522},[3499,13400,13401,13404,13406,13408],{"class":3501,"line":7875},[3499,13402,13403],{"class":3505},"    except",[3499,13405,8009],{"class":3509},[3499,13407,8012],{"class":3505},[3499,13409,8015],{"class":3509},[3499,13411,13412,13414,13416,13419],{"class":3501,"line":7898},[3499,13413,5687],{"class":3540},[3499,13415,3544],{"class":3509},[3499,13417,13418],{"class":3979},"\"❌ Помилка валідації вхідних даних Webhook:\"",[3499,13420,4876],{"class":3509},[3499,13422,13423,13426,13429,13431],{"class":3501,"line":7925},[3499,13424,13425],{"class":3505},"        for",[3499,13427,13428],{"class":3509}," error ",[3499,13430,6184],{"class":3505},[3499,13432,13433],{"class":3509}," e.errors():\n",[3499,13435,13436],{"class":3501,"line":7930},[3499,13437,13438],{"class":3529},"            # Виводимо точне місце виникнення помилки\n",[3499,13440,13441,13444,13447,13450,13452,13455,13457,13460,13462,13465,13468],{"class":3501,"line":7936},[3499,13442,13443],{"class":3509},"            path = ",[3499,13445,13446],{"class":3979},"\" -> \"",[3499,13448,13449],{"class":3509},".join(",[3499,13451,3560],{"class":3554},[3499,13453,13454],{"class":3509},"(p) ",[3499,13456,6178],{"class":3505},[3499,13458,13459],{"class":3509}," p ",[3499,13461,6184],{"class":3505},[3499,13463,13464],{"class":3509}," error[",[3499,13466,13467],{"class":3979},"\"loc\"",[3499,13469,7670],{"class":3509},[3499,13471,13472,13475,13477,13479,13482,13484,13487,13489,13492,13494,13497,13499,13501,13503,13506,13508,13511,13514,13516,13518,13520],{"class":3501,"line":7948},[3499,13473,13474],{"class":3540},"            print",[3499,13476,3544],{"class":3509},[3499,13478,5692],{"class":3536},[3499,13480,13481],{"class":3979},"\"   Поле: [",[3499,13483,4085],{"class":3536},[3499,13485,13486],{"class":3509},"path",[3499,13488,4094],{"class":3536},[3499,13490,13491],{"class":3979},"] | Помилка: ",[3499,13493,4085],{"class":3536},[3499,13495,13496],{"class":3509},"error[",[3499,13498,8039],{"class":3979},[3499,13500,8042],{"class":3509},[3499,13502,4094],{"class":3536},[3499,13504,13505],{"class":3979}," | Отримано: ",[3499,13507,4085],{"class":3536},[3499,13509,13510],{"class":3509},"error.get(",[3499,13512,13513],{"class":3979},"'input'",[3499,13515,4710],{"class":3509},[3499,13517,4094],{"class":3536},[3499,13519,7847],{"class":3979},[3499,13521,4876],{"class":3509},[3499,13523,13524],{"class":3501,"line":7974},[3499,13525,3523],{"emptyLinePlaceholder":3522},[3499,13527,13528,13530,13533,13535,13537,13540,13542],{"class":3501,"line":7979},[3499,13529,3537],{"class":3536},[3499,13531,13532],{"class":3540}," process_payment",[3499,13534,3544],{"class":3509},[3499,13536,5001],{"class":3547},[3499,13538,13539],{"class":3509},": PaymentSucceededData) -> ",[3499,13541,3483],{"class":3536},[3499,13543,3629],{"class":3509},[3499,13545,13546,13548,13550,13552,13555,13557,13560,13562,13565,13568,13570,13572],{"class":3501,"line":7985},[3499,13547,6236],{"class":3540},[3499,13549,3544],{"class":3509},[3499,13551,5692],{"class":3536},[3499,13553,13554],{"class":3979},"\"💳 [БІЗНЕС-ЛОГІКА] Зараховано платіж ",[3499,13556,4085],{"class":3536},[3499,13558,13559],{"class":3509},"data.amount",[3499,13561,4094],{"class":3536},[3499,13563,13564],{"class":3536}," {",[3499,13566,13567],{"class":3509},"data.currency",[3499,13569,4094],{"class":3536},[3499,13571,7847],{"class":3979},[3499,13573,4876],{"class":3509},[3499,13575,13576,13578,13580,13582,13585,13587,13590,13592,13595,13597,13600,13602,13604,13606],{"class":3501,"line":7997},[3499,13577,6236],{"class":3540},[3499,13579,3544],{"class":3509},[3499,13581,5692],{"class":3536},[3499,13583,13584],{"class":3979},"\"   Клієнт: ",[3499,13586,4085],{"class":3536},[3499,13588,13589],{"class":3509},"data.customer_email",[3499,13591,4094],{"class":3536},[3499,13593,13594],{"class":3979}," | ID транзакції: ",[3499,13596,4085],{"class":3536},[3499,13598,13599],{"class":3509},"data.transaction_id",[3499,13601,4094],{"class":3536},[3499,13603,7918],{"class":7917},[3499,13605,7847],{"class":3979},[3499,13607,4876],{"class":3509},[3499,13609,13610],{"class":3501,"line":8003},[3499,13611,3523],{"emptyLinePlaceholder":3522},[3499,13613,13614,13616,13619,13621,13623,13626,13628],{"class":3501,"line":8018},[3499,13615,3537],{"class":3536},[3499,13617,13618],{"class":3540}," process_refund",[3499,13620,3544],{"class":3509},[3499,13622,5001],{"class":3547},[3499,13624,13625],{"class":3509},": RefundedData) -> ",[3499,13627,3483],{"class":3536},[3499,13629,3629],{"class":3509},[3499,13631,13632,13634,13636,13638,13641,13643,13645,13647,13649],{"class":3501,"line":8053},[3499,13633,6236],{"class":3540},[3499,13635,3544],{"class":3509},[3499,13637,5692],{"class":3536},[3499,13639,13640],{"class":3979},"\"🔄 [БІЗНЕС-ЛОГІКА] Оформлено повернення коштів для транзакції ",[3499,13642,4085],{"class":3536},[3499,13644,13599],{"class":3509},[3499,13646,4094],{"class":3536},[3499,13648,7847],{"class":3979},[3499,13650,4876],{"class":3509},[3499,13652,13653,13655,13657,13659,13662,13664,13667,13669,13672,13674,13677,13679,13681,13683],{"class":3501,"line":8058},[3499,13654,6236],{"class":3540},[3499,13656,3544],{"class":3509},[3499,13658,5692],{"class":3536},[3499,13660,13661],{"class":3979},"\"   Причина: ",[3499,13663,4085],{"class":3536},[3499,13665,13666],{"class":3509},"data.refund_reason",[3499,13668,4094],{"class":3536},[3499,13670,13671],{"class":3979}," | Сума: ",[3499,13673,4085],{"class":3536},[3499,13675,13676],{"class":3509},"data.original_amount",[3499,13678,4094],{"class":3536},[3499,13680,7918],{"class":7917},[3499,13682,7847],{"class":3979},[3499,13684,4876],{"class":3509},[3499,13686,13687],{"class":3501,"line":8065},[3499,13688,3523],{"emptyLinePlaceholder":3522},[3499,13690,13691],{"class":3501,"line":8071},[3499,13692,3523],{"emptyLinePlaceholder":3522},[3499,13694,13695],{"class":3501,"line":8083},[3499,13696,13697],{"class":3529},"# --- Тестові сценарії (симуляція роботи вебсервера) ---\n",[3499,13699,13700,13702,13705,13708,13711],{"class":3501,"line":8108},[3499,13701,6223],{"class":3505},[3499,13703,13704],{"class":3547}," __name__",[3499,13706,13707],{"class":3509}," == ",[3499,13709,13710],{"class":3979},"\"__main__\"",[3499,13712,3629],{"class":3509},[3499,13714,13715,13718],{"class":3501,"line":8119},[3499,13716,13717],{"class":3509},"    correct_signature = ",[3499,13719,13720],{"class":3979},"\"stripe_whsec_supersecret_key_123\"\n",[3499,13722,13723],{"class":3501,"line":9289},[3499,13724,3523],{"emptyLinePlaceholder":3522},[3499,13726,13727],{"class":3501,"line":9295},[3499,13728,13729],{"class":3529},"    # Сценарій 1: Успішний платіж\n",[3499,13731,13732],{"class":3501,"line":9307},[3499,13733,13734],{"class":3509},"    payload_success = {\n",[3499,13736,13737,13740,13742,13744],{"class":3501,"line":9313},[3499,13738,13739],{"class":3979},"        \"type\"",[3499,13741,3609],{"class":3509},[3499,13743,12977],{"class":3979},[3499,13745,8474],{"class":3509},[3499,13747,13748,13751],{"class":3501,"line":9324},[3499,13749,13750],{"class":3979},"        \"data\"",[3499,13752,13753],{"class":3509},": {\n",[3499,13755,13756,13759,13761,13764],{"class":3501,"line":9343},[3499,13757,13758],{"class":3979},"            \"transaction_id\"",[3499,13760,3609],{"class":3509},[3499,13762,13763],{"class":3979},"\"tx_992019a82bb\"",[3499,13765,8474],{"class":3509},[3499,13767,13768,13771,13773,13776],{"class":3501,"line":9362},[3499,13769,13770],{"class":3979},"            \"amount\"",[3499,13772,3609],{"class":3509},[3499,13774,13775],{"class":3771},"250.50",[3499,13777,8474],{"class":3509},[3499,13779,13780,13783,13785,13788],{"class":3501,"line":9367},[3499,13781,13782],{"class":3979},"            \"currency\"",[3499,13784,3609],{"class":3509},[3499,13786,13787],{"class":3979},"\"USD\"",[3499,13789,8474],{"class":3509},[3499,13791,13792,13795,13797],{"class":3501,"line":9378},[3499,13793,13794],{"class":3979},"            \"customer_email\"",[3499,13796,3609],{"class":3509},[3499,13798,13799],{"class":3979},"\"student@kostyl.dev\"\n",[3499,13801,13803],{"class":3501,"line":13802},56,[3499,13804,13805],{"class":3509},"        }\n",[3499,13807,13809],{"class":3501,"line":13808},57,[3499,13810,13811],{"class":3509},"    }\n",[3499,13813,13815],{"class":3501,"line":13814},58,[3499,13816,7626],{"class":3509},[3499,13818,13820],{"class":3501,"line":13819},59,[3499,13821,13822],{"class":3529},"    # Сценарій 2: Повернення коштів\n",[3499,13824,13826],{"class":3501,"line":13825},60,[3499,13827,13828],{"class":3509},"    payload_refund = {\n",[3499,13830,13832,13834,13836,13838],{"class":3501,"line":13831},61,[3499,13833,13739],{"class":3979},[3499,13835,3609],{"class":3509},[3499,13837,12982],{"class":3979},[3499,13839,8474],{"class":3509},[3499,13841,13843,13845],{"class":3501,"line":13842},62,[3499,13844,13750],{"class":3979},[3499,13846,13753],{"class":3509},[3499,13848,13850,13852,13854,13856],{"class":3501,"line":13849},63,[3499,13851,13758],{"class":3979},[3499,13853,3609],{"class":3509},[3499,13855,13763],{"class":3979},[3499,13857,8474],{"class":3509},[3499,13859,13861,13864,13866,13868],{"class":3501,"line":13860},64,[3499,13862,13863],{"class":3979},"            \"original_amount\"",[3499,13865,3609],{"class":3509},[3499,13867,13775],{"class":3771},[3499,13869,8474],{"class":3509},[3499,13871,13873,13876,13878],{"class":3501,"line":13872},65,[3499,13874,13875],{"class":3979},"            \"refund_reason\"",[3499,13877,3609],{"class":3509},[3499,13879,13880],{"class":3979},"\"Клієнт передумав купувати курс\"\n",[3499,13882,13884],{"class":3501,"line":13883},66,[3499,13885,13805],{"class":3509},[3499,13887,13889],{"class":3501,"line":13888},67,[3499,13890,13811],{"class":3509},[3499,13892,13894],{"class":3501,"line":13893},68,[3499,13895,3523],{"emptyLinePlaceholder":3522},[3499,13897,13899],{"class":3501,"line":13898},69,[3499,13900,13901],{"class":3529},"    # Сценарій 3: Помилка ліміту оплати (amount > max_payment_limit)\n",[3499,13903,13905],{"class":3501,"line":13904},70,[3499,13906,13907],{"class":3509},"    payload_invalid_limit = {\n",[3499,13909,13911,13913,13915,13917],{"class":3501,"line":13910},71,[3499,13912,13739],{"class":3979},[3499,13914,3609],{"class":3509},[3499,13916,12977],{"class":3979},[3499,13918,8474],{"class":3509},[3499,13920,13922,13924],{"class":3501,"line":13921},72,[3499,13923,13750],{"class":3979},[3499,13925,13753],{"class":3509},[3499,13927,13929,13931,13933,13936],{"class":3501,"line":13928},73,[3499,13930,13758],{"class":3979},[3499,13932,3609],{"class":3509},[3499,13934,13935],{"class":3979},"\"tx_too_large_amount_value\"",[3499,13937,8474],{"class":3509},[3499,13939,13941,13943,13945,13948],{"class":3501,"line":13940},74,[3499,13942,13770],{"class":3979},[3499,13944,3609],{"class":3509},[3499,13946,13947],{"class":3771},"999999.00",[3499,13949,8474],{"class":3509},[3499,13951,13953,13955,13957,13959],{"class":3501,"line":13952},75,[3499,13954,13782],{"class":3979},[3499,13956,3609],{"class":3509},[3499,13958,13787],{"class":3979},[3499,13960,8474],{"class":3509},[3499,13962,13964,13966,13968],{"class":3501,"line":13963},76,[3499,13965,13794],{"class":3979},[3499,13967,3609],{"class":3509},[3499,13969,13970],{"class":3979},"\"rich_hacker@test.com\"\n",[3499,13972,13974],{"class":3501,"line":13973},77,[3499,13975,13805],{"class":3509},[3499,13977,13979],{"class":3501,"line":13978},78,[3499,13980,13811],{"class":3509},[3499,13982,13984],{"class":3501,"line":13983},79,[3499,13985,3523],{"emptyLinePlaceholder":3522},[3499,13987,13989],{"class":3501,"line":13988},80,[3499,13990,13991],{"class":3529},"    # Сценарій 4: Помилка структури (некоректний email)\n",[3499,13993,13995],{"class":3501,"line":13994},81,[3499,13996,13997],{"class":3509},"    payload_invalid_email = {\n",[3499,13999,14001,14003,14005,14007],{"class":3501,"line":14000},82,[3499,14002,13739],{"class":3979},[3499,14004,3609],{"class":3509},[3499,14006,12977],{"class":3979},[3499,14008,8474],{"class":3509},[3499,14010,14012,14014],{"class":3501,"line":14011},83,[3499,14013,13750],{"class":3979},[3499,14015,13753],{"class":3509},[3499,14017,14019,14021,14023,14026,14028],{"class":3501,"line":14018},84,[3499,14020,13758],{"class":3979},[3499,14022,3609],{"class":3509},[3499,14024,14025],{"class":3979},"\"tx_short\"",[3499,14027,3468],{"class":3509},[3499,14029,14030],{"class":3529},"# \u003C 10 символів\n",[3499,14032,14034,14036,14038,14041],{"class":3501,"line":14033},85,[3499,14035,13770],{"class":3979},[3499,14037,3609],{"class":3509},[3499,14039,14040],{"class":3771},"100.00",[3499,14042,8474],{"class":3509},[3499,14044,14046,14048,14050,14052],{"class":3501,"line":14045},86,[3499,14047,13782],{"class":3979},[3499,14049,3609],{"class":3509},[3499,14051,13787],{"class":3979},[3499,14053,8474],{"class":3509},[3499,14055,14057,14059,14061],{"class":3501,"line":14056},87,[3499,14058,13794],{"class":3979},[3499,14060,3609],{"class":3509},[3499,14062,14063],{"class":3979},"\"not-an-email\"\n",[3499,14065,14067],{"class":3501,"line":14066},88,[3499,14068,13805],{"class":3509},[3499,14070,14072],{"class":3501,"line":14071},89,[3499,14073,13811],{"class":3509},[3499,14075,14077],{"class":3501,"line":14076},90,[3499,14078,3523],{"emptyLinePlaceholder":3522},[3499,14080,14082,14084,14086,14089],{"class":3501,"line":14081},91,[3499,14083,6236],{"class":3540},[3499,14085,3544],{"class":3509},[3499,14087,14088],{"class":3979},"\"=== ТЕСТ 1: Успішний платіж ===\"",[3499,14090,4876],{"class":3509},[3499,14092,14094],{"class":3501,"line":14093},92,[3499,14095,14096],{"class":3509},"    handle_incoming_webhook(json.dumps(payload_success), correct_signature)\n",[3499,14098,14100],{"class":3501,"line":14099},93,[3499,14101,3523],{"emptyLinePlaceholder":3522},[3499,14103,14105,14107,14109,14112],{"class":3501,"line":14104},94,[3499,14106,6236],{"class":3540},[3499,14108,3544],{"class":3509},[3499,14110,14111],{"class":3979},"\"=== ТЕСТ 2: Оформлення повернення ===\"",[3499,14113,4876],{"class":3509},[3499,14115,14117],{"class":3501,"line":14116},95,[3499,14118,14119],{"class":3509},"    handle_incoming_webhook(json.dumps(payload_refund), correct_signature)\n",[3499,14121,14123],{"class":3501,"line":14122},96,[3499,14124,3523],{"emptyLinePlaceholder":3522},[3499,14126,14128,14130,14132,14135],{"class":3501,"line":14127},97,[3499,14129,6236],{"class":3540},[3499,14131,3544],{"class":3509},[3499,14133,14134],{"class":3979},"\"=== ТЕСТ 3: Порушення ліміту безпеки ===\"",[3499,14136,4876],{"class":3509},[3499,14138,14140],{"class":3501,"line":14139},98,[3499,14141,14142],{"class":3509},"    handle_incoming_webhook(json.dumps(payload_invalid_limit), correct_signature)\n",[3499,14144,14146],{"class":3501,"line":14145},99,[3499,14147,3523],{"emptyLinePlaceholder":3522},[3499,14149,14151,14153,14155,14158],{"class":3501,"line":14150},100,[3499,14152,6236],{"class":3540},[3499,14154,3544],{"class":3509},[3499,14156,14157],{"class":3979},"\"=== ТЕСТ 4: Помилка валідації пошти та довжини ID ===\"",[3499,14159,4876],{"class":3509},[3499,14161,14163],{"class":3501,"line":14162},101,[3499,14164,14165],{"class":3509},"    handle_incoming_webhook(json.dumps(payload_invalid_email), correct_signature)\n",[3452,14167],{},[3474,14169,14171],{"id":14170},"крок-4-встановлення-залежностей-та-запуск-проєкту","Крок 4: Встановлення залежностей та запуск проєкту",[3394,14173,14174],{},"Створіть необхідні файли у вашій системі та перевірте правильність роботи скрипта.",[3486,14176,14177,14265,14300],{},[3489,14178,14180],{"className":6765,"code":14179,"filename":6959,"language":6768,"meta":3495,"style":3495},"# Створюємо директорію проєкту та переходимо\nmkdir -p payment_processor\ncd payment_processor\n\n# Створюємо віртуальне оточення\npython -m venv .venv\nsource .venv\u002Fbin\u002Factivate\n\n# Встановлюємо залежності: pydantic, pydantic-settings та email-validator\npip install pydantic pydantic-settings \"pydantic[email]\"\n\n# Запускаємо скрипт\npython main.py\n",[3403,14181,14182,14187,14198,14205,14209,14214,14224,14230,14234,14239,14251,14255,14259],{"__ignoreMap":3495},[3499,14183,14184],{"class":3501,"line":3502},[3499,14185,14186],{"class":3529},"# Створюємо директорію проєкту та переходимо\n",[3499,14188,14189,14192,14195],{"class":3501,"line":3519},[3499,14190,14191],{"class":3540},"mkdir",[3499,14193,14194],{"class":3536}," -p",[3499,14196,14197],{"class":3979}," payment_processor\n",[3499,14199,14200,14203],{"class":3501,"line":3526},[3499,14201,14202],{"class":3540},"cd",[3499,14204,14197],{"class":3979},[3499,14206,14207],{"class":3501,"line":3533},[3499,14208,3523],{"emptyLinePlaceholder":3522},[3499,14210,14211],{"class":3501,"line":3571},[3499,14212,14213],{"class":3529},"# Створюємо віртуальне оточення\n",[3499,14215,14216,14218,14220,14222],{"class":3501,"line":3577},[3499,14217,3494],{"class":3540},[3499,14219,6973],{"class":3536},[3499,14221,6976],{"class":3979},[3499,14223,6979],{"class":3979},[3499,14225,14226,14228],{"class":3501,"line":3705},[3499,14227,6984],{"class":3540},[3499,14229,7084],{"class":3979},[3499,14231,14232],{"class":3501,"line":3800},[3499,14233,3523],{"emptyLinePlaceholder":3522},[3499,14235,14236],{"class":3501,"line":3812},[3499,14237,14238],{"class":3529},"# Встановлюємо залежності: pydantic, pydantic-settings та email-validator\n",[3499,14240,14241,14243,14245,14247,14249],{"class":3501,"line":3826},[3499,14242,6959],{"class":3540},[3499,14244,7011],{"class":3979},[3499,14246,7059],{"class":3979},[3499,14248,12049],{"class":3979},[3499,14250,8642],{"class":3979},[3499,14252,14253],{"class":3501,"line":3985},[3499,14254,3523],{"emptyLinePlaceholder":3522},[3499,14256,14257],{"class":3501,"line":3991},[3499,14258,8651],{"class":3529},[3499,14260,14261,14263],{"class":3501,"line":3997},[3499,14262,3494],{"class":3540},[3499,14264,7030],{"class":3979},[3489,14266,14268],{"className":6765,"code":14267,"filename":7034,"language":6768,"meta":3495,"style":3495},"# uv дозволяє запустити локальний файл із автоматичним керуванням залежностями\n# Створюємо файли у директорії та запускаємо:\nuv run --with pydantic --with pydantic-settings --with \"pydantic[email]\" main.py\n",[3403,14269,14270,14275,14280],{"__ignoreMap":3495},[3499,14271,14272],{"class":3501,"line":3502},[3499,14273,14274],{"class":3529},"# uv дозволяє запустити локальний файл із автоматичним керуванням залежностями\n",[3499,14276,14277],{"class":3501,"line":3519},[3499,14278,14279],{"class":3529},"# Створюємо файли у директорії та запускаємо:\n",[3499,14281,14282,14284,14286,14288,14290,14292,14294,14296,14298],{"class":3501,"line":3526},[3499,14283,7034],{"class":3540},[3499,14285,7053],{"class":3979},[3499,14287,7056],{"class":3536},[3499,14289,7059],{"class":3979},[3499,14291,7056],{"class":3536},[3499,14293,12049],{"class":3979},[3499,14295,7056],{"class":3536},[3499,14297,8678],{"class":3979},[3499,14299,7030],{"class":3979},[3489,14301,14303],{"className":6765,"code":14302,"filename":7105,"language":6768,"meta":3495,"style":3495},"# Ініціалізуємо проєкт та додаємо пакети\npoetry init -n\npoetry add pydantic pydantic-settings \"pydantic[email]\"\n\n# Запуск скрипта через віртуальне оточення poetry\npoetry run python main.py\n",[3403,14304,14305,14310,14318,14330,14334,14339],{"__ignoreMap":3495},[3499,14306,14307],{"class":3501,"line":3502},[3499,14308,14309],{"class":3529},"# Ініціалізуємо проєкт та додаємо пакети\n",[3499,14311,14312,14314,14316],{"class":3501,"line":3519},[3499,14313,7105],{"class":3540},[3499,14315,7119],{"class":3979},[3499,14317,7122],{"class":3536},[3499,14319,14320,14322,14324,14326,14328],{"class":3501,"line":3526},[3499,14321,7105],{"class":3540},[3499,14323,5378],{"class":3979},[3499,14325,7059],{"class":3979},[3499,14327,12049],{"class":3979},[3499,14329,8642],{"class":3979},[3499,14331,14332],{"class":3501,"line":3533},[3499,14333,3523],{"emptyLinePlaceholder":3522},[3499,14335,14336],{"class":3501,"line":3571},[3499,14337,14338],{"class":3529},"# Запуск скрипта через віртуальне оточення poetry\n",[3499,14340,14341,14343,14345,14347],{"class":3501,"line":3577},[3499,14342,7105],{"class":3540},[3499,14344,7053],{"class":3979},[3499,14346,7157],{"class":3979},[3499,14348,7030],{"class":3979},[3741,14350,14352],{"id":14351},"лог-вихідних-даних-запуску-програми","Лог вихідних даних запуску програми:",[3394,14354,14355],{},"При запуску скрипту ви маєте побачити детальні результати валідації:",[3489,14357,14360],{"className":14358,"code":14359,"language":5211,"meta":3495},[12148],"=== ТЕСТ 1: Успішний платіж ===\n✅ Webhook успішно верифіковано. Тип: payment.succeeded\n   Дата створення: 2026-06-27 15:55:00.123456\n💳 [БІЗНЕС-ЛОГІКА] Зараховано платіж 250.5 USD\n   Клієнт: student@kostyl.dev | ID транзакції: tx_992019a82bb\n\n=== ТЕСТ 2: Оформлення повернення ===\n✅ Webhook успішно верифіковано. Тип: payment.refunded\n   Дата створення: 2026-06-27 15:55:00.123890\n🔄 [БІЗНЕС-ЛОГІКА] Оформлено повернення коштів для транзакції tx_992019a82bb\n   Причина: Клієнт передумав купувати курс | Сума: 250.5\n\n=== ТЕСТ 3: Порушення ліміту безпеки ===\n❌ Помилка валідації вхідних даних Webhook:\n   Поле: [data] | Помилка: Value error, Сума транзакції 999999.0 перевищує встановлений ліміт 50000.0 | Отримано: {'transaction_id': 'tx_too_large_amount_value', 'amount': 999999.0, 'currency': 'USD', 'customer_email': 'rich_hacker@test.com'}\n\n=== ТЕСТ 4: Помилка валідації пошти та довжини ID ===\n❌ Помилка валідації вхідних даних Webhook:\n   Поле: [data -> transaction_id] | Помилка: String should have at least 10 characters | Отримано: tx_short\n   Поле: [data -> customer_email] | Помилка: value is not a valid email address: The email address is not valid. It must have exactly one @-sign. | Отримано: not-an-email\n",[3403,14361,14359],{"__ignoreMap":3495},[3452,14363],{},[3389,14365,14367],{"id":14366},"практичні-завдання","Практичні завдання",[3394,14369,14370,14371,14373],{},"Для закріплення матеріалу виконайте наступні завдання. Збережіть ваші рішення у окремі файли та перевірте правильність роботи за допомогою ",[3403,14372,3467],{}," та запуску коду.",[3474,14375,14377],{"id":14376},"рівень-1-базовий-створення-моделі-користувача","Рівень 1 (Базовий): Створення моделі користувача",[3394,14379,14380,14381,14384],{},"Створіть Pydantic-модель ",[3403,14382,14383],{},"UserCreate",", яка містить:",[4697,14386,14387,14394,14399],{},[3430,14388,14389,14391,14392,4744],{},[3403,14390,11211],{},": обов'язковий email (використовуйте ",[3403,14393,8256],{},[3430,14395,14396,14398],{},[3403,14397,4061],{},": ціле число від 18 до 100 років.",[3430,14400,14401,14404,14405,14408,14409,4744],{},[3403,14402,14403],{},"created_at",": дата реєстрації (об'єкт ",[3403,14406,14407],{},"datetime","), яка не повинна бути в майбутньому (реалізуйте через ",[3403,14410,8734],{},[3474,14412,14414],{"id":14413},"рівень-2-середній-вкладені-моделі-та-бізнес-правила","Рівень 2 (Середній): Вкладені моделі та бізнес-правила",[3394,14416,14417],{},"Спроектуйте моделі для замовлення інтернет-магазину:",[4697,14419,14420,14436,14448],{},[3430,14421,14422,14425,14426,14428,14429,14432,14433,14435],{},[3403,14423,14424],{},"Product",": має ",[3403,14427,6907],{}," (int), ",[3403,14430,14431],{},"name"," (str) та ",[3403,14434,7806],{}," (float, яка має бути строго більшою за 0).",[3430,14437,14438,14441,14442,4140,14444,14447],{},[3403,14439,14440],{},"OrderItem",": містить об'єкт ",[3403,14443,14424],{},[3403,14445,14446],{},"quantity"," (int, строго більше 0).",[3430,14449,14450,14453,14454,14456,14457,14459,14460,14463,14464,14467,14468,14470,14471,14473,14474,4744],{},[3403,14451,14452],{},"Order",": містить список ",[3403,14455,10172],{}," (список ",[3403,14458,14440],{},") та ",[3403,14461,14462],{},"total_price"," (float).\nДодайте ",[3403,14465,14466],{},"@model_validator(mode=\"after\")"," для класу ",[3403,14469,14452],{},", який перевіряє, що значення ",[3403,14472,14462],{}," точно збігається із сумою цін усіх товарів, помножених на їхню кількість (",[3403,14475,14476],{},"price * quantity",[3474,14478,14480],{"id":14479},"рівень-3-професійний-generic-валідатор-та-annotated","Рівень 3 (Професійний): Generic-валідатор та Annotated",[3394,14482,14483,14484,14487,14488,14490,14491,14493,14494,3840,14497,14500,14501,14504,14505,14507,14508,3418],{},"Напишіть кастомний валідатор для конвертації валют.\nСтворіть перевикористовуваний тип ",[3403,14485,14486],{},"PriceUSD",", який є анотацією для ",[3403,14489,7510],{},". За допомогою ",[3403,14492,9546],{}," перевірте, чи є вхідне значення рядком типу ",[3403,14495,14496],{},"\"$100.50\"",[3403,14498,14499],{},"\"100.50$\""," чи числом. Валідатор має автоматично очистити символ ",[3403,14502,14503],{},"$"," і повернути чисте значення типу ",[3403,14506,7510],{},". Якщо конвертація неможлива — викиньте ",[3403,14509,14510],{},"ValueError",[14512,14513,14514],"style",{},"html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}",{"title":3495,"searchDepth":3519,"depth":3519,"links":14516},[14517,14518,14532,14554,14563],{"id":3391,"depth":3519,"text":3392},{"id":3456,"depth":3519,"text":3457,"children":14519},[14520,14521,14522,14523,14524,14525,14526,14528,14530],{"id":3476,"depth":3526,"text":3477},{"id":3735,"depth":3526,"text":3736},{"id":4029,"depth":3526,"text":4030},{"id":4306,"depth":3526,"text":4307},{"id":4625,"depth":3526,"text":4626},{"id":5267,"depth":3526,"text":5268},{"id":5822,"depth":3526,"text":14527},"Анотування метаданих типу: Annotated[T, metadata] (PEP 593)",{"id":6055,"depth":3526,"text":14529},"Звуження типів та перевантаження: TypeGuard, TypeIs та overload",{"id":6617,"depth":3526,"text":14531},"Налаштування статичного аналізу: mypy у CI\u002FCD",{"id":6782,"depth":3519,"text":6783,"children":14533},[14534,14535,14536,14538,14540,14541,14542,14544,14545,14547,14548,14550,14552,14553],{"id":6796,"depth":3526,"text":6797},{"id":7177,"depth":3526,"text":7178},{"id":7359,"depth":3526,"text":14537},"BaseModel під капотом: Метакласи та Дескриптори",{"id":7416,"depth":3526,"text":14539},"Анатомія Field(): тонке налаштування полів",{"id":8245,"depth":3526,"text":8246},{"id":8721,"depth":3526,"text":8722},{"id":10026,"depth":3526,"text":14543},"Анатомія помилки: ValidationError",{"id":10205,"depth":3526,"text":10206},{"id":10765,"depth":3526,"text":14546},"Валідація без створення моделей: TypeAdapter",{"id":11027,"depth":3526,"text":11028},{"id":11370,"depth":3526,"text":14549},"Налаштування моделей: ConfigDict",{"id":11716,"depth":3526,"text":14551},"Керування конфігурацією застосунку: Pydantic Settings",{"id":12114,"depth":3526,"text":12115},{"id":12159,"depth":3526,"text":12160},{"id":12530,"depth":3519,"text":12531,"children":14555},[14556,14558,14560,14562],{"id":12548,"depth":3526,"text":14557},"Крок 1: Створення файлу конфігурації .env та config.py",{"id":12750,"depth":3526,"text":14559},"Крок 2: Опис моделей платіжних подій (schemas.py)",{"id":13154,"depth":3526,"text":14561},"Крок 3: Точка входу та логіка обробки подій (main.py)",{"id":14170,"depth":3526,"text":14171},{"id":14366,"depth":3519,"text":14367,"children":14564},[14565,14566,14567],{"id":14376,"depth":3526,"text":14377},{"id":14413,"depth":3526,"text":14414},{"id":14479,"depth":3526,"text":14480},"Дослідження статичної типізації в сучасній екосистемі Python, пристрій Pydantic v2 та перехід від інспекції типів до автоматичної валідації даних.","md",null,{},{"title":2614,"description":14568},"nCDaEXkA67mZIL7sFUSFu09KKGoXIh_Tb9TTIUge-FU",[14575,14577],{"title":2605,"path":2606,"stem":2607,"description":14576,"children":-1},"Вичерпний розбір asyncio у Python — від моделі event loop і корутин до Task, Future, примітивів синхронізації, таймаутів та інтеграції з синхронним кодом. Практичні патерни для побудови масштабованих асинхронних програм.",{"title":2618,"path":2619,"stem":2620,"description":14578,"children":-1},"Детальний розбір специфікацій WSGI та ASGI, їхньої архітектури, відмінностей та порівняння з вебсервером Kestrel в ASP.NET Core",1783248227206]