* feat(mxroute_relay): add mxroute_relay app with initial files and models
* feat(mxroute_relay): add server routes and database initialization * feat(mxroute_relay): add admin views for User, Post, Comment, and Dump models * feat(mxroute_relay): add custom ModelView class with pagination and export options * feat(mxroute_relay): add PostView with fields and exclusions * feat(mxroute_relay): add CommentView with exclusions and search/sort options * feat(mxroute_relay): add DumpView with fields and exclusions
This commit is contained in:
parent
ed16c81304
commit
7c45b422e6
3 changed files with 182 additions and 0 deletions
45
mxroute_relay/app.py
Normal file
45
mxroute_relay/app.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from sqlalchemy import create_engine
|
||||
from sqlmodel import SQLModel
|
||||
from starlette.applications import Starlette
|
||||
from starlette.responses import HTMLResponse
|
||||
from starlette.routing import Route
|
||||
from starlette_admin.contrib.sqlmodel import Admin
|
||||
from starlette_admin.contrib.sqlmodel import ModelView
|
||||
|
||||
from .config import ENGINE_URI
|
||||
from .models import Comment
|
||||
from .models import Dump
|
||||
from .models import Post
|
||||
from .models import User
|
||||
from .views import CommentView
|
||||
from .views import DumpView
|
||||
from .views import PostView
|
||||
|
||||
engine = create_engine(ENGINE_URI, connect_args={"check_same_thread": False}, echo=True)
|
||||
|
||||
|
||||
def init_database() -> None:
|
||||
SQLModel.metadata.create_all(engine)
|
||||
|
||||
|
||||
app = Starlette(
|
||||
routes=[
|
||||
Route(
|
||||
"/",
|
||||
lambda r: HTMLResponse('<a href="/admin/">Click me to get to Admin!</a>'),
|
||||
)
|
||||
],
|
||||
on_startup=[init_database],
|
||||
)
|
||||
|
||||
# Create admin
|
||||
admin = Admin(engine, title="Example: SQLModel")
|
||||
|
||||
# Add views
|
||||
admin.add_view(ModelView(User, icon="fa fa-users"))
|
||||
admin.add_view(PostView(Post, label="Blog Posts", icon="fa fa-blog"))
|
||||
admin.add_view(CommentView(Comment, icon="fa fa-comments"))
|
||||
admin.add_view(DumpView(Dump, icon="fa fa-dumpster"))
|
||||
|
||||
# Mount to admin to app
|
||||
admin.mount_to(app)
|
75
mxroute_relay/models.py
Normal file
75
mxroute_relay/models.py
Normal file
|
@ -0,0 +1,75 @@
|
|||
import enum
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import AnyHttpUrl
|
||||
from pydantic import BaseModel
|
||||
from pydantic import EmailStr
|
||||
from pydantic import Field as PydField
|
||||
from pydantic.color import Color
|
||||
from sqlalchemy import Column
|
||||
from sqlalchemy import DateTime
|
||||
from sqlalchemy import Enum
|
||||
from sqlalchemy import JSON
|
||||
from sqlalchemy import String
|
||||
from sqlalchemy import Text
|
||||
from sqlmodel import Field
|
||||
from sqlmodel import Relationship
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class Gender(str, enum.Enum):
|
||||
MALE = "male"
|
||||
FEMALE = "female"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
id: Optional[int] = Field(primary_key=True)
|
||||
full_name: str = Field(min_length=3, index=True)
|
||||
sex: Optional[str] = Field(sa_column=Column(Enum(Gender)), default=Gender.UNKNOWN, index=True)
|
||||
|
||||
posts: List["Post"] = Relationship(back_populates="publisher")
|
||||
comments: List["Comment"] = Relationship(back_populates="user")
|
||||
|
||||
|
||||
class Post(SQLModel, table=True):
|
||||
id: Optional[int] = Field(primary_key=True)
|
||||
title: str = Field(min_length=3)
|
||||
content: str = Field(sa_column=Column(Text))
|
||||
tags: List[str] = Field(sa_column=Column(JSON), min_items=1)
|
||||
published_at: Optional[datetime] = Field(sa_column=Column(DateTime(timezone=True), default=datetime.utcnow))
|
||||
|
||||
publisher_id: Optional[int] = Field(foreign_key="user.id")
|
||||
publisher: User = Relationship(back_populates="posts")
|
||||
|
||||
comments: List["Comment"] = Relationship(back_populates="post")
|
||||
|
||||
|
||||
class Comment(SQLModel, table=True):
|
||||
pk: Optional[int] = Field(primary_key=True)
|
||||
content: str = Field(sa_column=Column(Text), min_length=5)
|
||||
created_at: Optional[datetime] = Field(sa_column=Column(DateTime(timezone=True), default=datetime.utcnow))
|
||||
|
||||
post_id: Optional[int] = Field(foreign_key="post.id")
|
||||
post: Post = Relationship(back_populates="comments")
|
||||
|
||||
user_id: Optional[int] = Field(foreign_key="user.id")
|
||||
user: User = Relationship(back_populates="comments")
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
key: str = PydField(min_length=3)
|
||||
value: int = PydField(multiple_of=5)
|
||||
|
||||
|
||||
class Dump(SQLModel, table=True):
|
||||
id: Optional[int] = Field(primary_key=True)
|
||||
email: EmailStr = Field(index=True)
|
||||
color: Color = Field(sa_column=Column(String(10)))
|
||||
url: AnyHttpUrl
|
||||
json_field: Dict[str, Any] = Field(sa_column=Column(JSON))
|
||||
configs: List[Config] = Field(sa_column=Column(JSON))
|
62
mxroute_relay/views.py
Normal file
62
mxroute_relay/views.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
from starlette_admin import CollectionField
|
||||
from starlette_admin import ColorField
|
||||
from starlette_admin import EmailField
|
||||
from starlette_admin import ExportType
|
||||
from starlette_admin import IntegerField
|
||||
from starlette_admin import JSONField
|
||||
from starlette_admin import ListField
|
||||
from starlette_admin import StringField
|
||||
from starlette_admin import URLField
|
||||
from starlette_admin.contrib.sqlmodel import ModelView
|
||||
|
||||
from .models import Comment
|
||||
from .models import Post
|
||||
|
||||
|
||||
class MyModelView(ModelView):
|
||||
page_size = 5
|
||||
page_size_options = [5, 10, 25 - 1]
|
||||
export_types = [ExportType.EXCEL, ExportType.CSV]
|
||||
|
||||
|
||||
class PostView(MyModelView):
|
||||
fields = [
|
||||
"id",
|
||||
"title",
|
||||
"content",
|
||||
ListField(StringField("tags")),
|
||||
"published_at",
|
||||
"publisher",
|
||||
"comments",
|
||||
]
|
||||
exclude_fields_from_list = [Post.content]
|
||||
exclude_fields_from_create = [Post.published_at]
|
||||
exclude_fields_from_edit = ["published_at"]
|
||||
|
||||
|
||||
class CommentView(MyModelView):
|
||||
exclude_fields_from_create = ["created_at"]
|
||||
exclude_fields_from_edit = ["created_at"]
|
||||
searchable_fields = [Comment.content, Comment.created_at]
|
||||
sortable_fields = [Comment.pk, Comment.content, Comment.created_at]
|
||||
|
||||
|
||||
class DumpView(MyModelView):
|
||||
fields = [
|
||||
"id",
|
||||
EmailField("email"),
|
||||
URLField("url", required=True),
|
||||
ColorField("color"),
|
||||
JSONField("json_field"),
|
||||
ListField(
|
||||
CollectionField(
|
||||
"configs",
|
||||
fields=[
|
||||
StringField("key"),
|
||||
IntegerField("value", help_text="multiple of 5"),
|
||||
],
|
||||
)
|
||||
),
|
||||
]
|
||||
exclude_fields_from_list = ("configs",)
|
||||
searchable_fields = ("email", "url")
|
Loading…
Reference in a new issue