पर्यवेक्षित मशीन अनुवाद
यह सामग्री मानव-निर्मित शब्दावलियों और शैली गाइडों के मार्गदर्शन में मशीन जनरेशन द्वारा अनुवादित की गई है। चूँकि इस पाठ की समीक्षा लाइन-दर-लाइन मैन्युअल रूप से नहीं की गई है, इसलिए कभी-कभी त्रुटियाँ या अनाड़ी वाक्य-रचना हो सकती है।
किसी भी विसंगति की स्थिति में, मूल अंग्रेज़ी संस्करण ही प्रामाणिक स्रोत है।
Inline Forms
Inline forms users को parent model के create या edit page से ही related records manage करने देते हैं। ये उन child models के लिए उपयुक्त हैं जो केवल अपने parent के साथ ही meaningful होते हैं — जैसे article पर comments या project में tasks — और child model के लिए अलग admin view बनाने से आपको बचाते हैं।
इस पेज के तीनों patterns — auto-detected foreign key, explicit foreign key, और composite foreign key — cover करने वाले runnable app के लिए examples/06-inline-forms देखें।
एक minimal inline
from sqlalchemy import ForeignKey, Integer, String, Text
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from starlette.requests import Request
from starlette_admin.contrib.sqla import InlineModelView, ModelView
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = "articles"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
title: Mapped[str] = mapped_column(String(200))
body: Mapped[str] = mapped_column(Text, default="")
comments: Mapped[list["Comment"]] = relationship(
"Comment", back_populates="article", cascade="all, delete-orphan"
)
async def __admin_repr__(self, request: Request) -> str:
return self.title
class Comment(Base):
__tablename__ = "comments"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
article_id: Mapped[int] = mapped_column(Integer, ForeignKey("articles.id"))
author: Mapped[str] = mapped_column(String(100), default="Anonymous")
body: Mapped[str] = mapped_column(Text)
article: Mapped["Article"] = relationship("Article", back_populates="comments")
async def __admin_repr__(self, request: Request) -> str:
return f"{self.author}: {self.body[:50]}"
class CommentInline(InlineModelView):
model = Comment
fields = ["author", "body"]
extra = 1
class ArticleView(ModelView):
fields = ["title", "body"]
inlines = [CommentInline]
यह setup दो steps लेता है: child model के लिए InlineModelView subclass define करें, फिर उसे parent के ModelView की inlines list में जोड़ें।
ArticleView के create/edit pages अब article के अपने fields के नीचे एक Comments formset render करते हैं। Formset एक empty row (extra = 1) से शुरू होता है और add/delete controls include करता है जिन्हें SQLAlchemy backend आपके लिए wire up करता है।
ध्यान दें कि CommentInline ने कभी fk_attr सेट नहीं किया। SQLAlchemy backend Article.comments inspect करके Comment.article_id को foreign key infer कर लेता है, क्योंकि Comment की ओर इशारा करने वाला वही अकेला relationship है। fk_attr स्वयं तभी सेट करें जब inference अस्पष्ट (ambiguous) हो, या ORM model पर relationship declared न हो। Explicit and composite foreign keys देखें।
InlineModelView संदर्भ
| Attribute | Type | Default | Description |
|---|---|---|---|
model |
ORM model class | None |
The related model this inline manages. Required. |
fk_attr |
str \| tuple[str, ...] |
"" |
Name of the foreign-key field on the inline model that points to the parent. A tuple declares a composite foreign key. Optional on the SQLAlchemy backend, which auto-detects it from the parent's relationship when you omit it. |
extra |
int |
0 |
Number of empty rows shown on create and edit forms, in addition to existing rows. |
allow_delete |
bool |
True |
Show a delete checkbox or button on each existing row. |
inline_template |
str |
"inline.html" |
Template used to render the formset. |
collapsible |
bool |
True |
Whether users can expand and collapse the formset. |
collapsed |
bool |
False |
Initial collapsed state. Applies only when collapsible=True. |
जब आप fk_attr खाली छोड़ें और backend relationship को unambiguously resolve न कर सके, constructor ValueError raise करता है।
Collapsible formsets
डिफ़ॉल्ट रूप से (collapsible = True) हर InlineModelView अपना formset ऐसे header के साथ render करता है जिसे users select करके अनचाहे child records collapse कर सकते हैं। Formset open के बजाय closed शुरू कराने के लिए collapsed = True सेट करें:
class CommentInline(InlineModelView):
model = Comment
fields = ["author", "body"]
extra = 1
collapsed = True
Formset को entirely opt-out करने के लिए collapsible = False सेट करें — तब वह हमेशा expanded render होगा, toggle के बिना:
class CommentInline(InlineModelView):
model = Comment
fields = ["author", "body"]
extra = 1
collapsible = False
Explicit और composite foreign keys
जब parent का उसी child model से एक से अधिक relationships हों, जब ORM model पर relationship declared न हो, या जब foreign key composite हो, तब fk_attr स्वयं सेट करें:
from sqlalchemy import ForeignKey, ForeignKeyConstraint, Integer, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from starlette.requests import Request
from starlette_admin import StringField
from starlette_admin.contrib.sqla import InlineModelView, ModelView
class Base(DeclarativeBase):
pass
class Project(Base):
__tablename__ = "projects"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(200))
tasks: Mapped[list["Task"]] = relationship(
"Task", back_populates="project", cascade="all, delete-orphan"
)
async def __admin_repr__(self, request: Request) -> str:
return self.name
class Task(Base):
__tablename__ = "tasks"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
project_id: Mapped[int] = mapped_column(Integer, ForeignKey("projects.id"))
title: Mapped[str] = mapped_column(String(200))
done: Mapped[bool] = mapped_column(default=False)
project: Mapped["Project"] = relationship("Project", back_populates="tasks")
async def __admin_repr__(self, request: Request) -> str:
return self.title
class TaskInline(InlineModelView):
model = Task
fk_attr = "project_id"
fields = ["title", "done"]
extra = 2
class ProjectView(ModelView):
fields = [StringField("name")]
inlines = [TaskInline]
class Order(Base):
__tablename__ = "orders"
store_id: Mapped[int] = mapped_column(Integer, primary_key=True)
seq: Mapped[int] = mapped_column(Integer, primary_key=True)
customer: Mapped[str] = mapped_column(String(100))
lines: Mapped[list["OrderLine"]] = relationship(
"OrderLine", back_populates="order", cascade="all, delete-orphan"
)
async def __admin_repr__(self, request: Request) -> str:
return f"Order #{self.store_id}-{self.seq} ({self.customer})"
class OrderLine(Base):
__tablename__ = "order_lines"
order_store_id: Mapped[int] = mapped_column(Integer, primary_key=True)
order_seq: Mapped[int] = mapped_column(Integer, primary_key=True)
line_no: Mapped[int] = mapped_column(Integer, primary_key=True)
product: Mapped[str] = mapped_column(String(100))
qty: Mapped[int] = mapped_column(Integer, default=1)
order: Mapped["Order"] = relationship("Order", back_populates="lines")
__table_args__ = (
ForeignKeyConstraint(
["order_store_id", "order_seq"],
["orders.store_id", "orders.seq"],
),
)
async def __admin_repr__(self, request: Request) -> str:
return f"Line {self.line_no}: {self.product} x {self.qty}"
class OrderLineInline(InlineModelView):
model = OrderLine
fields = ["line_no", "product", "qty"]
extra = 1
class OrderView(ModelView):
fields = ["store_id", "seq", "customer"]
inlines = [OrderLineInline]
ध्यान दें कि OrderLineInline को fk_attr की आवश्यकता नहीं है, भले हि OrderLine की primary key composite (order_store_id, order_seq, line_no) है। SQLAlchemy backend Order और OrderLine के बीच ForeignKeyConstraint से composite foreign key resolve करके नई rows पर दोनों columns populate करता है। fk_attr को tuple[str, ...] तभी पास करें जब constraint introspection कोई match न पाए।
Validation
प्रत्येक submitted row अपने आप validate होती है — standalone ModelView वहीं create/edit paths से। Admin पहले parent save करता है, फिर हर inline row क्रमवार process करता है। एक comment के author field में typo बाकी comments को process होने से नहीं रोकता। जब किसी row की validation fail होती है, errors उस row से attach होते हैं, और form submitted values के साथ उस row को वहीं re-render करता है ताकि users उस entry को ठीक करके फिर submit कर सकें।
Important
SQLAlchemy backend पर पूरा request all-or-nothing होता है। Parent और हर inline row उसी request-scoped session को share करते हैं, और वह session तभी commit होता है जब पूरा request succeed करे। यदि किसी row की validation fail होती है, response error return करता है और session rollback हो जाता है — parent और सारे inline rows (validation pass करने वाली rows सहित) एक साथ revert हो जाते हैं। UI के per-row errors को ठीक करने की list समझें, saved हुए का record नहीं।
आगे क्या
- SQLAlchemy: Relationship introspection automatic foreign key detection कैसे सक्षम करती है।
- Custom Views: Standard create/edit/list workflow से आगे के pages बनाएँ।
- इवेंट: Records save होने के बाद inline बदलावों पर प्रतिक्रिया दें।