# 最初的專案(20230606)

# 閱讀文章

  • 專案建置
  • 用 Django REST Framework 撰寫 RESTful API 並生成 Swagger 文檔(上) — 用 Django REST Framework 撰寫 RESTful API

因為想寫成 RESTful 風格,所以照下面那篇多裝了:

pip install djangorestframework

setting.py 的 INSTALLED_APPS 陣列記得加上 'rest_framework'


# 嘗試集 + 閱讀重點整理

# ⭐加一個專案

django-admin startproject Project名稱

# ⭐加一個 app

Python manage.py startapp app名稱
# 建議不要叫 app 之類的好像會撞 =_=

# ⭐起服務

# 要 cd 到有 manage.py 這層
python manage.py runserver

# ⭐在 SQLite 新增一張資料表

model.py

from django.db import models
# Create your models here.
class User(models.Model):
    user_name = models.CharField(primary_key=True, max_length=15)
    password = models.CharField(max_length=15)
    name = models.CharField(max_length=20)

寫完在 cmd 後執行以下命令

python manage.py makemigrations #寫入異動
python manage.py migrate #執行異動

SQLite 的檢視方式筆記(躺在 Trello,待整理)

serializers.py

# serializers 跟 models 在同層所以我這樣寫而已
from models import User #我只撿了 model 裡面的一個小傢伙 User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
     class Meta:
         model = User
         fields = '__all__'

view.pyurl 幾乎是拾人(第二篇教學的作者)

牙慧了

# 問題

ModuleNotFoundError

照著堆疊追蹤最後一行一直解掉就好,通常是多一層少一層的問題,可以好好研究

例如下方的代碼

from models import User

它如果說沒有 models

from myapp.models import User

就指 package 給它..

又如下方的代碼

from FirstProject.myapp import views as user_view

它如果說沒有 FirstProject

from myapp import views as user_view

直接同層給它..


# 🎉連線時間 ><

輸入:http://127.0.0.1:8000/users

進到 GET 方法,輸出:

[]

因為還沒存入任何東西 xd

打開 Postman 新增一個測試,

選 POST 方法,Path 輸入:http://127.0.0.1:8000/users/

上行帶剛剛的欄位:

{
    "user_name":"kiki",
    "password":"mima",
    "name":"kiki chen"
}

送出!同時這支 API 會顯示剛剛輸入的 Data

{
    "user_name":"kiki",
    "password":"mima",
    "name":"kiki chen"
}

再去一次 GET 方法 http://127.0.0.1:8000/users(或加進 Postman 測試),我們會看到:

[
    {
        "user_name": "kiki",
        "password": "mima",
        "name": "kiki chen"
    }
]

# 其他 or 疑難雜症

# ⭐存檔馬上變!

跟 Java Web 不太一樣,存檔就會馬上生效

# ⭐PyCharm 亂抖紅線 =_=╬

Unresolved reference

解法


# 序列化器(20230607)

# 前言

  • DRF 是啥
    • DRF = Django RESTful Framework
    • 官方網站
  • 話說我覺得我剛開始的學習有點太鑽牛角尖了,先會基本使用就是很棒的一步了,至於底層框架如何運作之後慢慢探索
    • 先努力看看能不能使用它達成想要的效果即可

20230612 更新:因為我發現以前收藏的大江狗... 超會教!!超有自信!!所以決定 —

# 跟著大江狗學序列化跟 DRF

# ⭐Python 內建的序列化

a = 1
json.dump({"num": a}) # 這效果感覺跟 JS 的 JSON 物件有點像

輸出的 JSON 字串內容

{"num": 1}

Django 的序列化方法先略過

# ⭐⭐對比:DRF 的序列化

  • 以博客为例使用 DRF 的序列化器和基于函数的视图开发 API

# 其他資料

  • 今年四月收藏的 django-rest-framework-tutorial 也是很好的手把手教學

# API 狀態碼初步處理(20230608)

經過多方研究跟詢問,大概知道如何在 view 替 Data 外包一層 code 跟 message,還有藉由例外處理打印對應的狀態

from django.http import JsonResponse
from django.db import transaction
from rest_framework.generics import GenericAPIView
from myapp.serializers import UserSerializer
from myapp.models import User
class UsersView(GenericAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    def get(self, request, *args, **kwargs):
        users = self.get_queryset()
        serializer = self.serializer_class(users, many=True)
        data = serializer.data
        response_data = {
            "message": "Success",
            "code": "SUCCESS,
            "data": data
        }
        return JsonResponse(response_data, safe=False)
    def post(self, request, *args, **kwargs):
        data = request.data
        try:
            serializer = self.serializer_class(data=data)
            serializer.is_valid(raise_exception=True)
            with transaction.atomic():
                serializer.save()
            data = serializer.data
            response_data = {
                "message": "Success",
                "code": "SUCCESS",
                "data": data
            }
        except Exception as e:
            response_data = {
                "message": "Something throw a exception.",
                "code": "SOME_EXCEPTION",
                "error": str(e) # 不過比起這樣打印,我比較想記入日誌就好
            }
        return JsonResponse(response_data)

打 GET 方法輸出:

{
    "message": "Success",
    "code": "SUCCESS",
    "data": [
        {
            "user_name": "kiki",
            "password": "mima",
            "name": "kiki chen"
        },
        {
            "user_name": "kiki2",
            "password": "mima",
            "name": "kiki chen"
        }
    ]
}

打 POST 方法但製造一個例外:

{
    "message": "Something throw a exception.",
    "code": "SOME_EXCEPTION",
    "error": "{'user_name': [ErrorDetail(string='user with this user name already exists.', code='unique')], 'name': [ErrorDetail(string='This field is required.', code='required')]}"
}

# 小記

但我想想這樣之後 app 一多,這段程式是不是很重複,而且既然 response_data 是用寫出來的,內容可以不統一,能不能拆處來封裝跟做全域掌控呢,想想想...(不過也要顧慮 Python 的開發風格)

另外日誌紀錄也是蠻重要的......


# DB ORM(20230723)

  • models ID 自增
  • models 欄位非必填
  • 時間與快樂 (?) Model 欄位類型介紹 (上) 江狗 (Django) 鐵人 Day20
  • Enum 欄位
    • 案外案
  • 外鍵

# .gitignore 乾貨(20230722)

起因是因為從 GitHub import 進 Replit 的 Django 專案想推版了,但沒寫 .gitignore ,看到旁邊清單一萬塊兩萬個檔案,呃... 當然不行 r

一開始是參考這篇寫(說實話又學到不少 0.0),但寫到一半發現還有八千多個檔案擋不掉 Orz

好吧,後來犯懶了,而且網路上竟然有-Django .gitignore 懶人包!!,就直接拿來用啦(喂 www),效果超顯著的 'w'

不過我看到 Poetry 相關的檔案沒擋掉,看完這篇討論,得知它是類似 Java Maven pom.xml 的角色謎之音:你到昨天那刻為止都還沒發現 Django 也有套件管理齁,為了讓大家都能鎖定相同的依賴版本,當然是要一起推上去的~


# Replit 配置相關:如何在 Replit 運行從 Github import 的 Django 專案(20230629)

# 一、修改按下 run 的操作行為

# (一)官方教學,快狠準 QQ

# (二)我的做法,改 .replit 設定檔

在檔案搜尋框輸入.,找到一個叫 .replit 的檔案,貼上這個設定檔 **(基底來自 Replit 官方的 Django 範本)**,須注意由於我的 manage.py 在 FirstProject 的目錄下方,所以 path 才這樣設置,不然正常應該第二個字串帶 manage.py 就好

# The command that runs the program.
run = ["python3", "FirstProject/manage.py", "runserver", "0.0.0.0:3000"]
# The primary language of the repl. There can be others, though!
language = "python3"
# The main file, which will be shown by default in the editor.
entrypoint = "FirstProject/manage.py"
# A list of globs that specify which files and directories should
# be hidden in the workspace.
hidden = ["venv", ".config", "**/__pycache__", "**/.mypy_cache", "**/*.pyc"]
# Specifies which nix channel to use when building the environment.
[nix]
channel = "stable-21_11"
# Per-language configuration: python3
[languages.python3]
# Treats all files that end with `.py` as Python.
pattern = "**/*.py"
# Tells the workspace editor to syntax-highlight these files as
# Python.
syntax = "python"
  # The command needed to start the Language Server Protocol. For
  # linting and formatting.
  [languages.python3.languageServer]
  start = ["pyls"]
# The environment variables needed to correctly start Python and use the
# package proxy.
[env]
VIRTUAL_ENV = "/home/runner/${REPL_SLUG}/venv"
PATH = "${VIRTUAL_ENV}/bin"
PYTHONPATH="${VIRTUAL_ENV}/lib/python3.8/site-packages"
REPLIT_POETRY_PYPI_REPOSITORY="https://package-proxy.replit.com/pypi/"
MPLBACKEND="TkAgg"
# Enable unit tests. This is only supported for a few languages.
[unitTest]
language = "python3"
# Add a debugger!
[debugger]
support = true
  # How to start the debugger.
  [debugger.interactive]
  transport = "localhost:0"
  startCommand = ["dap-python", "main.py"]
    # How to communicate with the debugger.
    [debugger.interactive.integratedAdapter]
    dapTcpAddress = "localhost:0"
    # How to tell the debugger to start a debugging session.
    [debugger.interactive.initializeMessage]
    command = "initialize"
    type = "request"
      [debugger.interactive.initializeMessage.arguments]
      adapterID = "debugpy"
      clientID = "replit"
      clientName = "replit.com"
      columnsStartAt1 = true
      linesStartAt1 = true
      locale = "en-us"
      pathFormat = "path"
      supportsInvalidatedEvent = true
      supportsProgressReporting = true
      supportsRunInTerminalRequest = true
      supportsVariablePaging = true
      supportsVariableType = true
    # How to tell the debugger to start the debuggee application.
    [debugger.interactive.launchMessage]
    command = "attach"
    type = "request"
      [debugger.interactive.launchMessage.arguments]
      logging = {}
# Configures the packager.
[packager]
# Search packages in PyPI.
language = "python3"
# Never attempt to install `unit_tests`. If there are packages that are being
# guessed wrongly, add them here.
ignoredPackages = ["unit_tests"]
  [packager.features]
  enabledForHosting = false
  # Enable searching packages from the sidebar.
  packageSearch = true
  # Enable guessing what packages are needed from the code.
  guessImports = true

可以看到它改寫了 run 按鈕的行為,也可以寫成

run = "python FirstProject/manage.py runserver 0.0.0.0:3000"

寫法出處

所以說以後在 Replit 撰寫或 import 其他語言的專案,如果不是用範本建立,也可以透過修改設定檔改變按下 run▶️符號的行為

# 二、然後還要把 API 網址加到 setting.py allowed hosts

像我的要這樣寫:

ALLOWED_HOSTS = ['ourdrf.chi200706.repl.co']

補充

用 Replit 的 Django 範本生成之專案,因為 setting.py 中 SECRET_KEY 的部分被寫成這樣:

SECRET_KEY = os.getenv('SECRET_KEY')

所以要切到 Tools->Secrets 下方新增 key,產生方法有寫在範本建立之初的 README.md

python # 進 python
import secrets # 引進 secrets 
secrets.token_urlsafe(32) # 產生 token

其實平常推 Django 專案可能不小心暴露 SECRET_KEY,這個範本做了很好的示範,多學到了!


# DRF 例外 Handler(20230608)

# 閱讀資料

功能想像:

在 view 中拋出例外

  • Python Raise an Exception

被 DRF 的 handler 接住

  • 官方文件
    • 網友討論