-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from Studio-Yandex-Practicum/admin-panel
Add an admin panel
- Loading branch information
Showing
12 changed files
with
246 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,3 +160,5 @@ cython_debug/ | |
.idea/ | ||
idea.py | ||
*/.idea | ||
|
||
.isort.cfg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,17 @@ | ||
import os | ||
from dataclasses import dataclass | ||
|
||
from dotenv import load_dotenv | ||
|
||
|
||
load_dotenv() | ||
|
||
|
||
@dataclass | ||
class Settings: | ||
"""Настройки проекта.""" | ||
|
||
db_url: str = os.getenv("DB_URL") or "sqlite+aiosqlite:///sqlite.db" | ||
app_title = "API для админки проекта 'Созидатели'." | ||
app_title = "API для проекта 'Созидатели'." | ||
admin_panel_user = os.getenv("ADMIN_PANEL_USER") | ||
admin_panel_password = os.getenv("ADMIN_PANEL_PASSWORD") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from enum import Enum | ||
|
||
|
||
class AssistanceSegment(Enum): | ||
children_in_hospital = "Детям в больницах" | ||
children_in_orphanages = "Детям в детских домах" | ||
disabled_children = "Семьям с детьми-инвалидами" | ||
auto_volunteer = "Могу автоволонтерить" | ||
not_decide = "Еще не определился" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,29 @@ | ||
from sqlalchemy import ForeignKey, String | ||
from fastapi import Request | ||
from sqlalchemy import Enum, ForeignKey, String | ||
from sqlalchemy.orm import Mapped, mapped_column, relationship | ||
|
||
from src.domain.schemas import GetUser | ||
from src.infrastructure.db import Base | ||
|
||
from .assistance import AssistanceSegment | ||
|
||
|
||
class User(Base): | ||
"""Модель пользователя.""" | ||
|
||
name: Mapped[str] = mapped_column(String(255), nullable=False) | ||
phone: Mapped[str] = mapped_column(unique=True) | ||
email: Mapped[str] = mapped_column(unique=True, nullable=False) | ||
assistance_segment: Mapped[AssistanceSegment] = mapped_column( | ||
Enum(AssistanceSegment), default=AssistanceSegment.not_decide | ||
) | ||
meeting_id: Mapped[int] = mapped_column(ForeignKey("meeting.id")) | ||
meeting = relationship("Meeting", back_populates="users") | ||
|
||
def to_read_model(self) -> GetUser: | ||
attrs = self.__dict__.copy() | ||
attrs.pop("_sa_instance_state", None) | ||
return GetUser(**attrs) | ||
|
||
async def __admin_repr__(self, request: Request): | ||
return f"{self.name}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
from typing import Any, Dict | ||
|
||
from starlette.middleware import Middleware | ||
from starlette.middleware.sessions import SessionMiddleware | ||
from starlette.requests import Request | ||
from starlette_admin import ( | ||
BooleanField, | ||
DateTimeField, | ||
EmailField, | ||
EnumField, | ||
HasMany, | ||
HasOne, | ||
PhoneField, | ||
StringField, | ||
TextAreaField, | ||
) | ||
from starlette_admin.contrib.sqla import Admin | ||
from starlette_admin.contrib.sqla.ext.pydantic import ModelView | ||
from starlette_admin.exceptions import FormValidationError | ||
from starlette_admin.i18n import I18nConfig | ||
|
||
from src.domain.models import Meeting, User | ||
from src.domain.models.assistance import AssistanceSegment | ||
from src.domain.schemas import MeetingCreate, UserCreate | ||
|
||
from .db import engine | ||
from .provider import UsernameAndPasswordProvider | ||
|
||
|
||
class UserView(ModelView): | ||
"""Модель отображения пользователя в админке.""" | ||
|
||
identity = "user" | ||
|
||
fields = [ | ||
StringField("name", label="Имя", required=True), | ||
PhoneField("phone", label="Номер телефона", required=True), | ||
EmailField("email", required=True), | ||
EnumField( | ||
"assistance_segment", | ||
label="Направление помощи", | ||
enum=AssistanceSegment, | ||
), | ||
HasOne("meeting", label="Собрание", identity="meeting", required=True), | ||
] | ||
label = "Участники" | ||
sortable_fields = [User.meeting] | ||
|
||
async def validate(self, request: Request, data: Dict[str, Any]) -> None: | ||
if data["meeting"] is None: | ||
raise FormValidationError( | ||
{"meeting": "Надо выбрать дату собрания."} | ||
) | ||
meeting = data.pop("meeting") | ||
data["meeting_id"] = meeting.id | ||
await super().validate(request, data) | ||
if assistance_segment_value := data.get("assistance_segment"): | ||
assistance_segment_key = AssistanceSegment( | ||
assistance_segment_value | ||
).name | ||
data["assistance_segment"] = assistance_segment_key | ||
data.pop("meeting_id") | ||
data["meeting"] = meeting | ||
|
||
|
||
class MeetingView(ModelView): | ||
"""Модель для отображения собраний в админке.""" | ||
|
||
identity = "meeting" | ||
|
||
fields = [ | ||
DateTimeField("date", label="Дата и время"), | ||
BooleanField("is_open", label="Закрыто/Открыто"), | ||
TextAreaField("description", label="Описание собрания"), | ||
HasMany("users", label="Участники собрания", identity="user"), | ||
] | ||
label = "Собрания" | ||
sortable_fields = ["date", "is_open"] | ||
fields_default_sort = ["date", ("is_open", True)] | ||
|
||
|
||
admin = Admin( | ||
engine, | ||
title="Проект 'Созидатели'", | ||
i18n_config=I18nConfig(default_locale="ru"), | ||
middlewares=[Middleware(SessionMiddleware, secret_key="1234567890")], | ||
auth_provider=UsernameAndPasswordProvider(), | ||
) | ||
|
||
admin.add_view(UserView(User, pydantic_model=UserCreate, icon="fa fa-user")) | ||
|
||
admin.add_view( | ||
MeetingView(Meeting, pydantic_model=MeetingCreate, icon="fa fa-calendar") | ||
) |
Oops, something went wrong.