# 자동 코드생성 및 수정

1. 개요
2. 방법
3. TIP

## 1. 개요

해당 방법은 `sqlcodegen`을 통해 DB의 데이터를 `ORM`방식으로 전환하는 방법이다.</br>
이번에는 `PostgreSQL`을 기준으로 작성하였다.

## 2. 방법

먼저 다른 lib와 충돌을 위해 별도의 venv를 생성 후 아래의 lib를 설치한다.
```bash
pip install psycopg2-binary sqlacodegen SQLAlchemy
```

해당 명령어를 입력 후, 아래의 명령어를 통해 ORM를 자동생성한다.
```bash
sqlacodegen postgresql://{{id}}:{{passwd}}@{{DB URL or IP}}:{{port}}/{{DB schema}} > {{file명 i.e.) models.py}}
```

해당 명령어를 통해 아래와 같은 코드가 작성된다.

```python
from typing import Optional
import datetime
import uuid

from sqlalchemy import (ARRAY, Boolean, DateTime, Double, Enum, ForeignKeyConstraint, Index, Integer, JSON, PrimaryKeyConstraint, String, Time, UniqueConstraint, Uuid, text, Sequence, func)
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship

class Base(DeclarativeBase):
    pass


class Company(Base):
    __tablename__ = 'company'
    __table_args__ = (
        PrimaryKeyConstraint('id', name='company_pk'),
    )

    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    url: Mapped[Optional[str]] = mapped_column(String(255))
    created_at: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True), server_default=func.now())
    updated_at: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True), server_default=func.now(), onupdate=func.now())

    team: Mapped[list['Team']] = relationship('Team', back_populates='company')


class Product(Base):
    __tablename__ = 'product'
    __table_args__ = (
        PrimaryKeyConstraint('id', name='product_pk'),
    )

    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
    name: Mapped[str] = mapped_column(String(32), nullable=False)
    app_id: Mapped[Optional[str]] = mapped_column(String(16))
    scope: Mapped[Optional[dict]] = mapped_column(JSON)
    description: Mapped[Optional[str]] = mapped_column(String(256))
    created_at: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True), server_default=func.now())
    updated_at: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(True), server_default=func.now(), onupdate=func.now())

    robeing: Mapped[list['Robeing']] = relationship('Robeing', back_populates='product')

...
```

## 3. TIP

해당 항목은 자동으로 생성될 시, 몇가지 수정점이 있으므로 아래와 같은 주의사항을 작성하였다.

- `SEQUENCE`는 자동으로 작성되지 않는다.</br>
  그러므로 아래와 같은 방식으로 Colume에 추가한다.

  - ~에서
  ```python
  ....
  id: Mapped[int] = mapped_column(Integer, primary_key=True)
  ....
  ```

  - ~으로
  ```python
  ....
  id: Mapped[int] = mapped_column(Integer, Sequence('conversation_log_id_seq'), primary_key=True)
  ....
  ```

- DB에서 Default값을 작성시에는 상황에 맞게 수정한다.
  - case 1. 서버에서 기본값이 들어가야하는 경우
    해당경우는 code에서 기본값을 작성하여 DB에 insert 한다.</br>
    i.e.) datezone의 경우: `sqlalchemy`의 func.now()</br>
          uuid의 경우: uuid.uuid4</br>
    ※ DB의 호환성을 고려하여 만약 오류가 발생시 적절한 방법을 찾아서 코드로 구현한다.
  - case 2. DB에서 처리해도 되는 경우와 DB의 호환성을 고려하는 경우
    해당경우는 sqlcodegen에서 자동생성된 코드를 그대로 사용한다.</br>
    i.e.) jwt의 경우: `server_default=text("'Bearer'::character varying")` 그대로 사용한다.</br>
