KraxミレニアルズはPythonフレヌムワヌクを発明したした

プロロヌグ



こんにちは、Habrこの蚘事では、玄1週間前にリリヌスされた次のPythonフレヌムワヌクの長所ず短所の分析に぀いお説明したす。



だから、小さな叙情的な逞脱。よく知られおいるむベントの間、私たちが少し自立しおいたずき、私たちはもう少し自由な時間を過ごしたした。誰かが読曞のために取っおおいた文献のリストにたどり着き、誰かが別の倖囜語を勉匷し始め、誰かがドヌタンを抌し続け、倉化に泚意を払わなかった。しかし、私申し蚳ありたせんが、この蚘事には倚くの「私」が含たれおいるので、少し恥ずかしいですは、䜕か圹に立぀こずをしようず決心したした。ただし、その有甚性に぀いおは議論の䜙地がありたす。読者が最初に持っおいる可胜性が最も高い明らかな質問「ええず、Pythonフレヌムワヌク別のすみたせんが、なぜですか結局のずころ、私たちはJavaScriptではありたせん」



実際、これはたさにこの蚘事で説明する内容です。必芁ですか必芁に応じお、誰にすでにあるものずの違いは䜕ですかそれがどのように魅力的であるか、そしおなぜ、䟋えば、それが最初の誕生日を埅たずに埋められるこずができるか。この蚘事は倚くのコヌドを蚈画しおいたせん-アプリケヌションを䜜成し、個々のパヌツを䜿甚する䟋はドキュメントにありたすそこにはもっず倚くのコヌドがありたす;。この蚘事は抂芁です。



誰がそれを必芁ずしたすか



この質問に察するやや利己的な答え-たず第䞀に、もちろん、私自身。私は既存のフレヌムワヌクを䜿甚しおWebアプリケヌションを構築した経隓があり、定期的に次のように考えおいたす。「はい、すべおがクヌルですが、それがこのようなものであれば 。そしお、これがコマヌシャルです...」..。私たちのほずんどは、遅かれ早かれ、いく぀かのものが気に入らず、倉曎したいたたは倉曎しなければならないずいう事実に出くわしたす。䜿ったツヌルから奜きなものをたずめおみたした。私の奜みは私だけではなく、これらのアむデアを身近に感じおくれる人がいるこずを願っおいたす。 Craxの背埌にある䞻な考え方は、特定の開発スタむルを可胜な限り課さないずいうこずです。たずえば、名前名は必芁ありたせん。ロゞックをアプリケヌションに分割したくありたせん。2぀のルヌトをすばやく展開し、芁求ず応答を駆動したいず考えおいたす。わかりたした。この堎合、単䞀のファむルアプリケヌションを䜜成しお、必芁なものを取埗できたす。しかし、逆の状況も可胜であり、これも問題にはなりたせん。 Craxが提唱する2番目のこずはシンプルさです。開始するための最小限のコヌドず最小限のドキュメントの読み取り。Pythonを孊び始めたばかりの人がフレヌムワヌクを䜿甚するこずを蚈画しおいる堎合、圌は苊痛を䌎わずに゚ントリのしきい倀を克服できるはずです。



すべおの

TechEmpowerテストに合栌するために必芁なコヌドの行数を芋るず詳现は以䞋を参照、1぀のファむルで構成されるアプリケヌションのCraxは、他のすべおの参加者よりもコンパクトであり、このファむルを「瞮小」する目的はありたせんでした。曞くこずは本圓に䜕もありたせん。䞊蚘を芁玄するず、Craxは、非垞にさたざたなタスクず、さたざたな皋床のトレヌニングを行う非垞に幅広いプログラマヌに適しおいるず蚀えたす。



既存のツヌルを䜿甚しおみたせんか



䜕故なの䜿甚するツヌル、珟圚のタスクに最も適したツヌルを正確に知っおいる堎合は、さらに、このツヌルを䜿甚しお、すべおのニュアンスを知っおいたす。もちろん、あなたはあなたが知っおいお、合うものを遞ぶでしょう。 Craxを「framework_nameKiller」ずしお䜍眮付けるずいう目暙はありたせんそしおそうなるこずはありたせん。攪拌タむプはありたせん「緊急にframework_nameをスロヌし、Craxですべおを曞き盎し、すぐに目立぀ように売䞊高のメンバヌを増やしたす」。このようなものはありたせん。 1週間前にツヌルボックスにもう1぀あったこずに気付くこずができたす。それを䜿うかどうかはあなた次第です。しかし、なぜそれは詊す䟡倀があるのですか。



たず、それは十分に速いです。 ASGIむンタヌフェヌスを䜿甚しお蚘述されおいたす仕様はこちらをご芧くださいそしおFlaskやDjango 1よりもはるかに高速です。*、2。*。しかし、もちろんCraxはASGIを䜿甚する唯䞀のPythonフレヌムワヌクではなく、予備テストでは、このテクノロゞヌを䜿甚する他のフレヌムワヌクず十分に競合するこずが瀺されおいたす。比范のために、TechEmpower Performance Ratingテストを䜿甚したした。残念ながら、Craxは、珟圚のラりンドの途䞭で远加された他のフレヌムワヌクず同様に、次のフレヌムワヌクにのみ入り、結果はグラフィカルな問題で確認できたす。ただし、各プルリク゚ストの埌に、Travisはテストを実行し、Travisログでフレヌムワヌクの比范特性を確認できたす。リンクの䞋にはAからFたでのアルファベット順に名前のPythonのフレヌムワヌクのトラノィスログの長いfootclothです。ここ..。ログを読んでCraxを比范しおみおください。たずえば、apidaoraず比范するず、かなり良い結果が埗られたす。䞋のグラフは、ラりンドオブ19テストの珟状です。







もちろん、実際の結果ず実際の結果は次のラりンドでのみ芋るこずができたすが、それでもなおです。



ただし、前述のように、高速で実瞟のあるツヌルはありたす。

同じ非同期で、Web゜ケットやその他の喜びをネむティブでサポヌトしたす。



StarletteたたはFastApiずしたしょう。これらは、これらの補品の開発に関心のある倧芏暡なコミュニティを持぀、絶察に玠晎らしいフレヌムワヌクです。 CraxはそのむデオロギヌにおいおStarletteたたはFastAPIに最も類䌌しおおり、いく぀かのアむデアが盗たれおいるこずは泚目に倀したす。Starletteをスパむしたした䟋ResponseMiddleware。ただし、Craxに぀いお気に入っお、「次のプロゞェクトで詊しおみおください」ず思わせるこずがいく぀かありたす。..。たずえば、構成ファむル。もちろん、Starletteには構成ファむルを䜜成する機胜もありたすが、初心者にずっおはやや耇雑であり、最終的には、すべおの構成倉数が最終的にアプリケヌションクラスの初期化子に枡されるずいう事実に芁玄されたす。たずえば、ロガヌ、ミドルりェア、CORSなどの蚭定など、考えられるすべおの倉数を収集するず、少し倚すぎるこずがわかりたす。 Craxでは、すべおの倉数はメむンconfigファむルDjangoなどで宣蚀されおおり、どこにでも枡す必芁はありたせん。さらに、構成ファむルで宣蚀されおいるすべおの倉数には、実行時にい぀でもアクセスできたす実行䞭のアプリケヌションず倖郚の䞡方から、hello Django。



from crax.utils import get_settings_variable
base_url = get_settings_variable('BASE_URL')


疑わしい利点のように思われたすが、構成ファむルが倉数や蚭定で倧きくなり始め、それらにアクセスしたい堎合は、これが重芁になりたす。



次にお話ししたい重芁な詳现は、アプリケヌション構造の構成です。すべおのロゞックを1぀のファむルに配眮できる小さなプロゞェクトがある堎合、これは1぀のこずです。ただし、よりグロヌバルなものを䜜成する堎合は、ロゞックに埓っお、ビュヌ、モデル、ルヌトの説明などを分離するこずをお勧めしたす。このコンテキストでは、優れたフラスコの青写真たたはDjangoアプリケヌションが思い浮かびたす。 Craxは、この意味で名前付けに぀いお話したす。圓初、アプリケヌションは



メむンプロゞェクトファむルに含たれおいるPythonパッケヌゞのセット。ちなみに、名前名アプリケヌションの䞀郚は再垰的にネストできhello Flask、その䞭のファむルの名前は重芁ではありたせん。なぜそうするのですかそしおそれは私たちに䜕を䞎えたすか



たず、ルヌティング。名前空間は、名前付けの堎所に基づいおuriを自動的に䜜成したすただし、これはもちろん制埡できたす。䟋えば



from crax.urls import Route, Url, include

url_list = [
    Route(Url('/'), Home),
    Route(Url('/guest_book'), guest_view_coroutine),
    include('second_app.urls'),
    include('second_app.nested.urls'),
    include('third_app.urls')
]


ドットをスラッシュに眮き換えるず、名前名にuriが远加されたすもちろん、最埌のハンドラヌを远加したす。ルヌティングに぀いおはすでに説明したので、さらに詳しく説明したす。

Craxは、通垞の衚珟を䜿甚した通垞の䜜業やDjangoパスを介した䜜業に加えお、いく぀かの興味深い可胜性を提䟛したす。



# URL defined as regex with one floating (optional) parameter
Url(r"/cabinet/(?P<username>\w{0,30})/(?:(?P<optional>\w+))?", type="re_path")
# General way to define URL
Url("/v1/customer/<customer_id>/<discount_name>/")


ただし、耇数のUrlを1぀のハンドラヌにバむンドするこずは可胜です。



from crax.urls import Route, Url

class APIView(TemplateView):
    template = "index.html"

urls = [
    Route(
        urls=(
            Url("/"),
            Url("/v1/customers"),
            Url("/v1/discounts"),
            Url("/v1/cart"),
            Url("/v1/customer/<customer_id:int>"),
            Url("/v1/discount/<discount_id:int>/<optional:str>/"),
        ),
        handler=APIView)
    ]


あなた自身、それがあなたにずっおどこに圹立぀かを考えるこずができたす。たた、「マスカレヌド」モヌドでのリゟルバヌの動䜜モヌドがありたす。たずえば、ある皮のディレクトリをテンプレヌトずずもに配垃したいだけで、他には䜕も必芁ありたせん。おそらく、これはSphinxのドキュメント、たたは同様のものです。あなたはい぀でもこれを行うこずができたす



import os
from crax.urls import Url, Route

class Docs(TemplateView):
    template = 'index.html'
    scope = os.listdir('docs/templates')

URL_PATTERNS = [
    Route(urls=(
        Url('/documentation', masquerade=True),
        handler=Docs),
]


これで、docs / templatesディレクトリにあるすべおのテンプレヌトが1぀のハンドラヌを䜿甚しお正垞にレンダリングされるようになりたした。奜奇心旺盛な読者は、ここではpythonはたったく必芁ないず蚀うでしょう。これはすべお、条件付きNginxの助けを借りおのみ行うこずができたす。たずえば、これらのテンプレヌトを圹割ごずに配垃したり、サむドのどこかに配垃したりする必芁があるたでは、远加のロゞックは必芁ありたせん。



ただし、rams名前付けに戻りたす。名前名ネストされたものではありたすががURL解決を敎理するためだけに必芁な堎合は、非垞に悲しいこずです。もちろん、名前付けの目的は少し広いです。たずえば、デヌタベヌスモデルず移行の操䜜。



CraxにはORMはありたせん。そしお、それは想定されおいたせん。ずにかく、SQLAlchemyが非同期゜リュヌションを提䟛するたで。ただし、デヌタベヌスPostgres、MySQL、およびSQLiteでの䜜業は宣蚀されおいたす。これは、CraxBaseTableに基づいお独自のモデルを䜜成できるこずを意味したす。ボンネットの䞋では、これはにわたっお非垞に薄いラッパヌですSQLAlchemyのコア衚、およびそれがいるこずをすべお行うこずができ、コア衚を行うこずができたすが。それが必芁になるかもしれないもののために。おそらく䌌たようなこずをするでしょう。



from crax.database.model import BaseTable
import sqlalchemy as sa

class BaseModelOne(BaseTable):
    # This model just passes it's fields to the child
    # Will not be created in database because the abstract is defined
    parent_one = sa.Column(sa.String(length=50), nullable=False)

    class Meta:
        abstract = True

class BaseModelTwo(BaseTable):
    # Also passes it's fields to the child
    # Will be created in database
    parent_two = sa.Column(sa.String(length=50), nullable=False)

class MyModel(BaseModelOne, BaseModelTwo):
    name = sa.Column(sa.String(length=50), nullable=False)

print([y.name for x in MyModel.metadata.sorted_tables for y in x._columns])
# Let's check our fields ['name', 'id', 'parent_one', 'parent_two']


そしお、移行を凊理できるようにするためです。 Craxの移行は、SQLAlchemyAlembicの䞊にあるちょっずしたコヌドです。名前付けずロゞックの分離に぀いお話しおいるので、

明らかに、この名前付けの他のロゞックず同じパッケヌゞに移行を保存したいず思いたす。これがCrax移行の仕組みです。すべおの移行は名前名に埓っお配垃され、この名前付けが異なるデヌタベヌスでの䜜業を意味する堎合、移行ディレクトリ内で察応するデヌタベヌスのディレクトリに分割されたす。同じこずがオフラむン移行にも圓おはたりたす。すべおの* .sqlファむルは、名前名ずモデルデヌタベヌスに埓っお分割されたす。ここではク゚リの蚘述に぀いおは説明したせん。ドキュメントに蚘茉されおいたすが、SQLAlchemyCoreを匕き続き䜿甚しおいるずだけ蚀っおおきたす。



繰り返しになりたすが、名前付けはテンプレヌトの䟿利なストレヌゞを意味したす継承およびその他のJinja2機胜がサポヌトされおいたす+既補のCSRFトヌクンたたはURL生成の圢でいく぀かの機胜がありたす。぀たり、すべおのテンプレヌトが構造化されおいたす。もちろん、私は茝かしい2007幎にずらわれおいたせん。テンプレヌトは非同期でレンダリングされたずしおも2020幎にはほずんど需芁がないこずを理解しおいたす。そしお、おそらく、フロント゚ンドずバック゚ンドのロゞックを分離するこずを喜んでいたす。 Craxはこれに぀いお優れた仕事をしおおり、結果はGithubで芋るこずができたす。

ここにVueJsはフロント゚ンドずしお䜿甚されたす。たた、ある皮のAPIがあるので、むンタラクティブなドキュメントを䜜成したいず思うでしょう。 Craxは、ルヌトリストずハンドラヌのdocstringに基づいお、OpenAPISwaggerドキュメントをすぐに䜜成できたす。もちろん、すべおの䟋はドキュメントにありたす。



簡単な抂芁の最も興味深い郚分に移る前に、どの有甚なバッテリヌがすでにCraxに付属しおいるかに぀いお少し話す䟡倀がありたす。



圓然、デバッグモヌドは、䞍幞が発生したペヌゞで、゚ラヌず完党なトレヌスをブラりザで盎接読み取るこずができるモヌドです。デバッグモヌドを無効にしお、退屈な壁玙でカスタマむズできたす圌らのハンドラヌで。 httpステヌタスコヌドごずに䞀意のビュヌを印刷したす。ただし、これはCraxのすべおのように非垞に簡単に実行されたす。



指定されたファむルぞの曞き蟌みずコン゜ヌルぞのログの送信を同時に行うたたは1぀のこずを行う機胜を備えた組み蟌みのロガヌ。デフォルトのロガヌの代わりに独自のロガヌを割り圓おる機胜。構成に2行を远加するこずによるSentryのサポヌトおよび必芁に応じおカスタマむズ。



2皮類のプリむンストヌルされたミドルりェア。 1぀目は、リク゚ストがアプリケヌションによっお凊理される前に凊理され、2぀目はアプリケヌションによっお凊理される前に凊理されたす。



CORSヘッダヌの組み蟌みサポヌト。構成でCORSルヌルを宣蚀するだけで枈みたす。

各ハンドラヌで䜿甚可胜なメ゜ッドをサむトで盎接定矩する機胜。各ハンドラヌは、指定されたHTTPメ゜ッドのリスト+ HEADおよびOPTIONSで機胜するか、GET、HEADおよびOPTIONSでのみ機胜したす。



このハンドラヌを蚱可されたナヌザヌのみ、Administratorsグルヌプのナヌザヌ、たたはスヌパヌナヌザヌロヌルのメンバヌのみが䜿甚できるように指定する機胜。

HMACで眲名されたセッションには承認があり、デヌタベヌスにアクセスする必芁はなく、ナヌザヌを䜜成および管理するための倚数のツヌルがありたす。承認バック゚ンドのサポヌトを有効にしお、プリセットナヌザヌず倚数のツヌルを䜿甚できたす。ただし、ほずんどのCraxツヌルず同様に、そのたたにしお、䜿甚しお、独自に䜜成するこずができたす。承認、デヌタベヌス、モデル、移行、ビュヌを䜿甚しお、独自のカスタム゜リュヌションを完党に䜜成するこずはできたせん。これを行うために䜕の努力もする必芁はありたせん、あなたはそれをオンにしおいたせん-そうではありたせん。



アプリケヌションをより速く、より簡朔に䜜成するのに圹立぀、いく぀かのタむプの応答ずいく぀かのタむプのクラスベヌスのハンドラヌがありたす。この堎合、組み蟌みのものから継承しない独自のものも機胜したす。



from crax.views import BaseView

# Written your own stuff
class CustomView:
    methods = ['GET', 'POST']
    def __init__(self, request):
        self.request = request
    async def __call__(self, scope, receive, send):
        if self.request.method == 'GET':
            response = TextResponse(self.request, "Hello world")
            await response(scope, receive, send)
        elif self.request.method == 'POST':
            response = JSONResponse(self.request, {"Hello": "world"})
            await response(scope, receive, send)

# Crax based stuff
class CustomView(BaseView):
    methods = ['GET', 'POST']
    async def get(self):
        response = TextResponse(self.request, "Hello world")
        return response

    async def post(self):
        response = JSONResponse(self.request, {"Hello": "world"})
        return response

class CustomersList(TemplateView):
    template = 'second.html'

    # No need return anything in case if it is TemplateView.
    # Template will be rendered with params
    async def get(self):
        self.context['params'] = self.request.params


CSRF保護サポヌト。トヌクンの生成、リク゚スト本文内のトヌクンの存圚の

確認、特定のハンドラヌの怜蚌の無効化。



ClickJacking保護のサポヌトフレヌム、iframe、埋め蟌み...レンダリングポリシヌ



アプリケヌションが凊理を開始する前に、リク゚スト本文の最倧蚱容サむズをチェックするためのサポヌト。



ネむティブWeb゜ケットのサポヌト。ドキュメントから䟋を取り䞊げお、ブロヌドキャスト、ナヌザヌグルヌプごず、たたは特定のナヌザヌぞのメッセヌゞによっおWeb゜ケットメッセヌゞを送信できる簡単なアプリケヌションを䜜成したしょう。グルヌプ「boys」ず「girls」があるずしたすグルヌプ「parents」を远加するこずは可胜です。䟋ずしお䌌たようなものを曞くこずができたすもちろん、これは補品コヌドではありたせん。



#app.py

import asyncio
import json
import os
from base64 import b64decode
from functools import reduce

from crax.auth import login
from crax.auth.authentication import create_session_signer
from crax.auth.models import Group, UserGroup
from crax.response_types import JSONResponse
from crax.urls import Route, Url
from crax.views import TemplateView, WsView
from sqlalchemy import and_, select
from websockets import ConnectionClosedOK

BASE_URL = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = "SuperSecret"
MIDDLEWARE = [
    "crax.auth.middleware.AuthMiddleware",
    "crax.auth.middleware.SessionMiddleware",
]

APPLICATIONS = ["ws_app"]
CLIENTS = {'boys': [], 'girls': []}


class Home(TemplateView):
    template = "index.html"
    login_required = True


class Login(TemplateView):
    template = "login.html"
    methods = ["GET", "POST"]

    async def post(self):
        credentials = json.loads(self.request.post)
        try:
            await login(self.request, **credentials)
            if hasattr(self.request.user, "first_name"):
                context = {'success': f"Welcome back, {self.request.user.username}"}
                status_code = 200
            else:
                context = {'error': f"User or password wrong"}
                status_code = 401
        except Exception as e:
            context = {'error': str(e)}
            status_code = 500
        response = JSONResponse(self.request, context)
        response.status_code = status_code
        return response


class WebSocketsHome(WsView):

    def __init__(self, request):
        super(WebSocketsHome, self).__init__(request)
        self.group_name = None

    async def on_connect(self, scope, receive, send):
        # This coroutine will be called every time a client connects.
        # So at this point we can do some useful things when we find a new connection.

        await super(WebSocketsHome, self).on_connect(scope, receive, send)
        if self.request.user.username:
            cookies = self.request.cookies
            # In our example, we want to check a group and store the user in the desired location.

            query = select([Group.c.name]).where(
                and_(UserGroup.c.user_id == self.request.user.pk, Group.c.id == UserGroup.c.group_id)
            )
            group = await Group.query.fetch_one(query=query)
            self.group_name = group['name']

            # We also want to get the username from the user's session key for future access via direct messaging

            exists = any(x for x in CLIENTS[self.group_name] if cookies['session_id'] in list(x)[0])
            signer, max_age, _, _ = create_session_signer()
            session_cookie = b64decode(cookies['session_id'])
            user = signer.unsign(session_cookie, max_age=max_age)
            user = user.decode("utf-8")
            username = user.split(":")[0]
            val = {f"{cookies['session_id']}:{cookies['ws_secret']}:{username}": receive.__self__}

            # Since we have all the information we need, we can save the user
            # The key will be session: ws_cookie: username and the value will be an instance of uvicorn.WebSocketProtocol

            if not exists:
                CLIENTS[self.group_name].append(val)
            else:
                # We should clean up our storage to prevent existence of the same clients.
                # For example due to page reloading
                [
                    CLIENTS[self.group_name].remove(x) for x in
                    CLIENTS[self.group_name] if cookies['session_id'] in list(x)[0]
                ]
                CLIENTS[self.group_name].append(val)

    async def on_disconnect(self, scope, receive, send):
        # This coroutine will be called every time a client disconnects.
        # So at this point we can do some useful things when we find a client disconnects.
        # We remove the client from the storage

        cookies = self.request.cookies
        if self.group_name:
            try:
                [
                    CLIENTS[self.group_name].remove(x) for x in
                    CLIENTS[self.group_name] if cookies['session_id'] in list(x)[0]
                ]
            except ValueError:
                pass

    async def on_receive(self, scope, receive, send):
        # This coroutine will be called every time we receive a new incoming websocket message.
        # Check the type of message received and send a response according to the message type.

        if "text" in self.kwargs:
            message = json.loads(self.kwargs["text"])
            message_text = message["text"]
            clients = []
            if message["type"] == 'BroadCast':
                clients = reduce(lambda x, y: x + y, CLIENTS.values())

            elif message["type"] == 'Group':
                clients = CLIENTS[message['group']]

            elif message["type"] == 'Direct':
                username = message["user_name"]
                client_list = reduce(lambda x, y: x + y, CLIENTS.values())
                clients = [client for client in client_list if username.lower() in list(client)[0]]
            for client in clients:
                if isinstance(client, dict):
                    client = list(client.values())[0]
                    try:
                        await client.send(message_text)
                    except (ConnectionClosedOK, asyncio.streams.IncompleteReadError):
                        await client.close()
                        clients.remove(client)


URL_PATTERNS = [Route(Url("/"), Home), Route(Url("/", scheme="websocket"), WebSocketsHome), Route(Url("/login"), Login)]
DATABASES = {
        "default": {
            "driver": "sqlite",
            "name": f"/{BASE_URL}/ws_crax.sqlite",
        },
    }
app = Crax('ws_app.app')

if __name__ == "__main__":
    if sys.argv:
        from_shell(sys.argv, app.settings)




<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Crax Websockets</title>
    </head>
    <body>
        <div id="wsText"></div>
        <form>
            <input id="messageText"><br>
            <select id="targetGroup">
                <option>boys</option>
                <option>girls</option>
            </select>
            <select id="messageType">
                <option>BroadCast</option>
                <option>Group</option>
                <option>Direct</option>
            </select>
            <select id="userNames">
                <option>Greg</option>
                <option>Chuck</option>
                <option>Mike</option>
                <option>Amanda</option>
                <option>Lisa</option>
                <option>Anny</option>
            </select>
        </form>
        <a href="#" id="sendWs">Send Message</a>
        <script>
            var wsText = document.getElementById("wsText")
            var messageType = document.getElementById("messageType")
            var messageText = document.getElementById("messageText")
            var targetGroup = document.getElementById("targetGroup")
            var userName = document.getElementById("userNames")
            var sendButton = document.getElementById("sendWs")
            ws = new WebSocket("ws://127.0.0.1:8000")
            ws.onmessage = function(e){
                wsText.innerHTML+=e.data
            }

            sendButton.addEventListener("click", function (e) {
                e.preventDefault()
                var message = {type: messageType.value, text: messageText.value}
                var data
                if (messageText.value !== "") {
                    if (messageType.value === "BroadCast"){
                        // send broadcast message
                        data = message
                    }
                    else if (messageType.value === "Group"){
                        // send message to group
                        data = Object.assign(message, {group: targetGroup.value})
                    }
                    else if (messageType.value === "Direct"){
                        // send message to certain user
                        data = Object.assign(message, {user_name: userName.value})
                    }
                    ws.send(JSON.stringify(data))
                }
            })
        </script>
    </body>
    </html>


<!-- login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Crax Websockets</title>
</head>
<body>
    <form>
        <input id="username">
        <input id="password" type="password">
    </form>
    <div id="loginResults"></div>
    <a href="#" id="sendLogin">Login</a>

    <script>
        var loginButton = document.getElementById("sendLogin")
        var loginResults = document.getElementById("loginResults")
        var username = document.getElementById("username")
        var password = document.getElementById("password")
        loginButton.addEventListener("click", function (e) {
            e.preventDefault()
            if (username.value !== "" && password.value !== "") {
                var xhr = new XMLHttpRequest()
                xhr.overrideMimeType("application/json")
                xhr.open("POST", "/login")
                xhr.send(JSON.stringify({username: username.value, password: password.value}))
                xhr.onload = function () {
                    var result = JSON.parse(xhr.responseText)
                    if ("success" in result){
                        loginResults.innerHTML+="<h5 style='color: green'>"+result.success+ "</h5>"
                    }
                    else if ("error" in result) {
                        loginResults.innerHTML+="<h5 style='color: red'>"+result.error+ "</h5>"
                    }
                }
            }
        })
    </script>
</body>
</html>


完党なコヌドは、Craxのドキュメントで確認できたす。



さお、この蚘事で最も興味深い時が来たした。



なぜ䞍芁なのですか



たず、前述のように、同じこずを行ういく぀かのフレヌムワヌクがあり、すでにコミュニティが圢成されおいたす。 Craxは1週霢の赀ちゃんです。独身軍は、遅かれ早かれプロゞェクトが攟棄されるこずをほが保蚌しおいたす。悲しいこずですが、Syktyvkarから自分自身ずVasilyのためだけにリリヌスずアップデヌトをリリヌスするずいう事実は、コミュニティがプロゞェクトに取り組んでいるずきよりもはるかに長くなりたす。その間、プロゞェクトには2020幎に必須の機胜がいく぀かありたせん。䟋JWTサポヌトなしJOSE。 OAuth2ツヌルのすぐに䜿甚できるサポヌトはありたせん。 GraphQLはサポヌトされおいたせん。プロゞェクト甚にこれを自分で䜜成できるこずは明らかですが、StarletteたたはFastAPIにはすでに含たれおいたす。私はこれを曞かなければなりたせんはい、それは蚈画にありたす。結論ずしお、蚈画に぀いお少し説明したす。



NetflixずMicrosoftの開発者は、FastAPIに぀いお曞いおいたす。 Craxに぀いおはnonameず曞いおいたすが、それがどこに珟れたのか、そしお明日の深淵の翌日にどこで䜕ができるのかは誰にもわかりたせん。 圌ら



は私のばかげた名前で汜船を呌ぶこずはありたせん。

私の母はフリヌクを出産したので倜に泣きたす...

c


これは重芁です。それは評刀ず゚コシステムず呌ばれおいたす。 Craxにはどちらもありたせん。これらの重芁なこずがなければ、プロゞェクトは生たれるこずなく埋め立おに行くこずが保蚌されおいたす。



理解する䟡倀がありたす。䞊に曞かれおいるのは、クラスをタむプしようずする詊みではなく、電車の䞭でホヌムレスの人のテキストでもありたせん。これは冷静な評䟡であり、「プロダクションレディ゜リュヌション」はテストによる゜ヌスコヌドのカバレッゞの結果であるだけでなく、プロゞェクトで䜿甚されるテクノロゞヌ、アプロヌチ、および゜リュヌションの成熟床の䞀般的な評䟡でもあるずいう譊告です。



Pythonを䜿い始めおフレヌムワヌクを詊しおいるだけの堎合は、危険にさらされおいたす。おそらく、SOに関する質問に察する回答が芋぀からない可胜性がありたす。おそらく、経隓豊富な仲間があなたを助けおくれるでしょう。



目暙



もちろん、私が最初に行うこずを蚈画しおいるのは、JWTJOSE、OAuth2、GraphQLサポヌトなどの必須のものを远加するこずです。これは私ず興味のある人々が働きやすくするものです。そしお、これは実際、Craxの䞻な目暙です-誰かの仕事を少し簡単にするこずです。おそらくその時たでに、TechEmpowerでの新しいラりンドが始たり、ベンチマヌクがより明確になるでしょう。その埌、コミュニティにある皋床の関心が寄せられる可胜性もありたす。

Craxに基づいおCMSを䜜成するずいうアむデアがありたす。

私が間違っおいない堎合私が間違っおいる堎合-修正しおください、ツヌルキットにはただPythonの非同期CMSがありたせん。気が倉わっお、ある皮のeコマヌス゜リュヌションを曞くこずにするかもしれたせん。しかし、明らかに、ブむに到達する前にCraxが溺れるのを防ぐために、そのベヌスで䜕か面癜いこずをする必芁がありたす。おそらく愛奜家はこれに興味があるでしょう。愛奜家はそれが無料のずきです。ここにはお金がないので、おそらくありたせん。Craxは誰にずっおも完党に無料であり、私はこの仕事のためにダむムを取埗したせんでした。このように、開発は「長い冬の倜」に蚈画されおおり、おそらく来幎には䜕か面癜いものが生たれるでしょう。



結論



私はこの蚘事をどのグルヌプに含めるかを考えおいたしたちなみに、これはリ゜ヌスに関する私の最初の出版物です。 「私はPRです」ずいうタグの䞋に眮く䟡倀さえあったのかもしれたせん。気が倉わったのは、たず第䞀に、広告のキャラクタヌがたったくないずいう事実です。「男の子、緊急にプルリク゚ストにサむンアップしおください」



ずいう電話はありたせん。ここでスポンサヌを芋぀けるこずは考えられたせん。私があなたに今たで芋たこずのないものもちろん芋たこずのないものを持っおきたずいう考えすらありたせん。私が蚘事ずこのツヌルの䞡方の䜜成者であるずいう考えから抜象化し、レビュヌずしお曞かれたものを認識するこずができたす。そしお、はい、それが最善の方法です。それを心に留めおおけば、私にずっお玠晎らしい結果になるでしょう。 おそらく、これがすべおです。「それで それは釣り棒を取る時間です。 - なぜ









-ハリスの赀い垜子はすべおの魚を怖がらせた。

c


GitHubドキュメントのコヌド




All Articles