الموارد والترقيم الصفحي¶
المورد (resource) يجمّع نقاط النهاية الخاصة بنوع واحد من السجلّات (المستخدمون، الطلبات،
…) ويربطها بعميل. صرّح بـ path وmodel اختياري، فتحصل على عمليات CRUD الخمس و الترقيم
الصفحي الكسول مجّانًا — مع بقاء قدرتك على إضافة نقاط نهاية مخصّصة بالمزخرفات المعتادة.
from pydantic import BaseModel
from asas import AsasClient, AsasResource
class User(BaseModel):
id: int
name: str
class Users(AsasResource):
path = "/users"
model = User
class MyClient(AsasClient):
users = Users() # اربط المورد بالعميل
client = MyClient(base_url="https://api.example.com")
client.users.get(1) # -> User(id=1, name="...")
يُربَط المورد بعميله عند الوصول إليه، فيتشارك تلقائيًا base_url وengine وauth الخاصة
بالعميل — بما في ذلك تجديد الرمز عند 401.
عمليات CRUD المُولَّدة¶
مع ضبط path وmodel، يكشف المورد المتزامن (AsasResource) عن:
| الدالة | الطلب | تُعيد |
|---|---|---|
list(**params) |
GET /users |
list[model] (صفحة واحدة) |
get(id) |
GET /users/{id} |
model |
create(obj) |
POST /users |
model |
update(id, obj) |
PUT /users/{id} |
model |
delete(id) |
DELETE /users/{id} |
Response |
client.users.list(active=True) # GET /users?active=True
user = client.users.create(User(id=0, name="Ada"))
user = client.users.update(user.id, user)
client.users.delete(user.id)
تقبل create وupdate نموذج Pydantic (يُرسَل بوصفه جسم JSON)؛ ويُتحقَّق من الاستجابات
وتُحوَّل إلى model. وإذا حذفت model، تعود النتائج بصيغة JSON خام.
path مطلوب لـ CRUD
استدعاء دالة CRUD أو التكرار دون path يرفع ValueError واضحًا. أما المورد الذي يحوي
دوالًا مزخرفة مخصّصة فقط فلا يحتاج إليه.
نقاط النهاية المخصّصة¶
أي شيء خارج CRUD العادي ما هو إلا دالة مزخرفة على الفئة نفسها — تمامًا كما على العميل (راجع التوجيه والوسائط):
from typing import List
class Users(AsasResource):
path = "/users"
model = User
@get("/users/{id}/roles", response_model=List[Role])
def roles(self, roles: List[Role], id: int) -> List[Role]:
return roles
client.users.roles(id=1) # GET /users/1/roles
التكرار الكسول على كل السجلّات¶
يمرّ تكرار المورد على كل الصفحات، ويجلب كلّ صفحة عند الطلب — فلا تكتب حلقة الصفحات بنفسك أبدًا:
وهو كسول: تُطلَب الصفحات فقط بقدر ما تستهلك من سجلّات، لذا فإن التوقّف مبكّرًا يوقف الجلب أيضًا.
# تُطلَب فقط الصفحات اللازمة لأول 10 سجلّات.
first_ten = [u for _, u in zip(range(10), client.users)]
استخدم iterate(**params) لتمرير وسائط استعلام إلى التكرار:
أنماط الترقيم الصفحي¶
اضبط paginator على المورد ليطابق طريقة ترقيم واجهتك. الافتراضي هو الترقيم برقم الصفحة؛ وكل
الأنماط تتشارك الحلقة الكسولة نفسها.
أين تقع السجلّات في الصفحة¶
يعثر كل مرقِّم على قائمة السجلّات في الصفحة تلقائيًا: تُستخدم مصفوفة JSON عُليا كما هي، وإلّا
فأوّل ما يوجد من data / items / results. تجاوز ذلك بـ items_key (مسار منقّط) عندما
تضعها واجهتك في مكان آخر متداخل:
الاستراتيجيات المخصّصة¶
لدعم نمط خاص، ورِث Paginator وطبّق first_page وnext_page، مُعيدًا PageRequest (أو
None للتوقّف):
from asas import Paginator, PageRequest
from asas.core.models import Response
class HeaderCountPaginator(Paginator):
def first_page(self, params):
page = dict(params); page["page"] = 1
return PageRequest(params=page)
def next_page(self, response: Response, previous: PageRequest):
total = int(response.headers.get("X-Total-Pages", "1"))
current = int(previous.params["page"])
if current >= total:
return None
page = dict(previous.params); page["page"] = current + 1
return PageRequest(params=page)
الموارد غير المتزامنة¶
يعكس AsasAsyncResource المورد المتزامن من أجل AsasAsyncClient. دوال CRUD قابلة للانتظار
بـ await، والتكرار يستخدم async for:
from asas import AsasAsyncClient, AsasAsyncResource
class Users(AsasAsyncResource):
path = "/users"
model = User
class MyClient(AsasAsyncClient):
users = Users()
async def main():
client = MyClient(base_url="https://api.example.com")
user = await client.users.get(1)
async for user in client.users: # كسول، صفحةً صفحة
print(user.name)
await client.engine.aclose()