{"id":490239,"date":"2026-08-08T09:46:13","date_gmt":"2026-08-08T09:46:13","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=490239"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=490239","title":{"rendered":"FlatAdapter \u0434\u043b\u044f \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u041a\u0430\u043a \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0439 JSON \u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u0411\u0414 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e pydantic-\u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0439 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 Python-\u043a\u043b\u0430\u0441\u0441\u043e\u0432<\/h2>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430<\/h3>\n<p>\u041d\u0430 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0443 \u043c\u0435\u043d\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0442\u0438\u043f\u0438\u0447\u043d\u0430\u044f ETL-\u0437\u0430\u0434\u0430\u0447\u0430: \u0438\u0437 \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0433\u043e JSON \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u044c \u0438\u0445 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<p>\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u0442\u0430\u043a:<\/p>\n<pre><code class=\"json\">{  \"order_id\": \"1001\",  \"customer\": {    \"id\": \"42\",    \"name\": \"Alice\"  },  \"items\": [    {      \"product\": {        \"id\": \"10\",        \"title\": \"Keyboard\"      },      \"quantity\": \"2\"    },    {      \"product\": {        \"id\": \"20\",        \"title\": \"Mouse\"      },      \"quantity\": \"1\"    }  ]}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043b\u043e\u0441\u043a\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438:<\/p>\n<pre><code class=\"python\">[    {        \"order_id\": 1001,        \"customer_name\": \"Alice\",        \"product_id\": 10,        \"quantity\": 2,    },    {        \"order_id\": 1001,        \"customer_name\": \"Alice\",        \"product_id\": 20,        \"quantity\": 1,    },]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0443\u0436\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432 staging-\u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435 \u0432 ETL-\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435.<\/p>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440<\/h3>\n<p>\u041f\u0435\u0440\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e\u0431\u044b\u0447\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0437\u0430 \u0432\u0435\u0447\u0435\u0440. \u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435:<\/p>\n<ul>\n<li>\n<p>\u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0432\u043e\u0439 \u043f\u0443\u0442\u044c \u0432 JSON;<\/p>\n<\/li>\n<li>\n<p>\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a <code>int<\/code>, <code>float<\/code>, <code>bool<\/code>, <code>date<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0447\u0430\u0441\u0442\u044c \u043f\u043e\u043b\u0435\u0439 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f;<\/p>\n<\/li>\n<li>\n<p>\u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0437\u043c\u043d\u043e\u0436\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0434\u0432\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0440\u043e\u0434\u0438\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u0438\u043c\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043f\u043e\u043b\u0435\u043c;<\/p>\n<\/li>\n<li>\n<p>\u0433\u043b\u0443\u0431\u043e\u043a\u0430\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u043d\u0435\u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u044b \u0441\u0442\u0440\u043e\u043a.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u0434 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c <code>if<\/code>, \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0438 \u0440\u0443\u0447\u043d\u044b\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439. \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e, \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 \u0441\u0442\u0438\u043b\u0435 <code>pydantic.BaseModel<\/code>, \u043d\u043e \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0449\u0451 \u043e\u0434\u043d\u0443 \u043c\u043e\u0434\u0435\u043b\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u043d\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043a ORM.<\/p>\n<p>\u0422\u0430\u043a \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0438\u0434\u0435\u044f <code>FlatAdapter<\/code>.<\/p>\n<h3>\u0418\u0435\u0440\u0430\u0440\u0445\u0438\u044f adapters<\/h3>\n<p>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430:<\/p>\n<pre><code class=\"bash\">pip install flat-adapter<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 Python-\u043a\u043b\u0430\u0441\u0441\u044b:<\/p>\n<pre><code class=\"python\">from flat_adapter import FlatAdapterclass OrderItemAdapter(FlatAdapter):    product_id: int    quantity: intclass OrderAdapter(FlatAdapter):    order_id: int    customer_name: str    items: list[OrderItemAdapter]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0437\u0430\u0434\u0430\u044e\u0442 \u0442\u0438\u043f\u044b \u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438. \u0412\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 <code>FlatAdapter<\/code> \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 mapping, \u0430 <code>list[FlatAdapter]<\/code> \u2014 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0443\u044e\u0441\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a.<\/p>\n<h3>\u041f\u0443\u0442\u0438 \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f<\/h3>\n<p>\u0414\u043b\u044f \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <code>Field<\/code>. \u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0441 <code>mypy --strict<\/code> \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c metadata \u0447\u0435\u0440\u0435\u0437 <code>typing.Annotated<\/code>:<\/p>\n<pre><code class=\"python\">from typing import Annotatedfrom flat_adapter import Field, FlatAdapterclass OrderItemAdapter(FlatAdapter):    product_id: Annotated[int, Field(source=\"product.id\")]    product_title: Annotated[str, Field(source=\"product.title\")]    quantity: intclass OrderAdapter(FlatAdapter):    order_id: int    customer_name: Annotated[str, Field(source=\"customer.name\")]    items: list[OrderItemAdapter]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 payload \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e adapter:<\/p>\n<pre><code class=\"python\">payload = {    \"order_id\": \"1001\",    \"customer\": {\"name\": \"Alice\"},    \"items\": [        {            \"product\": {\"id\": \"10\", \"title\": \"Keyboard\"},            \"quantity\": \"2\",        },        {            \"product\": {\"id\": \"20\", \"title\": \"Mouse\"},            \"quantity\": \"1\",        },    ],}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e mapping \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">rows = OrderAdapter.adapt(payload)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <code>\"1001\"<\/code>, <code>\"10\"<\/code> \u0438 <code>\"2\"<\/code> \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f\u043c.<\/p>\n<p>\u0423 \u043f\u043e\u043b\u044f \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438:<\/p>\n<pre><code class=\"python\">class CustomerAdapter(FlatAdapter):    name: Annotated[        str,        Field(            source=\"customer.name\",            default=\"Unknown\",            prepare_data_func=lambda value: str(value).strip(),        ),    ]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Legacy-\u0432\u0430\u0440\u0438\u0430\u043d\u0442 <code>name: str = Field(...)<\/code> \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043d\u043e <code>Annotated<\/code> \u043b\u0443\u0447\u0448\u0435 \u0441\u043e\u0433\u043b\u0430\u0441\u0443\u0435\u0442\u0441\u044f \u0441\u043e \u0441\u0442\u0440\u043e\u0433\u043e\u0439 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0435\u0439.<\/p>\n<h3>\u041a\u0430\u043a \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c<\/h3>\n<p><code>FlatAdapter<\/code> \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 repository \u0438\u043b\u0438 ORM-\u0441\u043b\u043e\u0439. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b:<\/p>\n<pre><code class=\"python\">class OrderTableAdapter(FlatAdapter):    order_id: int    customer_id: Annotated[int, Field(source=\"customer.id\")]    customer_name: Annotated[str, Field(source=\"customer.name\")]class OrderItemsTableAdapter(FlatAdapter):    order_id: int    items: list[OrderItemAdapter]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c <code>OrderItemsTableAdapter<\/code> \u2014 \u0432\u0435\u0440\u0445\u043d\u0435\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 adapter \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b items. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u043e <code>payload[\"items\"]<\/code> \u043d\u0435 \u043d\u0443\u0436\u0435\u043d:<\/p>\n<pre><code class=\"python\">order_rows = OrderTableAdapter.adapt(payload)item_rows = OrderItemsTableAdapter.adapt(payload)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e <code>order_row<\/code> \u0438 <code>item_rows<\/code> \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432 \u0411\u0414. \u0422\u0430\u043a\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u0430\u0436\u043d\u043e: \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043d\u043e \u043d\u0435 \u0440\u0435\u0448\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0439, retry \u0438 bulk insert.<\/p>\n<h3>\u0427\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438<\/h3>\n<p>\u0415\u0441\u043b\u0438 \u0432 adapter \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u0435\u043a\u0430\u0440\u0442\u043e\u0432\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"python\">class TagAdapter(FlatAdapter):    tag: strclass RegionAdapter(FlatAdapter):    region: strclass ProductAdapter(FlatAdapter):    tags: list[TagAdapter]    regions: list[RegionAdapter]<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e payload:<\/p>\n<pre><code class=\"python\">payload = {    \"tags\": [{\"tag\": \"python\"}, {\"tag\": \"etl\"}],    \"regions\": [        {\"region\": \"eu\"},        {\"region\": \"us\"},        {\"region\": \"apac\"},    ],}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0432\u0430 \u0442\u0435\u0433\u0430 \u0438 \u0442\u0440\u0438 \u0440\u0435\u0433\u0438\u043e\u043d\u0430 \u0434\u0430\u0434\u0443\u0442 \u0448\u0435\u0441\u0442\u044c \u0441\u0442\u0440\u043e\u043a. \u041f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f, \u0430 \u043f\u0443\u0441\u0442\u043e\u0439 \u0438\u043b\u0438 <code>None<\/code> \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 <code>None<\/code> \u0432 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u0445.<\/p>\n<p>\u0422\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f ETL, \u043d\u043e \u0443 \u043d\u0435\u0433\u043e \u0435\u0441\u0442\u044c \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u0440\u0438\u0441\u043a. \u0415\u0441\u043b\u0438 \u0434\u043b\u0438\u043d\u044b \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0440\u0430\u0432\u043d\u044b <code>L1<\/code>, <code>L2<\/code>, \u2026, \u0447\u0438\u0441\u043b\u043e \u0441\u0442\u0440\u043e\u043a \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u0442\u0438 \u043a\u0430\u043a:<\/p>\n<pre><code>R = product(max(1, len(Li)))<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u0430\u0449\u0438\u0442\u043d\u044b\u0439 \u043b\u0438\u043c\u0438\u0442:<\/p>\n<pre><code class=\"python\">rows = OrderAdapter.adapt(payload, max_rows=10_000)<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438 \u043f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u0438 \u043b\u0438\u043c\u0438\u0442\u0430 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 <code>RowLimitExceeded<\/code>, \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.<\/p>\n<h3>\u0427\u0442\u043e \u0441 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e<\/h3>\n<p>\u0421\u0435\u043c\u044c-\u0434\u0435\u0441\u044f\u0442\u044c \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0441\u0430\u043c\u0438 \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 \u0434\u043b\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e\u0433\u043e Python-\u043e\u0431\u0445\u043e\u0434\u0430. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u043e\u0440 \u2014 \u043d\u0435 \u0433\u043b\u0443\u0431\u0438\u043d\u0430, \u0430 \u0447\u0438\u0441\u043b\u043e \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0439 \u0438 \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0440\u043e\u043a\u0430\u0445.<\/p>\n<p>\u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u0435\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 benchmark:<\/p>\n<pre><code class=\"bash\">uv run python benchmarks\/flatten_benchmark.py<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u043d \u0438\u0437\u043c\u0435\u0440\u044f\u0435\u0442:<\/p>\n<ul>\n<li>\n<p>\u0441\u043c\u0435\u0448\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0433\u043b\u0443\u0431\u0438\u043d\u043e\u0439 10 \u0443\u0440\u043e\u0432\u043d\u0435\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437 1000 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 <code>10 \u00d7 10 \u00d7 10<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u0441 20 \u043f\u043b\u043e\u0441\u043a\u0438\u043c\u0438 \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u043c\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>adapt()<\/code> \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>list[dict[str, object]]<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u043f\u0430\u043c\u044f\u0442\u0438. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>iter_adapt()<\/code>: \u043e\u043d \u0432\u044b\u0434\u0430\u0451\u0442 \u0441\u0442\u0440\u043e\u043a\u0438 \u043b\u0435\u043d\u0438\u0432\u043e \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c \u0441 <code>max_rows<\/code>, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0438 \u043d\u0435 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u043d\u0430\u0431\u043e\u0440 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438.<\/p>\n<h3>\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u0438\u0445\u043e\u0439 \u043f\u043e\u0440\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/h3>\n<p>\u0410\u0434\u0430\u043f\u0442\u0435\u0440 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043c\u043e\u043b\u0447\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u043b\u043e\u043d\u043a\u0443 \u0438\u043b\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u044b \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438:<\/p>\n<ul>\n<li>\n<p><code>MissingFieldError<\/code> \u2014 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442;<\/p>\n<\/li>\n<li>\n<p><code>ConversionError<\/code> \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u0442\u0438\u043f\u0443;<\/p>\n<\/li>\n<li>\n<p><code>InvalidShapeError<\/code> \u2014 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u043d\u0435\u0432\u0435\u0440\u043d\u0443\u044e \u0444\u043e\u0440\u043c\u0443;<\/p>\n<\/li>\n<li>\n<p><code>DuplicateFieldError<\/code> \u2014 \u0434\u0432\u0430 nested adapter \u043f\u043e\u0440\u043e\u0434\u0438\u043b\u0438 \u043e\u0434\u043d\u0443 \u043a\u043e\u043b\u043e\u043d\u043a\u0443;<\/p>\n<\/li>\n<li>\n<p><code>RowLimitExceeded<\/code> \u2014 expansion \u043f\u0440\u0435\u0432\u044b\u0441\u0438\u043b \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043b\u0438\u043c\u0438\u0442.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442<\/h3>\n<p>\u042d\u0442\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u043d\u0430 Pydantic, ORM \u0438 ETL-\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435:<\/p>\n<ul>\n<li>\n<p>\u043d\u0435\u0442 HTTP API;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u0442 \u0441\u0445\u0435\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u0430\u0432\u0438\u043b;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 dataclass \u0438 Pydantic-\u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0432 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438.<\/p>\n<\/li>\n<\/ul>\n<p>\u0417\u0430\u0434\u0430\u0447\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0443\u0436\u0435 \u0438 \u043f\u0440\u043e\u0449\u0435: \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c nested mapping \u0432 \u0441\u0442\u0440\u043e\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u0441\u043b\u043e\u044e.<\/p>\n<h3>\u0418\u0442\u043e\u0433\u0438<\/h3>\n<p>\u0418\u0434\u0435\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 adapters \u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0433\u0440\u0430\u043d\u0438\u0446\u0435\u0439 \u043c\u0435\u0436\u0434\u0443 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 persistence-\u0441\u043b\u043e\u0435\u043c:<\/p>\n<ol>\n<li>\n<p>\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 JSON \u0432\u0438\u0434\u043d\u0430 \u0432 \u043a\u043e\u0434\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0442\u0438\u043f\u044b \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0440\u044f\u0434\u043e\u043c \u0441 \u043f\u043e\u043b\u044f\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e;<\/p>\n<\/li>\n<li>\n<p>\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u0434\u0435\u043a\u0430\u0440\u0442\u043e\u0432\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c;<\/p>\n<\/li>\n<li>\n<p>\u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043d\u0435 \u0441\u0432\u044f\u0437\u0430\u043d \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0411\u0414.<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043d\u0430 PyPI:<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/pypi.org\/project\/flat-adapter\/\" rel=\"noopener noreferrer nofollow\">PyPI<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/groshevpavel\/flat_adapter\" rel=\"noopener noreferrer nofollow\">GitHub<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/groshevpavel\/flat_adapter\/blob\/master\/docs\/README.en.md\" rel=\"noopener noreferrer nofollow\">English documentation<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0431\u044b\u043b\u0430 \u043f\u043e\u0445\u043e\u0436\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441 \u0432\u0430\u0448\u0438\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438: \u0440\u0443\u0447\u043d\u044b\u043c\u0438 flatten-\u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, Pydantic-\u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 \u0438\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u043c\u0438 ETL \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<\/p>\n<p><strong>\u0422\u0435\u0433\u0438:<\/strong> Python, ETL, JSON, \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, PyPI, data engineering<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1068172\/\">https:\/\/habr.com\/ru\/articles\/1068172\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u041a\u0430\u043a \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0433\u043b\u0443\u0431\u043e\u043a\u0438\u0439 JSON \u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u0411\u0414 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e pydantic-\u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0439 \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 Python-\u043a\u043b\u0430\u0441\u0441\u043e\u0432\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u041d\u0430 \u043e\u0434\u043d\u043e\u043c \u0438\u0437 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432 \u0443 \u043c\u0435\u043d\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0442\u0438\u043f\u0438\u0447\u043d\u0430\u044f ETL-\u0437\u0430\u0434\u0430\u0447\u0430: \u0438\u0437 \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0433\u043e JSON \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u044c \u0438\u0445 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043c \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u0442\u0430\u043a:{  &#171;order_id&#187;: &#171;1001&#187;,  &#171;customer&#187;: {    &#171;id&#187;: &#171;42&#187;,    &#171;name&#187;: &#171;Alice&#187;  },  &#171;items&#187;: [    {      &#171;product&#187;: {        &#171;id&#187;: &#171;10&#187;,        &#171;title&#187;: &#171;Keyboard&#187;      },      &#171;quantity&#187;: &#171;2&#187;    },    {      &#171;product&#187;: {        &#171;id&#187;: &#171;20&#187;,        &#171;title&#187;: &#171;Mouse&#187;      },      &#171;quantity&#187;: &#171;1&#187;    }  ]}\u041d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043b\u043e\u0441\u043a\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438:[    {        &#171;order_id&#187;: 1001,        &#171;customer_name&#187;: &#171;Alice&#187;,        &#171;product_id&#187;: 10,        &#171;quantity&#187;: 2,    },    {        &#171;order_id&#187;: 1001,        &#171;customer_name&#187;: &#171;Alice&#187;,        &#171;product_id&#187;: 20,        &#171;quantity&#187;: 1,    },]\u0422\u0430\u043a\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0443\u0436\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432 staging-\u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438\u043b\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0430\u043b\u044c\u0448\u0435 \u0432 ETL-\u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435.\u041f\u043e\u0447\u0435\u043c\u0443 \u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440\u041f\u0435\u0440\u0432\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e \u0442\u0430\u043a\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e\u0431\u044b\u0447\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0437\u0430 \u0432\u0435\u0447\u0435\u0440. \u0421\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435:\u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0432\u043e\u0439 \u043f\u0443\u0442\u044c \u0432 JSON;\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a int, float, bool, date;\u0447\u0430\u0441\u0442\u044c \u043f\u043e\u043b\u0435\u0439 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f;\u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0440\u0430\u0437\u043c\u043d\u043e\u0436\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438;\u0434\u0432\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043c\u043e\u0433\u0443\u0442 \u043f\u043e\u0440\u043e\u0434\u0438\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e\u0435 \u0438\u043c\u044f \u043a\u043e\u043b\u043e\u043d\u043a\u0438;\u043e\u0448\u0438\u0431\u043a\u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043f\u043e\u043b\u0435\u043c;\u0433\u043b\u0443\u0431\u043e\u043a\u0430\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u044f \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u043d\u0435\u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u044b \u0441\u0442\u0440\u043e\u043a.\u0412 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u0434 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c if, \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0438 \u0440\u0443\u0447\u043d\u044b\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439. \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e, \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 \u0441\u0442\u0438\u043b\u0435 pydantic.BaseModel, \u043d\u043e \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0449\u0451 \u043e\u0434\u043d\u0443 \u043c\u043e\u0434\u0435\u043b\u044c \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u043d\u0435 \u043f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043a ORM.\u0422\u0430\u043a \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u0438\u0434\u0435\u044f FlatAdapter.\u0418\u0435\u0440\u0430\u0440\u0445\u0438\u044f adapters\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430:pip install flat-adapter\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 Python-\u043a\u043b\u0430\u0441\u0441\u044b:from flat_adapter import FlatAdapterclass OrderItemAdapter(FlatAdapter):    product_id: int    quantity: intclass OrderAdapter(FlatAdapter):    order_id: int    customer_name: str    items: list[OrderItemAdapter]\u0410\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438 \u0437\u0430\u0434\u0430\u044e\u0442 \u0442\u0438\u043f\u044b \u0438 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0442 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u043a\u043e\u043b\u043e\u043d\u043a\u0438. \u0412\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 FlatAdapter \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 mapping, \u0430 list[FlatAdapter] \u2014 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u044e\u0449\u0443\u044e\u0441\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0434\u043e\u043b\u0436\u043d\u0430 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a.\u041f\u0443\u0442\u0438 \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f\u0414\u043b\u044f \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f Field. \u0412 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445 \u0441 mypy &#8212;strict \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c metadata \u0447\u0435\u0440\u0435\u0437 typing.Annotated:from typing import Annotatedfrom flat_adapter import Field, FlatAdapterclass OrderItemAdapter(FlatAdapter):    product_id: Annotated[int, Field(source=&#187;product.id&#187;)]    product_title: Annotated[str, Field(source=&#187;product.title&#187;)]    quantity: intclass OrderAdapter(FlatAdapter):    order_id: int    customer_name: Annotated[str, Field(source=&#187;customer.name&#187;)]    items: list[OrderItemAdapter]\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 payload \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e adapter:payload = {    &#171;order_id&#187;: &#171;1001&#187;,    &#171;customer&#187;: {&#171;name&#187;: &#171;Alice&#187;},    &#171;items&#187;: [        {            &#171;product&#187;: {&#171;id&#187;: &#171;10&#187;, &#171;title&#187;: &#171;Keyboard&#187;},            &#171;quantity&#187;: &#171;2&#187;,        },        {            &#171;product&#187;: {&#171;id&#187;: &#171;20&#187;, &#171;title&#187;: &#171;Mouse&#187;},            &#171;quantity&#187;: &#171;1&#187;,        },    ],}\u0422\u0435\u043f\u0435\u0440\u044c \u0430\u0434\u0430\u043f\u0442\u0430\u0446\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e mapping \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:rows = OrderAdapter.adapt(payload)\u0421\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f &#171;1001&#187;, &#171;10&#187; \u0438 &#171;2&#187; \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f\u043c.\u0423 \u043f\u043e\u043b\u044f \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438:class CustomerAdapter(FlatAdapter):    name: Annotated[        str,        Field(            source=&#187;customer.name&#187;,            default=&#187;Unknown&#187;,            prepare_data_func=lambda value: str(value).strip(),        ),    ]Legacy-\u0432\u0430\u0440\u0438\u0430\u043d\u0442 name: str = Field(&#8230;) \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u043d\u043e Annotated \u043b\u0443\u0447\u0448\u0435 \u0441\u043e\u0433\u043b\u0430\u0441\u0443\u0435\u0442\u0441\u044f \u0441\u043e \u0441\u0442\u0440\u043e\u0433\u043e\u0439 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0435\u0439.\u041a\u0430\u043a \u0440\u0430\u0437\u043b\u043e\u0436\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u043cFlatAdapter \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u043d\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 repository \u0438\u043b\u0438 ORM-\u0441\u043b\u043e\u0439. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b:class OrderTableAdapter(FlatAdapter):    order_id: int    customer_id: Annotated[int, Field(source=&#187;customer.id&#187;)]    customer_name: Annotated[str, Field(source=&#187;customer.name&#187;)]class OrderItemsTableAdapter(FlatAdapter):    order_id: int    items: list[OrderItemAdapter]\u0417\u0434\u0435\u0441\u044c OrderItemsTableAdapter \u2014 \u0432\u0435\u0440\u0445\u043d\u0435\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u044b\u0439 adapter \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b items. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u043f\u043e payload[&#171;items&#187;] \u043d\u0435 \u043d\u0443\u0436\u0435\u043d:order_rows = OrderTableAdapter.adapt(payload)item_rows = OrderItemsTableAdapter.adapt(payload)\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e order_row \u0438 item_rows \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u043b\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432 \u0411\u0414. \u0422\u0430\u043a\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u0430\u0436\u043d\u043e: \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043d\u043e \u043d\u0435 \u0440\u0435\u0448\u0430\u0435\u0442 \u0437\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u0439, retry \u0438 bulk insert.\u0427\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u0430\u043c\u0438\u0415\u0441\u043b\u0438 \u0432 adapter \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0434\u0435\u043a\u0430\u0440\u0442\u043e\u0432\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435:class TagAdapter(FlatAdapter):    tag: strclass RegionAdapter(FlatAdapter):    region: strclass ProductAdapter(FlatAdapter):    tags: list[TagAdapter]    regions: list[RegionAdapter]\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e payload:payload = {    &#171;tags&#187;: [{&#171;tag&#187;: &#171;python&#187;}, {&#171;tag&#187;: &#171;etl&#187;}],    &#171;regions&#187;: [        {&#171;region&#187;: &#171;eu&#187;},        {&#171;region&#187;: &#171;us&#187;},        {&#171;region&#187;: &#171;apac&#187;},    ],}\u0414\u0432\u0430 \u0442\u0435\u0433\u0430 \u0438 \u0442\u0440\u0438 \u0440\u0435\u0433\u0438\u043e\u043d\u0430 \u0434\u0430\u0434\u0443\u0442 \u0448\u0435\u0441\u0442\u044c \u0441\u0442\u0440\u043e\u043a. \u041f\u043e\u0440\u044f\u0434\u043e\u043a \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f, \u0430 \u043f\u0443\u0441\u0442\u043e\u0439 \u0438\u043b\u0438 None \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0441 None \u0432 \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u0445 \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u0445.\u0422\u0430\u043a\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f ETL, \u043d\u043e \u0443 \u043d\u0435\u0433\u043e \u0435\u0441\u0442\u044c \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0439 \u0440\u0438\u0441\u043a. \u0415\u0441\u043b\u0438 \u0434\u043b\u0438\u043d\u044b \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u0440\u0430\u0432\u043d\u044b L1, L2, \u2026, \u0447\u0438\u0441\u043b\u043e \u0441\u0442\u0440\u043e\u043a \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0441\u0442\u0438 \u043a\u0430\u043a:R = product(max(1, len(Li)))\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0432\u043d\u0435\u0448\u043d\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u0430\u0449\u0438\u0442\u043d\u044b\u0439 \u043b\u0438\u043c\u0438\u0442:rows = OrderAdapter.adapt(payload, max_rows=10_000)\u041f\u0440\u0438 \u043f\u0440\u0435\u0432\u044b\u0448\u0435\u043d\u0438\u0438 \u043b\u0438\u043c\u0438\u0442\u0430 \u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u0432\u044b\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 RowLimitExceeded, \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.\u0427\u0442\u043e \u0441 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e\u0421\u0435\u043c\u044c-\u0434\u0435\u0441\u044f\u0442\u044c \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0441\u0442\u0438 \u0441\u0430\u043c\u0438 \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 \u0434\u043b\u044f \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u043e\u0433\u043e Python-\u043e\u0431\u0445\u043e\u0434\u0430. \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u0444\u0430\u043a\u0442\u043e\u0440 \u2014 \u043d\u0435 \u0433\u043b\u0443\u0431\u0438\u043d\u0430, \u0430 \u0447\u0438\u0441\u043b\u043e \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u0439 \u0438 \u043a\u043e\u043b\u043e\u043d\u043e\u043a \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0440\u043e\u043a\u0430\u0445.\u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u0435\u0441\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 benchmark:uv run python benchmarks\/flatten_benchmark.py\u041e\u043d \u0438\u0437\u043c\u0435\u0440\u044f\u0435\u0442:\u0441\u043c\u0435\u0448\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0433\u043b\u0443\u0431\u0438\u043d\u043e\u0439 10 \u0443\u0440\u043e\u0432\u043d\u0435\u0439;\u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437 1000 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432;\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 10 \u00d7 10 \u00d7 10;\u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u0441 20 \u043f\u043b\u043e\u0441\u043a\u0438\u043c\u0438 \u043a\u043e\u043b\u043e\u043d\u043a\u0430\u043c\u0438.\u041c\u0435\u0442\u043e\u0434 adapt() \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 list[dict[str, object]], \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u043f\u0430\u043c\u044f\u0442\u0438. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c iter_adapt(): \u043e\u043d \u0432\u044b\u0434\u0430\u0451\u0442 \u0441\u0442\u0440\u043e\u043a\u0438 \u043b\u0435\u043d\u0438\u0432\u043e \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c \u0441 max_rows, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0438 \u043d\u0435 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u043d\u0430\u0431\u043e\u0440 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438.\u041e\u0448\u0438\u0431\u043a\u0438 \u0432\u043c\u0435\u0441\u0442\u043e \u0442\u0438\u0445\u043e\u0439 \u043f\u043e\u0440\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\u0410\u0434\u0430\u043f\u0442\u0435\u0440 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u043c\u043e\u043b\u0447\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u043b\u043e\u043d\u043a\u0443 \u0438\u043b\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u044b \u0442\u0438\u043f\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438:MissingFieldError \u2014 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442;ConversionError \u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u0442\u0438\u043f\u0443;InvalidShapeError \u2014 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u0430\u044f \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u043d\u0435\u0432\u0435\u0440\u043d\u0443\u044e \u0444\u043e\u0440\u043c\u0443;DuplicateFieldError \u2014 \u0434\u0432\u0430 nested adapter \u043f\u043e\u0440\u043e\u0434\u0438\u043b\u0438 \u043e\u0434\u043d\u0443 \u043a\u043e\u043b\u043e\u043d\u043a\u0443;RowLimitExceeded \u2014 expansion \u043f\u0440\u0435\u0432\u044b\u0441\u0438\u043b \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u0439 \u043b\u0438\u043c\u0438\u0442.\u0427\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442\u042d\u0442\u043e \u043d\u0435 \u0437\u0430\u043c\u0435\u043d\u0430 Pydantic, ORM \u0438 ETL-\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435:\u043d\u0435\u0442 HTTP API;\u043d\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0411\u0414;\u043d\u0435\u0442 \u0441\u0445\u0435\u043c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0439;\u043d\u0435\u0442 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u0430\u0432\u0438\u043b;\u043d\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 dataclass \u0438 Pydantic-\u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u0432 \u043f\u0435\u0440\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438.\u0417\u0430\u0434\u0430\u0447\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0443\u0436\u0435 \u0438 \u043f\u0440\u043e\u0449\u0435: \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u043e \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c nested mapping \u0432 \u0441\u0442\u0440\u043e\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c\u0443 \u0441\u043b\u043e\u044e.\u0418\u0442\u043e\u0433\u0438\u0418\u0434\u0435\u044f \u0438\u0435\u0440\u0430\u0440\u0445\u0438\u0438 adapters \u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0433\u0440\u0430\u043d\u0438\u0446\u0435\u0439 \u043c\u0435\u0436\u0434\u0443 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 persistence-\u0441\u043b\u043e\u0435\u043c:\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 JSON \u0432\u0438\u0434\u043d\u0430 \u0432 \u043a\u043e\u0434\u0435;\u0442\u0438\u043f\u044b \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0440\u044f\u0434\u043e\u043c \u0441 \u043f\u043e\u043b\u044f\u043c\u0438;\u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0441\u043f\u0438\u0441\u043a\u0438 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u0435\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e;\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u0434\u0435\u043a\u0430\u0440\u0442\u043e\u0432\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c;\u0430\u0434\u0430\u043f\u0442\u0435\u0440 \u043d\u0435 \u0441\u0432\u044f\u0437\u0430\u043d \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0411\u0414.\u041f\u0440\u043e\u0435\u043a\u0442 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d \u043d\u0430 PyPI:PyPIGitHubEnglish documentation\u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0431\u044b\u043b\u0430 \u043f\u043e\u0445\u043e\u0436\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u2014 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u0441 \u0432\u0430\u0448\u0438\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u044f\u043c\u0438: \u0440\u0443\u0447\u043d\u044b\u043c\u0438 flatten-\u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, Pydantic-\u043c\u043e\u0434\u0435\u043b\u044f\u043c\u0438 \u0438\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u043c\u0438 ETL \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.\u0422\u0435\u0433\u0438: Python, ETL, JSON, \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, PyPI, data engineering\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 https:\/\/habr.com\/ru\/articles\/1068172\/<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-490239","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/490239","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=490239"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/490239\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=490239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=490239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=490239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}