Code Monkey home page Code Monkey logo

g6's Issues

templates/basic/moible/board 디렉토리 없을때 오류

관리자 > 게시판 관리

File "admin/templates/board_list.html", line 1, in top-level template code
    {% extends "base.html" %}
  File "admin/templates/base.html", line 175, in top-level template code
    {% block content %}{% endblock %}
  File "admin/templates/board_list.html", line 71, in block 'content'
    {{ get_skin_select('board', 'bo_mobile_skin[]', board.bo_mobile_skin, event='required', device='mobile')|safe }}
  File "/home/ubuntu/gnu6/lib/common.py", line 182, in get_skin_select
    for skin in os.listdir(skin_path):
FileNotFoundError: [Errno 2] No such file or directory: 'templates/basic/mobile/board'

게시판 글 읽기시 오류

master 를 새로 pull 한 후 새로 설치하여 테스트 해보세요.

Traceback (most recent call last):
  File "/home/ubuntu/.local/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/home/ubuntu/.local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/applications.py", line 292, in __call__
    await super().__call__(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/core/middleware.py", line 68, in core_middleware
    return await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/core/middleware.py", line 101, in dispatch
    return await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/main.py", line 211, in main_middleware
    response: Response = await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/routing.py", line 273, in app
    raw_response = await run_endpoint_function(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
  File "/home/ubuntu/gnu6/bbs/board.py", line 867, in read_post
    insert_point(request, mb_id, read_point, f"{board.bo_subject} {write.wr_id} 글읽기", board.bo_table, write.wr_id, "읽기")
  File "/home/ubuntu/gnu6/lib/common.py", line 880, in insert_point
    db.commit()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1969, in commit
    trans.commit(_to_root=True)
  File "<string>", line 2, in commit
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/state_changes.py", line 139, in _go
    ret_value = fn(self, *arg, **kw)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1256, in commit
    self._prepare_impl()
  File "<string>", line 2, in _prepare_impl
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/state_changes.py", line 139, in _go
    ret_value = fn(self, *arg, **kw)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1231, in _prepare_impl
    self.session.flush()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4312, in flush
    self._flush(objects)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4447, in _flush
    with util.safe_reraise():
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4408, in _flush
    flush_context.execute()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/unitofwork.py", line 466, in execute
    rec.execute(self)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/unitofwork.py", line 642, in execute
    util.preloaded.orm_persistence.save_obj(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/persistence.py", line 93, in save_obj
    _emit_insert_statements(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/persistence.py", line 1226, in _emit_insert_statements
    result = connection.execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1416, in execute
    return meth(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 516, in _execute_on_connection
    return connection._execute_clauseelement(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_clauseelement
    ret = self._execute_context(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1820, in _execute_context
    self._handle_dbapi_exception(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 2343, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1814, in _execute_context
    context = constructor(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 1455, in _init_compiled
    l_param: List[Any] = [
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 1456, in <listcomp>
    flattened_processors[key](compiled_params[key])
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/base.py", line 1142, in process
    raise TypeError(
sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite Date type only accepts Python date objects as input.
[SQL: INSERT INTO g6_point (mb_id, po_datetime, po_content, po_point, po_use_point, po_expired, po_expire_date, po_mb_point, po_rel_table, po_rel_id, po_rel_action) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]
[parameters: [{'po_mb_point': 219, 'po_content': '질문답변 1 글읽기', 'po_rel_id': '1', 'po_rel_table': 'qa', 'po_datetime': datetime.datetime(2024, 1, 3, 15, 44, 48, 225496), 'po_expire_date': '2024-01-03', 'po_use_point': 0, 'po_rel_action': '읽기', 'mb_id': 'admin', 'po_point': -1, 'po_expired': 1}]]

dataclasses 반영

dataclassform.py 에 폼 관련 class 생성하세요.
_admin/admin_config.py 반영 완료 (참고하세요)

url_for() 함수가 https가 아닌 http를 반환하는 현상

원인

  • uvicorn 실행 시, HTTP로 실행되어 FastAPI에서 사용하는 base_url이 http로 설정됨.

테스트

1. uvicorn 실행 시, HTTPS로 실행

  • 간단하고 근본적인 해결책
  • 서버 실행 시, 명령어가 길어져 사용성이 떨어지므로 프로그램 방식으로 실행하는 방법을 테스트 해봄
명령어 방식
  • uvicorn main:app --reload --ssl-keyfile=./key.pem --ssl-certfile=./cert.pem
프로그램 방식
  • python deployment.py
  • 실행 코드
# deployment.py
import uvicorn

if __name__ == "__main__":
    uvicorn.run("main:app", port=8000, reload=True, ssl_keyfile="key.pem", ssl_certfile="cert.pem")
실행결과

image

2. url_for 함수 사용시 https로 강제 변경

  • 근본적인 해결책이 되지 않으므로 지양해야할 방법이라고 판단된다.
Jinja2 Template > url_for 함수 테스트 코드
  • 아래 코드는 template에서 사용하는 url_for만 변경한다. python 파일 > request.url_for은 별개의 처리가 필요하다..
try:
    import jinja2
    if hasattr(jinja2, "pass_context"):
        pass_context = jinja2.pass_context
    else:  # pragma: nocover
        pass_context = jinja2.contextfunction  # type: ignore[attr-defined]
except ImportError:  # pragma: nocover
    jinja2 = None  # type: ignore


@pass_context
def url_for(context: dict, name: str, **path_params) -> str:
    request = context["request"]
    http_url = request.url_for(name, **path_params)
    if request.url.scheme == 'https' or "x-forwarded-for" in request.headers.keys():
        return http_url.__str__().replace("http", "https", 1)
    else:
        return http_url

class MyTemplates(Jinja2Templates):
    """
    Jinja2Template 설정 클래스
    """
    def __init__(self,
                 directory: Union[str, os.PathLike],
                 context_processors: dict = None,
                 globals: dict = None,
                 env: Environment = None
                 ):
        super().__init__(directory=directory, context_processors=context_processors)
        # 공통 env.global 설정
        self.env.globals["editor_path"] = editor_path
        self.env.globals["generate_token"] = generate_token
        self.env.globals["getattr"] = getattr
        self.env.globals["get_selected"] = get_selected
        self.env.globals["get_member_icon"] = get_member_icon
        self.env.globals["get_member_image"] = get_member_image
        self.env.globals["url_for"] = url_for

...

결론

  • 테스트 1번과 같은 방식으로 해결
  • 서버 실행을 명령어 방식으로 할 것인가 프로그램 방식으로 할 것인가 정해야 한다.

member 테이블 mb_leave_date 컬럼 제약조건 관련 에러

문제 상황

회원 탈퇴 시 백엔드를 통해 g6_member 테이블의 mb_leave_date에 입력되는 값은 '2023-12-21'와 같이 10 글자임 (코드 참고)

# bbs/member_leave.py

@router.post("/member_leave", dependencies=[Depends(validate_token)])
async def member_leave(
    ...
    leave_date = datetime.now().strftime("%Y-%m-%d")     # 10 글자로 생성 ex) 2023-12-22
    memo = f"{login_member.mb_memo}\n{leave_date}탈퇴함"
    db.execute(
        update(Member)
        .values(mb_leave_date=leave_date, mb_memo=memo)  # 8글자 제약조건의 mb_leave_date에 10글자 leave_date 값 입력
        .where(Member.mb_id == login_member.mb_id)
    )

컬럼 제약조건은 varchar(8)로 8글자라서 아래와 같은 오류 발생

 sqlalchemy.exc.DataError: (pymysql.err.DataError) (1406, "Data too long for column 'mb_leave_date' at row 1")

수정시 고려 사항

그누보드 5 DB도 해당 컬럼의 제약조건이 varchar(8)이므로 호환성을 고려하여 수정해야 할 것으로 보임

cheditor4/config.js 의 dataFeed()

사용하지 않는 함수라면 삭제해 주세요.

function dataFeed( opts, callback ) {
    var data = [];

    if (opts.marker == "@" && opts.query) {
        
        var thisVal = opts.query,
            srcRegex = /<img.*?src=["'](.*?)["']/;
        
        ck_itemsMentions = ck_cachequeryMentions[thisVal];

        if(typeof ck_itemsMentions == "object"){
            callback(ck_itemsMentions);
        } else {
            var data_url = g5_plugin_url+"/mention/q.php";

에러) 댓글에 답변 달기 할때 에러 납니다.

에러 메세지

INFO:     59.10.38.107:0 - "POST /board/write_comment_update/ HTTP/1.0" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 922, in do_execute
    cursor.execute(statement, parameters)
sqlite3.OperationalError: near "(": syntax error

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/ubuntu/.local/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/home/ubuntu/.local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/applications.py", line 292, in __call__
    await super().__call__(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/main.py", line 260, in main_middleware
    response = await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/main.py", line 95, in dispatch
    return await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/routing.py", line 273, in app
    raw_response = await run_endpoint_function(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
  File "/home/ubuntu/gnu6/bbs/board.py", line 1123, in write_comment_update
    comment.wr_comment_reply = generate_reply_character(board, parent_comment)
  File "/home/ubuntu/gnu6/lib/board_lib.py", line 891, in generate_reply_character
    result = query.order_by(desc("reply")).first()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/query.py", line 2748, in first
    return self.limit(1)._iter().first()  # type: ignore
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/query.py", line 2847, in _iter
    result: Union[ScalarResult[_T], Result[_T]] = self.session.execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 2308, in execute
    return self._execute_internal(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 2190, in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
    result = conn.execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1416, in execute
    return meth(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 516, in _execute_on_connection
    return connection._execute_clauseelement(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_clauseelement
    ret = self._execute_context(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 2343, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 922, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "(": syntax error
[SQL: SELECT right(g6_write_notice.wr_comment_reply, ?) AS reply 
FROM g6_write_notice 
WHERE g6_write_notice.wr_parent = ? AND g6_write_notice.wr_comment = ? AND length(g6_write_notice.wr_comment_reply) = ? ORDER BY reply DESC
 LIMIT ? OFFSET ?]
[parameters: (1, 4, 1, 1, 1, 0)]
(Background on this error at: https://sqlalche.me/e/20/e3q8)

관리자 리스트의 경우 board_list(), member_list() 함수 참고하세요.

공통 검색 파라미터를 사용시

async def member_list(request: Request, db: Session = Depends(get_db), search_params: dict = Depends(common_search_query_params)):

select query 시

result = select_query(
            request, 
            models.Member, 
            search_params, 
            same_search_fields = ["mb_level"], 
            prefix_search_fields = ["mb_name", "mb_nick", "mb_tel", "mb_hp", "mb_datetime", "mb_recommend"]
        )

    same_search_fields: # 값이 완전히 같아야지만 필터링 '검색어'
    prefix_search_fields: # 뒤에 %를 붙여서 필터링 '검색어%'

수정된 회원 관리 리스트 코드

@router.get("/member_list")
async def member_list(request: Request, db: Session = Depends(get_db), search_params: dict = Depends(common_search_query_params)):
    '''
    회원관리 목록
    '''
    request.session["menu_key"] = MEMBER_MENU_KEY
    
    result = select_query(
                request, 
                models.Member, 
                search_params, 
                same_search_fields = ["mb_level"], 
                prefix_search_fields = ["mb_name", "mb_nick", "mb_tel", "mb_hp", "mb_datetime", "mb_recommend"]
            )
    
    query_string = generate_query_string(request)
    
    context = {
        "request": request,
        "members": result['rows'],
        "admin": request.state.context['member'], # 로그인해 있는 회원을 관리자로 간주함
        "total_count": result['total_count'],
        "paging": get_paging(request, search_params['current_page'], result['total_count'], f"/admin/member_list?{query_string}&page="),
    }
    return templates.TemplateResponse("member_list.html", context)

데이터베이스 관련 개선안

개요

개선안

1. ORM 연관관계

  • 현재는 ORM 연관관계를 사용하지 않고 있음.
  • ORM 연관관계를 사용하면 코드량 & Query 요청감소/속도증가 효과를 보일 것으로 예상됨
  • 데이터베이스에 연관관계가 물리적으로 존재하지 않아도 사용이 가능함.

2. SQLAlchemy 표현식

3. SQLModel

장점

단점

  • 개발이 진행 중이다.
  • 기본적인 메뉴얼만 제공되고 있다. => 사용법을 익히는데 어려울 가능성이 있다.

결론

  • ORM 연관관계는 초기에 적용해야 할 것으로 판단됨. (성능/코드)
  • SQLModel의 도입과 함께 표현식도 변경하는 것이 좋다고 판단됨.
    • 표현식을 변경하기 위해선 작업량이 많은 것으로 예상됨.
  • SQLModel의 도입 시기를 잘 정해야 할 듯 하다..

설치시 .env 에 COOKIE_DOMAIN 추가 바랍니다.

# www.gnuboard.com 과 gnuboard.com 도메인은 서로 다른 도메인으로 인식합니다. 
# 쿠키를 공유하려면 .gnuboard.com 과 같이 입력하세요.
# 이곳에 입력하지 않으면 www 붙은 도메인과 그렇지 않은 도메인은 쿠키를 공유하지 못하므로 
# 로그인이 풀릴 수 있습니다.
COOKIE_DOMAIN = ""

.env 파일 없을 경우 설치시 오류

화면이 출력되지 않으며 아래와 같은 오류 발생

File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/main.py", line 129, in main_middleware
    return template_response("alert.html", context, e.status_code)
  File "/home/ubuntu/gnu6/core/exception.py", line 73, in template_response
    return template.TemplateResponse(template_html, context, status_code)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/templating.py", line 110, in TemplateResponse
    context.update(context_processor(request))
  File "/home/ubuntu/gnu6/core/template.py", line 87, in _default_context
    "menus": get_menus(),
  File "/home/ubuntu/gnu6/lib/common.py", line 1305, in get_menus
    parent_menus = db.scalars(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 2420, in scalars
    return self._execute_internal(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 2190, in _execute_internal
    result: Result[Any] = compile_state_cls.orm_execute_statement(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/context.py", line 293, in orm_execute_statement
    result = conn.execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1416, in execute
    return meth(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 516, in _execute_on_connection
    return connection._execute_clauseelement(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_clauseelement
    ret = self._execute_context(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1848, in _execute_context
    return self._exec_single_context(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1988, in _exec_single_context
    self._handle_dbapi_exception(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 2343, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1969, in _exec_single_context
    self.dialect.do_execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 922, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: g6_menu
[SQL: SELECT g6_menu.me_id, g6_menu.me_code, g6_menu.me_name, g6_menu.me_link, g6_menu.me_target, g6_menu.me_order, g6_menu.me_use, g6_menu.me_mobile_use 
FROM g6_menu 
WHERE length(g6_menu.me_code) = ? ORDER BY g6_menu.me_order]
[parameters: (2,)]
(Background on this error at: https://sqlalche.me/e/20/e3q8)

그누보드6 코드 <-> 그누보드5 DB 호환: Config

1. config 테이블 cf_id pk 컬럼

  • 그누보드6는 primary key로 cf_id를 설정
  • 그누보드5는 cf_id값 존재하지 않음
    install.py 파일 실행시 다음과 같은 오류 발생
    sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1054, "Unknown column 'g5_config.cf_id' in 'field list'")

2. config 테이블 cf_optimize_date 컬럼

  • config table에 cf_id 컬럼 추가하는 과정에서 다음과 같은 오류 발생
    Invalid default value for 'cf_optimize_date'
    그누보드 5 DB는 default 값이 0000-00-00으로 되어 있음
  • MySQL의 NO_ZERO_DATE 모드로 실제로 존재하지 않는 날짜, 시간 형식 저장이 안되도록 default 지정되어 있어서 오류 발생

base_login.html 과 base_window.html 을 없애기 위해 base.html 을 3개의 파일로 나눕니다.

기존

base.html

<html>
<head>
<title>제목</title>
<script></script>
</head>
<body>
{% block content %}
{% endblock content %}
</body>
</html>

변경

base.html

{% extends "base_body.html" %}

{% block content %}
{% endblock content %}

base_body.html

{% extends "base_head.html" %}

{% block base_body %}
<!-- layout -->
{% endblock base_body %}

base_head.html

<html>
<head>
<title>제목</title>
<script></script>
</head>
<body>
{% block base_body %}
    {% block content %}
    {% endblock content %}
{% endblock base_body %}
</body>
</html>

기존의 base_window.html 을 사용하는 파일

memo_list.html

{% extends "base_head.html" %}
{% import "/sideview/macros.html" as sideview %}

{% block title %}내 쪽지함{% endblock title %}

{% block content %}
<!-- 컨텐츠 -->
{% endblock content %}

그누보드6 코드 <-> 그누보드5 DB 호환: 쪽지

쪽지 보내기 기능

  • 그누보드 6에서는 g6_memo테이블의 me_read_datetime column이 Null 값이 허용되어 있음
me_read_datetime = Column(DateTime, nullable=True)
  • 그누보드 5 DB는 해당 컬럼이 Not Null, Default는 0000-00-00 00:00:00으로 되어 있음
    -> 그누보드 5 DB에서는 쪽지를 보낼 시에 me_read_datetime이 입력되지 않아서 아래의 오류 발생
sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1048, "Column 'me_read_datetime' cannot be null")

Annotated 활용 제안

정의

Annotated 클래스는 파이썬 타입 힌트와 FastAPI의 의존성 주입 시스템을 세밀하게 제어하기 위한 도구입니다.
Annotated을 사용하면 더 많은 정보를 타입 힌트에 추가하고 FastAPI가 이 정보를 활용하도록 할 수 있습니다.

Annotated 활용해야하는 이유

  • FastAPI 공식사이트 및 블로그에서 참고한 장점들을 정리

1. 기본값 선언이 더 직관적이다.

  • 이 부분은 솔직히 공감이 잘 안된다...
# Before
kind: str = Query(default="recv"),

# After
kind: Annotated[str, Query()] = "recv",

2. 중복코드량 감소

# 데이터베이스 의존성 주입
DBSession = Annotated[Session, Depends(get_db)]

@router.post("/memo_form_update")
async def memo_form_update(
    request: Request,
    db: DBSession,
    ...

3. parmater 순서에 대한 제한이 없음

4. 의존성주입을 세부적으로 커스터마이징 가능

5. API 문서화

  • 그누보드6는 자동 문서화를 사용하지 않으니 상관없을듯..

결론

  • 2, 4번의 활용방법이 적절할 것으로 판단됨.
  • Annotated를 사용하지 않은 현 상태가 가독성이 더 좋은 경우가 많은 것으로 보임.
  • 일부 복잡한 유효성 검사 및 중복된 파라미터의 체크 등에는 적용이 필요할 것으로 판단됨.
    • 토큰
    • 캡챠
    • 일부 파라미터의 유효성검사

급) base.html 을 분할 했을때 나오는 오류

브랜치 : https://github.com/gnuboard/gnu6/tree/split-html

File "/home/ubuntu/.local/lib/python3.10/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "templates/basic/alert.html", line 1, in top-level template code
    {% extends "base_head.html" %}
  File "templates/basic/base_head.html", line 11, in top-level template code
    <link rel="stylesheet" href="{{ theme_asset('css/board.css') }}">
  File "/home/ubuntu/.local/lib/python3.10/site-packages/jinja2/utils.py", line 83, in from_obj
    if hasattr(obj, "jinja_pass_arg"):
jinja2.exceptions.UndefinedError: 'theme_asset' is undefined

글쓰기, 쪽지 보내기시 오류 발행

그누보드6 코드 <-> 그누보드5 DB 호환: 게시판

1. 자유게시판 -> 글 작성 -> 댓글 달기시 에러 발생

  • DataError: (pymysql.err.DataError) (1406, "Data too long for column 'wr_last' at row 1")
  • g6_write_free의 wr_last 컬럼은 VARCHAR(30)
    g5_write_free의 wr_last 컬럼은 VARCHAR(19)
  • 그누보드 6는 wr_last 컬럼에 2023-12-20 17:48:32.607198와 같은 형식으로 값이 들어가는데
    그누보드 5의 wr_last 컬럼은 19자로 설정해두어서 값이 입력되지 못해 오류 발생
  • 관련 테이블: write_free, write_gallery, write_notice, write_qa

2. write_free 테이블 wr_datetime 컬럼

  • write_free 테이블에서 wr_last 컬럼 varchar 글자수 변경하는 과정에서 다음과 같은 에러 발생
    Invalid default value for 'wr_datetime'
    그누보드 5 DB는 wr_datime의 default 값이 0000-00-00 00:00:00으로 되어 있음
  • MySQL의 NO_ZERO_DATE 모드로 실제로 존재하지 않는 날짜, 시간 형식 저장이 안되도록 default 지정되어 있어서 오류 발생
  • 관련 테이블: write_free, write_gallery, write_notice, write_qa 외 datetime 타입 컬럼을 사용하는 다수의 테이블

웹브라우저에서 설치하는 기능 추가

그누보드5 버전과 같이 웹브라우저에서 설치하는 기능 추가

DB 선택
MySQL 선택시 HOST, PORT, USER, PASSWORD, DB NAME 설정
PostgreSQL 선택시 ???

관리자 아이디, 패스워드 등 설정

재설치 여부

템플릿 변경시 기존템플릿 로딩

jinja 템플릿 캐시로 인해 템플릿이름이 동일한 경우 바뀌지 않습니다.

basic 테마 index.html -> test 템플릿 로딩 index.html -> basic index.html

env.loader.searchpath 변경시에도 basic index.html 계속로딩

변경시 캐시삭제

게시판 글쓰기 시 오류

Traceback (most recent call last):
  File "/home/ubuntu/.local/lib/python3.10/site-packages/uvicorn/protocols/http/httptools_impl.py", line 426, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/home/ubuntu/.local/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
    return await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/applications.py", line 292, in __call__
    await super().__call__(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/sessions.py", line 86, in __call__
    await self.app(scope, receive, send_wrapper)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/main.py", line 250, in main_middleware
    response: Response = await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 108, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/home/ubuntu/gnu6/main.py", line 109, in dispatch
    return await call_next(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 84, in call_next
    raise app_exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/base.py", line 70, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 20, in __call__
    raise e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 17, in __call__
    await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 718, in __call__
    await route.handle(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/routing.py", line 273, in app
    raw_response = await run_endpoint_function(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/fastapi/routing.py", line 190, in run_endpoint_function
    return await dependant.call(**values)
  File "/home/ubuntu/gnu6/bbs/board.py", line 864, in read_post
    insert_point(request, mb_id, read_point, f"{board.bo_subject} {write.wr_id} 글읽기", board.bo_table, write.wr_id, "읽기")
  File "/home/ubuntu/gnu6/lib/common.py", line 927, in insert_point
    db.commit()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1969, in commit
    trans.commit(_to_root=True)
  File "<string>", line 2, in commit
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/state_changes.py", line 139, in _go
    ret_value = fn(self, *arg, **kw)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1256, in commit
    self._prepare_impl()
  File "<string>", line 2, in _prepare_impl
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/state_changes.py", line 139, in _go
    ret_value = fn(self, *arg, **kw)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 1231, in _prepare_impl
    self.session.flush()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4312, in flush
    self._flush(objects)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4447, in _flush
    with util.safe_reraise():
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 146, in __exit__
    raise exc_value.with_traceback(exc_tb)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/session.py", line 4408, in _flush
    flush_context.execute()
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/unitofwork.py", line 466, in execute
    rec.execute(self)
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/unitofwork.py", line 642, in execute
    util.preloaded.orm_persistence.save_obj(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/persistence.py", line 93, in save_obj
    _emit_insert_statements(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/orm/persistence.py", line 1226, in _emit_insert_statements
    result = connection.execute(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1416, in execute
    return meth(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/sql/elements.py", line 516, in _execute_on_connection
    return connection._execute_clauseelement(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1639, in _execute_clauseelement
    ret = self._execute_context(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1820, in _execute_context
    self._handle_dbapi_exception(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 2343, in _handle_dbapi_exception
    raise sqlalchemy_exception.with_traceback(exc_info[2]) from e
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/base.py", line 1814, in _execute_context
    context = constructor(
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 1455, in _init_compiled
    l_param: List[Any] = [
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/engine/default.py", line 1456, in <listcomp>
    flattened_processors[key](compiled_params[key])
  File "/home/ubuntu/.local/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/base.py", line 1142, in process
    raise TypeError(
sqlalchemy.exc.StatementError: (builtins.TypeError) SQLite Date type only accepts Python date objects as input.
[SQL: INSERT INTO g6_point (mb_id, po_datetime, po_content, po_point, po_use_point, po_expired, po_expire_date, po_mb_point, po_rel_table, po_rel_id, po_rel_action) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]
[parameters: [{'po_rel_action': '읽기', 'po_rel_table': 'free', 'po_content': '자유게시판 2 글읽기', 'po_point': -1, 'po_datetime': datetime.datetime(2024, 1, 3, 10, 18, 23, 707228), 'po_expired': 1, 'po_use_point': 0, 'mb_id': 'admin', 'po_rel_id': '2', 'po_mb_point': 209, 'po_expire_date': '2024-01-03'}]]

버전 표시 안내

예) 6.0.1-beta

fastapi 의 버전 표시를 참고하여 예) 와 같이 표시할까 합니다.
앞에 v 는 붙이지 않습니다.
https://github.com/tiangolo/fastapi/tags

beta 버전을 거쳐 안정화가 되었다고 하는 시기에 -beta 를 떼려고 합니다.
예) 6.1.1

다른 의견 있으면 주세요.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.