Prolog
Halo, Habr! Artikel ini dikhususkan untuk analisis pro dan kontra dari framework Python berikutnya, yang dirilis sekitar seminggu yang lalu.
Jadi, sedikit penyimpangan lirik. Selama peristiwa-peristiwa terkenal, ketika kami sedikit mengisolasi diri, kami memiliki lebih banyak waktu luang. Ada yang masuk ke daftar lektur yang disisihkan untuk dibaca, ada yang mulai belajar bahasa asing lain, ada yang terus menekan di Dotan dan tidak memperhatikan perubahannya. Tapi saya (maaf, artikel ini akan banyak mengandung "saya", dan saya sedikit malu) memutuskan dan mencoba melakukan sesuatu yang bermanfaat. Namun, kegunaannya masih bisa diperdebatkan. Pertanyaan-pertanyaan jelas yang kemungkinan besar dimiliki pembaca di tempat pertama:โUm, kerangka Python? Lain? Permisi, tapi kenapa? Kami bukan JavaScript! "
Sebenarnya inilah yang akan dibahas di artikel ini: Apakah perlu? Jika perlu, kepada siapa? Apa bedanya dengan yang sudah ada? Bagaimana bisa menarik dan kenapa, misalnya bisa dikuburkan tanpa menunggu ulang tahun pertama. Artikel tersebut tidak merencanakan banyak kode - contoh penulisan aplikasi dan menggunakan bagian individu dapat ditemukan di dokumentasi (ada lebih banyak kode di sana;)). Artikel ini lebih merupakan ikhtisar.
Siapa yang membutuhkannya?
Jawaban yang agak egois untuk pertanyaan ini - pertama-tama, tentu saja, saya sendiri. Saya memiliki pengalaman dalam membangun aplikasi web menggunakan kerangka kerja yang ada dan saya sering berpikir: โYa, semuanya keren, tapi kalau saja seperti iniโฆ. Dan ini iklannya ... "... Kebanyakan dari kita, dengan satu atau lain cara, cepat atau lambat akan menemukan fakta bahwa beberapa hal tidak disukai dan ingin (atau bahkan harus) mengubahnya. Saya mencoba mengumpulkan apa yang saya suka dari alat yang telah saya gunakan. Saya berharap bahwa saya tidak sendirian dalam preferensi saya, dan bahwa ada orang yang akan menemukan ide ini dekat. Ide utama di balik Crax adalah bahwa Crax tidak memaksakan gaya pengembangan tertentu sebanyak mungkin. Misalnya, kami tidak memerlukan ruang nama, kami tidak ingin membagi logika ke dalam aplikasi, kami ingin dengan cepat menerapkan dua rute dan mendorong permintaan dan tanggapan. Ok, dalam hal ini kita bisa membuat aplikasi file tunggal dan mendapatkan apa yang kita inginkan. Tetapi situasi sebaliknya juga mungkin terjadi, dan ini juga tidak akan menjadi masalah. Hal kedua yang didukung Crax adalah kesederhanaan. Minimal kode dan minimal membaca dokumentasi untuk memulai.Jika seseorang yang baru mulai belajar Python berencana untuk bekerja dengan kerangka kerja, dia harus dapat mengatasi ambang pintu masuk tanpa rasa sakit.
Jika Anda melihat jumlah baris kode yang diperlukan untuk lulus semua tes
TechEmpower (lebih lanjut di bawah), maka Crax dalam aplikasi yang terdiri dari satu file lebih ringkas daripada semua peserta lainnya, dan tidak ada tujuan untuk "mengecilkan" file ini. Tidak ada lagi yang bisa ditulis. Meringkas hal di atas, kita dapat mengatakan bahwa Crax cocok untuk berbagai tugas yang sangat berbeda dan sangat banyak pemrogram dengan berbagai tingkat pelatihan.
Mengapa tidak menggunakan alat yang sudah ada?
Kenapa tidak? Jika Anda tahu persis alat mana yang akan digunakan, apa yang paling cocok untuk tugas Anda saat ini, terlebih lagi, Anda telah bekerja dengan alat ini dan mengetahui semua nuansanya. Tentu saja, Anda akan memilih apa yang Anda ketahui dan sesuai. Tidak ada (dan tidak akan) tujuan untuk memposisikan Crax sebagai "% framework_name% Killer". Tidak akan ada jenis agitasi: "Lemparkan segera% framework_name%, tulis ulang semua yang ada di Crax dan segera tingkatkan
Pertama, cukup cepat. Itu ditulis menggunakan antarmuka ASGI (baca spesifikasinya di sini) dan jauh lebih cepat dari Flask atau Django 1. *, 2. *. Tetapi Crax tentu saja bukan satu-satunya framework Python yang menggunakan ASGI, dan tes pendahuluan menunjukkan bahwa Crax bersaing dengan baik dengan framework lain yang menggunakan teknologi ini. Sebagai perbandingan, kami menggunakan tes Peringkat Kinerja TechEmpower . Sayangnya, Crax, seperti kerangka lain yang ditambahkan di tengah putaran saat ini, hanya akan masuk ke putaran berikutnya, dan kemudian Anda dapat melihat hasilnya dalam masalah grafis. Namun, setelah setiap permintaan tarik, Travis menjalankan pengujian dan Anda dapat melihat karakteristik komparatif kerangka kerja di log Travis. Di bawah tautan adalah footcloth panjang dari log Travis untuk kerangka kerja Python dengan nama dalam urutan abjad dari A hingga F Di sini... Anda dapat mencoba membaca log dan membandingkan Crax, misalnya, dengan apidaora, hasilnya akan cukup bagus. Di bawah ini pada grafik adalah keadaan saat ini dalam tes Putaran ke 19.
Tentu saja, kami akan dapat melihat hasil nyata dan hasil nyata hanya di babak berikutnya, namun demikian.
Namun, kami, seperti yang disebutkan di atas, memiliki alat yang tidak kalah cepat dan sudah terbukti.
Asynchronous yang sama, dengan dukungan asli untuk websockets dan kesenangan lainnya.
Katakanlah Starlette atau FastApi. Mereka benar-benar kerangka kerja yang luar biasa dengan komunitas besar yang tertarik untuk mengembangkan produk ini. Perlu dicatat bahwa Crax paling mirip dengan Starlette atau FastAPI dalam ideologinya, dan beberapa ide telah
from crax.utils import get_settings_variable
base_url = get_settings_variable('BASE_URL')
Akan tampak keuntungan yang meragukan, namun, ketika file konfigurasi mulai bertambah banyak dengan variabel dan pengaturan, dan kita ingin memiliki akses ke sana, ini menjadi penting.
Detail penting berikutnya yang ingin saya bicarakan adalah organisasi struktur aplikasi. Ketika Anda memiliki proyek kecil, semua logikanya dapat ditempatkan dalam satu file, ini adalah satu hal. Tetapi ketika Anda menulis sesuatu yang lebih global, Anda mungkin ingin memisahkan tampilan, model, deskripsi rute, dll., Sesuai dengan logikanya. Dalam konteks ini, cetak biru Flask yang bagus atau aplikasi Django muncul di benak. Crax berbicara tentang namespace dalam pengertian ini. Awalnya, aplikasi Anda dimaksudkan untuk menjadi
satu set paket python yang termasuk dalam file proyek utama. Ngomong-ngomong, namespace (bagian aplikasi Anda) dapat disarangkan secara rekursif (hello Flask), dan nama file di dalamnya tidak menjadi masalah. Mengapa demikian? Dan apa yang diberikannya kepada kita?
Pertama, perutean. Namespaces akan membuat uri berdasarkan lokasi namespace secara otomatis (tapi ini, tentu saja, dapat dikontrol). Misalnya:
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')
]
Ganti titik dengan garis miring dan Anda akan mendapatkan uri ke namespace Anda (tentu saja, dengan menambahkan penangan terakhir). Karena kami telah menyebutkan perutean, kami akan membahasnya lebih detail.
Crax menawarkan beberapa kemungkinan menarik, selain pekerjaan biasa dengan ekspresi reguler atau pekerjaan melalui jalur 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>/")
Namun, dimungkinkan untuk mengikat beberapa Url ke satu penangan.
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)
]
Anda sendiri dapat memikirkan di mana hal itu dapat berguna bagi Anda. Dan juga, ada mode operasi resolver dalam mode "masquerading". Misalnya, Anda hanya ingin mendistribusikan beberapa jenis direktori dengan templat, dan tidak menginginkan yang lain. Mungkin ini dokumentasi Sphinx, atau yang serupa. Anda selalu dapat melakukan ini:
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),
]
Bagus, sekarang semua templat yang ada di direktori docs / templates akan berhasil dirender menggunakan satu penangan. Seorang pembaca yang ingin tahu akan mengatakan bahwa python tidak diperlukan sama sekali di sini, dan semua ini dapat dilakukan hanya dengan bantuan Nginx bersyarat. Saya sangat setuju, persis sampai perlu, misalnya, untuk mendistribusikan templat ini berdasarkan peran atau di suatu tempat di samping, logika tambahan tidak diperlukan.
Namun, kembali ke ruang nama
Tidak ada ORM di Crax. Dan itu tidak seharusnya. Bagaimanapun, sampai SQLAlchemy menawarkan solusi asynchronous. Namun, bekerja dengan database (Postgres, MySQL dan SQLite) dideklarasikan. Ini berarti dimungkinkan untuk menulis model Anda sendiri berdasarkan Crax BaseTable . Di balik terpal, ini adalah pembungkus yang sangat tipis di atas Tabel Inti SQLAlchemy , dan dapat melakukan semua yang dapat dilakukan Tabel Inti . Untuk apa itu mungkin dibutuhkan. Mungkin untuk melakukan sesuatu yang serupa.
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']
Dan agar dapat bekerja dengan migrasi. Migrasi Crax adalah sedikit kode di atas SQLAlchemy Alembic. Karena kita berbicara tentang ruang nama dan pemisahan logika, maka,
jelas, kita ingin menyimpan migrasi dalam paket yang sama dengan logika lain dari ruang nama ini. Beginilah cara kerja migrasi Crax. Semua migrasi akan didistribusikan sesuai dengan namespace mereka, dan jika namespace ini menyiratkan bekerja dengan database yang berbeda, maka di dalam direktori migrasi akan ada pembagian ke direktori dari database yang sesuai. Hal yang sama berlaku untuk migrasi offline - semua file * .sql akan dibagi menurut namespace dan database model. Saya tidak akan melukis di sini tentang menulis kueri - ini ada dalam dokumentasi, saya hanya akan mengatakan bahwa Anda masih bekerja dengan SQLAlchemy Core.
Sekali lagi, namespace menyiratkan penyimpanan templat yang nyaman (warisan dan fitur Jinja2 lainnya didukung + beberapa fasilitas dalam bentuk token CSRF atau pembuatan url yang sudah jadi). Artinya, semua template Anda terstruktur. Yah, tentu saja, saya tidak terjebak di tahun 2007 yang gemilang, saya memahami bahwa templat (meskipun dibuat secara asinkron) akan sedikit diminati pada tahun 2020. Dan itu, kemungkinan besar, Anda senang memisahkan logika frontend dan backend. Crax melakukan pekerjaan ini dengan sangat baik, hasilnya dapat dilihat di Github.
SiniVueJs digunakan sebagai frontend. Dan karena kami memiliki sejenis API, kami mungkin ingin membuat dokumentasi interaktif. Crax dapat membuat dokumentasi OpenAPI (Swagger) di luar kotak berdasarkan daftar rute dan dokumen penangan Anda. Semua contoh, tentu saja, ada di dokumentasi.
Sebelum kita beralih ke bagian paling menarik dari ikhtisar singkat kami, ada baiknya berbicara sedikit tentang baterai berguna mana yang sudah disediakan dengan Crax.
Secara alami, mode debug adalah ketika kesalahan dan jejak lengkap dapat dibaca langsung di browser, di halaman tempat kemalangan terjadi. Mode debug dapat dinonaktifkan dan disesuaikan dengan
Logger internal dengan kemampuan untuk secara bersamaan menulis ke file yang ditentukan dan mengirim log ke konsol (atau melakukan satu hal). Kemampuan untuk menetapkan logger Anda sendiri, bukan yang default. Sentry support dengan menambahkan dua baris ke konfigurasi (dan, jika perlu, kustomisasi).
Dua jenis middleware prainstal. Yang pertama diproses SEBELUM permintaan diproses oleh aplikasi, dan yang kedua SETELAH.
Dukungan bawaan untuk header CORS. Anda hanya perlu mendeklarasikan aturan CORS di konfigurasi.
Kemampuan untuk menentukan metode yang tersedia untuk setiap penangan langsung di situs. Setiap penangan akan bekerja dengan daftar metode HTTP yang ditentukan (+ KEPALA dan OPSI), atau hanya dengan GET, KEPALA, dan OPSI.
Kemampuan untuk menentukan bahwa penangan ini hanya tersedia untuk pengguna yang berwenang, atau hanya untuk pengguna dari grup Administrator, atau hanya untuk anggota peran superuser.
Ada otorisasi untuk sesi bertanda HMAC, yang tidak perlu masuk ke database dan sejumlah alat untuk membuat dan mengelola pengguna. Anda dapat mengaktifkan dukungan backend otorisasi dan mendapatkan pengguna prasetel dan sejumlah alat untuk digunakan. Namun, seperti kebanyakan alat Crax, Anda dapat membiarkannya, menggunakannya, dan menulisnya sendiri. Anda tidak dapat menggunakan otorisasi, database, model, migrasi, tampilan, dan sepenuhnya menulis solusi kustom Anda sendiri. Anda tidak perlu melakukan apa pun untuk melakukan ini, Anda belum menyalakannya - belum.
Ada beberapa jenis Respon dan beberapa jenis penangan Berbasis Kelas yang akan membantu Anda menulis aplikasi lebih cepat dan lebih ringkas. Dalam hal ini, keinginan Anda juga akan berfungsi, yang tidak mewarisi dari yang sudah ada.
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
Dukungan perlindungan CSRF. Menghasilkan token, memeriksa keberadaan token di badan permintaan,
menonaktifkan verifikasi untuk penangan tertentu.
Dukungan untuk perlindungan ClickJacking (Bingkai, iframe, embed ... kebijakan rendering)
Dukungan untuk memeriksa ukuran tubuh maksimum yang diizinkan dari permintaan SEBELUM aplikasi mulai memprosesnya.
Dukungan websocket asli. Mari kita ambil contoh dari dokumentasi dan tulis aplikasi sederhana yang dapat mengirim pesan websocket melalui siaran, per grup pengguna, atau pesan ke pengguna tertentu. Misalkan kita memiliki kelompok "laki-laki" dan "perempuan" (dimungkinkan untuk menambahkan kelompok "orang tua"). Kita dapat menulis sesuatu yang serupa sebagai contoh (tentu saja, ini bukan kode produk).
#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>
Kode lengkapnya bisa dilihat di dokumentasi Crax.
Nah, waktunya telah tiba untuk yang paling menarik dalam artikel ini.
Mengapa tidak perlu?
Pertama, seperti disebutkan di atas, ada beberapa kerangka kerja yang melakukan hal yang sama, dan komunitas sudah terbentuk. Sedangkan Crax adalah seorang bayi yang baru berusia satu minggu. Tentara lajang hampir menjadi jaminan bahwa cepat atau lambat proyek itu akan ditinggalkan. Ini menyedihkan, tetapi fakta bahwa bekerja di atas meja, merilis rilis dan pembaruan hanya untuk Anda sendiri dan Vasily dari Syktyvkar, jauh lebih lama daripada ketika komunitas mengerjakan proyek. Sementara itu, proyek tersebut belum memiliki sejumlah fitur yang harus dimiliki di tahun 2020. Misalnya: tidak ada dukungan JWT (JOSE). Tidak ada dukungan out-of-the-box untuk OAuth2. Tidak ada dukungan GraphQL. Jelas bahwa Anda dapat menulis ini sendiri untuk proyek Anda, tetapi Starlette atau FastAPI sudah memilikinya. Saya hanya perlu menulis ini (ya, itu ada dalam rencana). Akan ada sedikit tentang rencana sebagai kesimpulan.
Pengembang Netflix dan Microsoft menulis tentang FastAPI. Tentang Crax menulis noname, tidak diketahui di mana ia muncul, dan siapa yang tahu di mana mampu tepat lusa jurang. Mereka
tidak akan memanggil kapal uap dengan nama idiot saya.
Ibuku menangis di malam hari, karena dia melahirkan anak aneh ...
(c)
Ini penting. Ini disebut reputasi dan ekosistem. Crax juga tidak memilikinya. Tanpa hal-hal penting tersebut, proyek dijamin masuk ke TPA tanpa pernah lahir.
Itu sangat berharga untuk dipahami. Apa yang tertulis di atas bukanlah upaya untuk mengetik kelas dan bukan teks dari seorang tunawisma di kereta. Ini adalah penilaian yang bijaksana dan peringatan bahwa "solusi siap produksi" tidak hanya merupakan hasil dari cakupan kode sumber melalui pengujian, tetapi juga penilaian umum tentang kematangan teknologi, pendekatan, dan solusi yang digunakan dalam proyek.
Jika Anda baru saja mulai mengenal Python dan mencoba kerangka kerja, Anda berada dalam bahaya: Kemungkinan besar, Anda tidak akan menemukan jawaban atas pertanyaan di SO, mungkin rekan yang lebih berpengalaman akan membantu Anda, yang sayangnya mungkin tidak ada di sana.
Tujuan
Hal pertama yang saya rencanakan adalah, tentu saja, menambahkan beberapa hal yang harus dimiliki seperti JWT (JOSE), OAuth2 dan dukungan GraphQL. Inilah yang akan memudahkan saya dan orang-orang yang tertarik untuk bekerja. Dan inilah, sebenarnya, tujuan utama Crax - membuat pekerjaan seseorang sedikit lebih mudah. Mungkin pada saat itu babak baru di TechEmpower akan dimulai dan tolok ukurnya akan menjadi lebih jelas. Bahkan tidak menutup kemungkinan setelah ini akan ada kepentingan tertentu di masyarakat.
Ada ide untuk menulis CMS berdasarkan Crax.
Jika saya tidak salah (jika saya salah - perbaiki), kami belum memiliki CMS asinkron dengan Python di toolkit kami. Saya mungkin berubah pikiran dan memutuskan untuk menulis semacam solusi e-commerce. Tapi, jelas, untuk mencegah Crax tenggelam sebelum mencapai pelampung, sesuatu yang menarik perlu dilakukan di dasarnya. Mungkin para peminat akan tertarik dengan ini. Penggemar saat itu gratis. Karena tidak ada uang di sini dan kemungkinan besar tidak akan ada. Crax benar-benar gratis untuk semua orang dan saya tidak mendapat sepeser pun untuk pekerjaan ini. Dengan demikian, pembangunan direncanakan untuk "malam musim dingin yang panjang" dan, mungkin, di tahun mendatang, sesuatu yang menarik akan lahir.
Kesimpulan
Saya sedang memikirkan tentang grup mana yang akan menyertakan artikel ini (omong-omong, ini adalah publikasi pertama saya tentang sumber daya). Mungkin layak untuk menempatkannya di bawah tag "Saya PR". Apa yang membuat saya berubah pikiran: pertama-tama, fakta bahwa iklan itu tidak memiliki karakter iklan apa pun.
Tidak ada panggilan "Anak laki-laki, segera daftar untuk permintaan tarik . " Tidak ada ide untuk mencari sponsor di sini. Bahkan tidak ada ide bahwa saya membawakan Anda sesuatu yang belum pernah Anda lihat (tentu saja, lihat). Anda dapat mengabstraksikan gagasan bahwa saya adalah penulis kedua artikel dan alat ini, dan memahami apa yang telah ditulis sebagai tinjauan. Dan, ya, itulah cara terbaik. Ini akan menjadi hasil yang sangat baik bagi saya jika Anda terus mengingatnya.
Dalam hal ini, mungkin, semuanya.
โJadiโฆ saatnya mengambil pancing.
- Kenapa?
- Topi merah Harris membuat takut semua ikan.
(c)
Kode pada Dokumentasi GitHub