استفاده از Mountaineer برای توسعه اپلیکیشن React با پایتون

نوشته شده توسط روزاریو دی کیارا✏️
Mountaineer چارچوبی برای ساخت اپلیکیشن های وب در پایتون و React به راحتی است. ایده Mountaineer این است که به توسعهدهنده اجازه دهد از دانش قبلی پایتون و TypeScript استفاده کند تا از هر زبان (و فریمورک) برای کاری که برای آن مناسب است استفاده کند.
ایده اصلی توسعه frontend به عنوان یک برنامه React مناسب و backend به عنوان چندین سرویس Python است: همه چیز در یک پروژه واحد، انواع سازگار بالا و پایین پشته، اتصال ساده دادهها و فراخوانی تابع، و ارائه سرور برای دسترسی بهتر.
در این مقاله، راهاندازی پروژه کوهنوردی را شرح میدهیم و نشان میدهیم که چگونه یک برنامه کاربردی ساده حاوی تمام مفاهیم اولیه توسعه دهیم. در پایان، شما قادر خواهید بود اجزای فرانت اند و باطن را به طور یکپارچه در یک پروژه ادغام کنید.
راه اندازی محیط توسعه کوهنورد
قبل از شروع، مهم است که یک محیط مناسب را تنظیم کنید. Mountaineer اساسا یک پروژه React را که یک برنامه Node است به محیط پایتون پیوند می دهد. بنابراین، می تواند در مورد نسخه های هر جزء در محیط بسیار حساس باشد.
در تنظیمات من، WSL نسخه کاملاً قدیمی اوبونتو (Ubuntu 20.04.6 LTS) را تحت ویندوز اجرا می کند، بنابراین مجبور شدم بسته های زیر را به روز کنم:
- زبان زنگ زده: به نسخه بزرگتر یا برابر با 1.77 به روز رسانی شد
- برو زبان: به روز رسانی به نسخه 1.22
- گره: به نسخه بزرگتر یا مساوی 20 به روز شد
- زبان پایتون: به روز رسانی به نسخه بزرگتر یا برابر با 3.11
زمانی که Mountaineer پایدار شود، این تنظیمات تغییر خواهند کرد. همچنین لازم به ذکر است که نویسنده در هنگام راه اندازی سیستم پاسخگو و حمایت کننده بود.
هنگامی که محیط شما تنظیم شد، می توانید با استفاده از دستور زیر یک برنامه boilerplate ایجاد کنید:
pipx run create-mountaineer-app
این امکانی است که برای ایجاد ساختار دایرکتوری (بسیار پیچیده) با تمام وابستگیهای این دو محیط موجود است: پروژه Python و پروژه Node.
در شکل زیر می توانید خروجی دستور را مشاهده کنید که بسته به نیازهای قبلی ممکن است متفاوت باشد:
$ pipx run create-mountaineer-app
? Project name [my-project]: microblog
? Author [Rosario De Chiara <rosdec@gmail.com>]
? Use poetry for dependency management? [Yes] Yes
? Create stub MVC files? [Yes] No
? Use Tailwind CSS? [Yes] No
? Add editor configuration? [vscode] vscode
Creating project...
Creating .gitignore
Creating docker-compose.yml
Creating README.md
Creating pyproject.toml
Creating .env
Creating microblog/app.py
Creating microblog/main.py
Creating microblog/cli.py
Creating microblog/config.py
Creating microblog/__init__.py
Creating microblog/views/package.json
No content detected in microblog/views/tailwind.config.js, skipping...
No content detected in microblog/views/postcss.config.js, skipping...
No content detected in microblog/views/__init__.py, skipping...
No content detected in microblog/views/app/main.css, skipping...
No content detected in microblog/views/app/home/page.tsx, skipping...
No content detected in microblog/views/app/detail/page.tsx, skipping...
No content detected in microblog/controllers/home.py, skipping...
No content detected in microblog/controllers/detail.py, skipping...
Creating microblog/controllers/__init__.py
No content detected in microblog/models/detail.py, skipping...
Creating microblog/models/__init__.py
Project created at /home/user/microblog
Creating virtualenv microblog-IiOnN0qh-py3.11 in /home/user/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies... (1.3s)
Package operations: 29 installs, 0 updates, 0 removals
[....INSTALLATION OF PYTHON PACKAGES....]
Writing lock file
Installing the current project: microblog (0.1.0)
Poetry venv created: /home/user/.cache/pypoetry/virtualenvs/microblog-IiOnN0qh-py3.11
added 129 packages, and audited 130 packages in 9s
34 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Environment created successfully
Creating .vscode/settings.json
Editor config created at /home/user/microblog
$
اگر دقت کنید، می توانید دو مرحله قابل تشخیص از نصب وابستگی ها را تشخیص دهید. توجه داشته باشید که چگونه create-mountaineer-app
نه تنها قادر به تولید داربست برنامه است بلکه می تواند آن را با یک نمونه پروژه MVC نیز پر کند.
برای این مقاله، ما از این تابع استفاده نخواهیم کرد، اما یک برنامه نمونه را به صورت دستی با این دستور پیاده سازی می کنیم:
$ poetry run runserver
شما می توانید وب سرور را راه اندازی کنید (به http://127.0.0.1:5006/ مراجعه کنید) و پس از اصلاح کد منبع، توسعه برنامه را با بارگذاری مجدد داغ آغاز کنید.
باید اعتراف کنم، اولین اجرا خیلی هیجان انگیز نیست – صفحه بارگذاری شده ای وجود ندارد و خروجی بسیار کسل کننده است:
قبل از شروع توسعه، بیایید به ساختار دایرکتوری که توسط اسکریپتی که اخیراً اجرا کردیم ایجاد شده است نگاه کنیم. از توضیحات، باید بتوانید بفهمید که بسته به کاری که قصد انجام آن را دارید کجا تمرکز کنید:
اضافه کردن کنترلر و نمای
ایجاد یک صفحه شامل راه اندازی یک کنترلر جدید برای تعریف داده هایی است که می توان آنها را به جلوی صفحه خود منتقل کرد.
بیایید یک کنترلر به نام ایجاد کنیم home.py
در کنترل کننده دایرکتوری؛ به عنوان یک کنترل کننده، روی باطن اجرا می شود و همانطور که احتمالاً انتظار دارید، یک اسکریپت پایتون است.
ایده کلی این است که یک نمونه از ControllerBase
کلاس و رندر متد را پیاده سازی کنید که بار داده خام را فراهم می کند که در رندر اولیه و در طول هر به روز رسانی عوارض جانبی به frontend ارسال می شود.
در بیشتر موارد، باید a را برگردانید RenderBase
به عنوان مثال (پایین را ببینید) اما اگر داده ای برای نمایش ندارید، می توانید برگردید None
مثل ما اینجا علاوه بر این، ما باید تنظیم کنیم url
پارامتری که شامل URL است که کنترلر در آن قابل دسترسی است و view_path
که دیدگاه را به این کنترل کننده مرتبط می کند.
این render()
تابع یک بلوک اصلی سازنده Mountaineer است و همه کنترلکنندهها باید یکی داشته باشند: تمام دادههایی را که frontend شما برای حل نمای خود به آن نیاز دارد را تعریف میکند:
from mountaineer import ControllerBase
class HomeController(ControllerBase):
url = "/"
view_path = "/app/home/page.tsx"
async def render( self ) -> None:
pass
مرحله بعدی ثبت کنترلر جدید در آن است app.py
:
from mountaineer.app import AppController
from mountaineer.js_compiler.postcss import PostCSSBundler
from mountaineer.render import LinkAttribute, Metadata
from intro_to_mountaineer.config import AppConfig
from intro_to_mountaineer.controllers.home import HomeController
controller = AppController(
config=AppConfig(), # type: ignore
)
controller.register(HomeController())
در این مرحله، اگر فایل خود را ذخیره کنید، سرور به طور خودکار بارگذاری مجدد می شود، کنترل کننده جدید را ثبت می کند اما از گم شدن شکایت می کند. page.tsx
، که نمای ما با کنترلر جدید مرتبط است.
البته این نما یک فایل TypeScript/React است:
import React from "react";
import { useServer } from "./_server/useServer";
const Home = () => {
const serverState = useServer();
return (
<div>
<h1>Home</h1>
<p>Hello, world!</p>
</div>
);
};
export default Home;
شما باید مسیر دایرکتوری را ایجاد کنید /app/home/
زیر /view/
جایی که پروژه React ما زندگی می کند و فایل بالا را با نام قرار دهید page.tsx
.
اگر همه چیز را در مکان های صحیح قرار دهید، مرورگر شما صفحه جدید ایجاد شده را بازخوانی کرده و نشان می دهد:
اگر در قرار دادن فایل مناسب در دایرکتوری مشکل دارید، این commit خاص را در مخزن بررسی کنید و ببینید برنامه چگونه به نظر می رسد.
اضافه کردن مدل
یکی از ایده های هوشمند Mountaineer این است که با پشتیبانی از پایگاه داده PostgresSQL برای پشتیبان گیری از داده های شما ارائه می شود. برای تسهیل استفاده از آن، همراه با فایل های موجود در پروژه خود، create_mountaineer_app
اسکریپت نیز یک را ایجاد می کند docker-compose.yml
فایل YAML برای راه اندازی سرور PostgresSQL در داکر شما. شما مجبور نیستید فرآیند ساخت جداول را انجام دهید. کوهنورد فقط کد شما را در قسمت بازرسی می کند /model
دایرکتوری برای درک اینکه چه اشیایی در پایگاه داده نیاز دارید.
اولین قدم ایجاد یک اسکریپت پایتون جدید است. در این مثال خواهد بود blogpost.py
در /model
فهرست راهنما:
from mountaineer.database import SQLModel, Field
from uuid import UUID, uuid4
from datetime import datetime
class BlogPost(SQLModel, table=True):
id: UUID = Field(default_factory=uuid4, primary_key=True)
text: str
data: str = datetime.now()
برای افزودن این فایل به پروژه، باید آن را در قسمت قرار دهید __init__.py
فایل پایتون:
from .blogpost import BlogPost
در این مرحله با دستورات زیر آماده ایجاد جدول در پایگاه داده هستیم. البته، شما باید یک docker
نصب در سیستم شما که در آن ظرف را نمونه سازی کنید:
docker-compose up -d
poetry run createdb
این createdb
اسکریپت را بررسی خواهد کرد /model
دایرکتوری و، برای هر شی که در فایل اولیه گنجانده شده است __init__.py
، جدولی با ستون هایی ایجاد می کند که با نام ها و انواع تعریف شده در آن مطابقت دارند blogpost.py
فایل (برای این مثال).
از روی کنجکاوی، اگر پایگاه داده PostgresSQL خود را بررسی کنید، می توانید ساختار آن را ببینید blogpost
جدول:
اکنون ما تمام عناصر را برای تکمیل توسعه برنامه در اختیار داریم. ابتدا، اجازه دهید قابلیت افزودن یک پست وبلاگ جدید را اضافه کنیم. برای انجام این کار، باید فیلدهای ورودی را به جلویی اضافه کنیم (در /views
دایرکتوری) و کنترلر را به روز کنید تا به درستی یک ردیف به آن اضافه شود blogpost
جدول (در /controllers
فهرست راهنما).
ابتدا یک متد جدید در backend که همان کنترلر است در فایل اضافه می کنیم /controllers/home.py
(فایل کامل در مخزن موجود است):
class HomeController(ControllerBase):
url = "/"
view_path = "/app/home/page.tsx"
async def render(self) -> None:
pass
@sideeffect
async def add_blogpost( self,
payload: str,
session: AsyncSession = Depends(DatabaseDependencies.get_db_session)
) -> None:
new_blogpost = BlogPost(text=payload)
session.add(new_blogpost)
await session.commit()
در کد بالا متد را تعریف می کنیم add_blogpost
که توسط علامت گذاری شده است @sideeffect
دکوراتور این به Mountaineer می گوید که این کد روی داده ها تأثیر می گذارد و به همین دلیل هنگام فراخوانی باید وضعیت سرور دوباره بارگذاری شود. این add_blogpost
متد به سادگی پارامتر payload را وارد می کند، که شامل تمام داده های ارسال شده از backend و جلسه ای است که نشانگر به پایگاه داده است. با استفاده از payload، یک بار جدید ایجاد می کنیم BlogPost
نمونه شی، با استفاده از رشته payload برای مقداردهی اولیه آن. این new_blogpost
شی به جلسه اضافه می شود که به یک درج در جدول Blogpost در پایگاه داده ترجمه می شود.
اصلاح دوم در نمای است. در زیر قطعه ای از page.tsx
فایل در /views/app/home
(فایل کامل در مخزن موجود است):
const CreatePost = ({ serverState }: { serverState: ServerState }) => {
const [newBlogpost, setNewBlogpost] = useState("");
return (
<div>
<input type="text"
value={newBlogpost}
onChange={(e) => setNewBlogpost(e.target.value)} />
<button
onClick={
async () => {
await serverState.add_blogpost({
payload: newBlogpost.toString()
});
setNewBlogpost("");
}}>
Post</button>
</div>
);
};
با کار بر روی frontend، یک React کامپوننت جدید را تعریف می کنیم که فقط یک فرم ساده برای مدیریت جعبه متن و onClick
رویداد زمانی که ما آن را ارسال می کنیم. همانطور که در کد بالا می بینید، ما فقط سرویس Backend را فراخوانی کردیم add_blogpost
در کنترلر تعریف شده است.
در تصویر زیر می توانید ابرقدرت واقعی Mountaineer را ببینید: ارتباط قوی بین پروژه پایتون و پروژه React/TypeScript:
در بخش React/TypeScript از کد بلوک بالا، ما تابع جدید را می بینیم، add_blogpost()
، بدون هیچ گونه پیکربندی بیشتر به کنترلر اضافه شد. هر بار که ما کد منبع را اصلاح می کنیم، Mountaineer با به روز رسانی نکات نوع و پیشنهادات عملکرد، تغییرات را در خود جای می دهد.
پس از تکمیل تمام تغییرات در مرورگر، میتوانید رابط کاربری جدید را ببینید:
هنگامی که با بررسی پایگاه داده با فرم تعامل می کنید، می توانید ببینید که کل فرآیند کار می کند:
اکنون ما یک جریان داده مناسب از frontend به پایگاه داده از طریق backend داریم. آخرین مرحله پیاده سازی جریان در جهت مخالف، از پایگاه داده به فرانت اند است. این شامل تغییرات در دو مکان است: کنترلر و نمای.
کنترلر نسخه جدیدی از آن خواهد داشت render
تابع:
async def render(
self,
request: Request,
session: AsyncSession = Depends(DatabaseDependencies.get_db_session)
) -> HomeRender:
posts = await session.execute(select(BlogPost))
return HomeRender(
posts=posts.scalars().all()
)
render
در طول رندر اولیه و در هر موردی فراخوانی می شود sideeffect
به روز رسانی. هدف آن ارائه داده هایی از پایگاه داده است که به فرانت اند ارسال می شود. به روز رسانی ها به حالت سرور با استفاده از RenderBase
نمونه ای برای به روز رسانی آن در مثال بالا، نحوه نامگذاری آرایه را می بینید posts
با اجرای یک پرس و جو بر روی یک شی خاص پر می شود BlogPost
.
از نظر، ما فقط باید داده ها را دستکاری کنیم serverState
و آنها را در اینترفیس مونتاژ کنید: برای این منظور یک کامپوننت جدید React به نام نوشتیم ShowPosts
:
const ShowPosts = ({ serverState }: { serverState: ServerState }) => {
return (
serverState.posts.map((post) => (
<div key={post.id}>
<div>{post.text}</div>
<div>{post.data}</div>
<br></br>
</div>
)))
}
ما قبلاً می دانیم که در serverState
شی، یک شی به نام پیدا خواهیم کرد posts
که در کنترلر بالا پر شده است. فایل های به روز شده در مخزن هستند و نتیجه نهایی به شرح زیر است:
نتیجه
این پروژه شباهت هایی با پروژه هایی مانند ReactPy و PyReact دارد. با این حال، بر خلاف آن پروژهها، به شما امکان میدهد هر دو قسمت ظاهری نوشته شده در TypSscript/React خالص و باطن در Python/Uvicorn خالص را در یک condebase ادغام کنید و با استفاده از Mountaineer به جفت شدن قوی بین دو لایه برسید.
با ردیابی خطای React مدرن LogRocket در چند دقیقه راهاندازی کنید:
- برای دریافت شناسه برنامه به https://logrocket.com/signup/ مراجعه کنید.
- LogRocket را از طریق NPM یا تگ اسکریپت نصب کنید.
LogRocket.init()
باید سمت مشتری نامیده شود نه سمت سرور.
NPM:
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
برچسب اسکریپت:
Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
3. (اختیاری) پلاگین ها را برای ادغام عمیق تر با پشته خود نصب کنید:
- میان افزار Redux
- میان افزار ngrx
- افزونه Vuex
در حال حاضر آغاز شده است