星期三, 8月 19, 2020

Django site framework

 之前專案都是使用 cookiecutter + cookiecutter-django 來產生的,前一陣子在它產生出來的設定裡看到 SITE_ID 以及註解,就發現了 Django site framework

Django site framework 裡的 site ,就有點像是 virtual host 裡的 host,簡單的說,可以做到以下事情:

  • Virtual host:Django 可以有能力去判斷網址裡的網域,來決定如何處理。
  • 不同網站入口,但共用資料庫

啟用方法

基本上 site framework 已經內建在 Django 裡了,所以只要啟用就可以

  1. 在 settings 的 INSTALLED_APPS 加入 “django.contrib.sites”
  2. 增加 SITE_ID  (選填,可以加這個設定,也可以不加)

執行 migrate python manage.py migrate

接著利用 admin 後台或者是 shell 去新增 site。

功能

有提供以下功能

  • 物件 (model) 跟指定的 site 關連
  • View 可以依據 site 來做出不同的處理跟回應
  • 多個 site,但只使用指定的 site
  • 自動依照網址內的網域來判斷,並將 site 放到 request 裡
物件 (model) 跟指定的 site 關連

site framework 本身有建立 model ,也就是說有建立資料表格,所以其實是可以讓物件模型跟 site 做關連,那麼之後在處理時,就可以依據這個關係,只顯示出跟指定 site 相關連的物件。

# 引用自 django 文件
from django.contrib.sites.models import Site
from django.db import models

class Article(models.Model):
    headline = models.CharField(max_length=200)
    # ...
    sites = models.ManyToManyField(Site)

上面的程式碼,就是表示 Article 跟 Site 做多對多的關聯。之後在 View 裡就可以用下面的程式碼撈出跟指定站點相關連的 Article

# 引用自 Django 文件
from django.contrib.sites.shortcuts import get_current_site

def article_detail(request, article_id):
    try:
        a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id)
    except Article.DoesNotExist:
        raise Http404("站點裡沒有文章")
    # ...
View 可以依據 site 來做出不同的處理跟回應

其實上面的程式碼已經就是了,這邊再舉個例子

# 引用自 Django 文件
from django.contrib.sites.shortcuts import get_current_site

def my_view(request):
    current_site = get_current_site(request)
    if current_site.domain == 'foo.com':
        # Do something
        pass
    else:
        # Do something else.
        pass

這邊使用 site framework 提供的 shortcut – get_current_site 來取得目前的 site,然後用 if – else 來做判斷,執行不同的邏輯

多個 site,但只使用指定的 site

那程式裡已經有多個 site,但是想拆分出來,只服務單一個 site 時,可以在 settings 裡指定 SITE_ID,程式裡可以直接引用,例如

from django.conf import settings

def my_view(request):
    if settings.SITE_ID == 3:
        # Do something.
        pass
    else:
        # Do something else.
        pass

這個蠻適合應用在 docker 上,我只要打包好 docker image,之後就可以再利用指定環境變數的方式去讓這個 container 能處理指定的 site

自動依照網址內的網域來判斷,並將 site 放到 request 裡

要完成這個功能,需要在 settings 裡的 MIDDLEWARE 裡,加入 “django.contrib.sites.middleware.CurrentSiteMiddleware”

加入以後,View 裡的 request 就會多出一個 site 的屬性,那在 View 裡,就可以直接使用 request.site

Site framework 內部的運作

依據上面的說明,除了資料庫以外,get_current_site 好像…挺重要的,下面就來繼續挖掘。

我們先從 middleware 開始,CurrentSiteMiddleware 裡面蠻簡單的,只有 override process_request(),裡面只有一行:request.site = get_current_site(request)

!! 又是 get_current_site()

那 get_current_site() 又做了什麼事情?get_current_site() 只判斷 django.contrib.sites 有沒有在 settings INSTALLED_APPS 裡,有的話,就使用 Site.objects.get_current() 來取得目前的 site;沒有的話,改用 RequestSite 類別來判斷,RequestSite 只是一個封裝,封裝出類似 Site 的物件,讓你後續存取可以跟原來的 site 物件一樣。

    if apps.is_installed('django.contrib.sites'):
        from .models import Site
        return Site.objects.get_current(request)
    else:
        from .requests import RequestSite
        return RequestSite(request)

先看 Site.objecs.get_current() ,這函式的實作是在 django/contrib/sites/models.py 裡。裡面也很簡單,先去看 settings 裡有沒有 SITE_ID,有的話,就用 SITE_ID 去查詢資料表格,取出對應的 site;沒有 SITE_ID 或者是 SITE_ID 被判定為 False (‘’, 0 都算是 False),就改使用 _get_site_by_request() 從 request 去做判讀。

_get_site_by_request() 使用 request.get_host() 取出 host,然後解析出 domain/port,再使用 domain 去查詢資料表格,找到對應的 site。

總結

以上,就是 site framework ,為了驗證我對 site framework 的認知,製作了一個小的展示專案,放在 github 上:https://github.com/elleryq/site_framework_demo

檢查 python 套件是否過期

 

用 pip 就可以檢查了

pip list ——outdated ——format=columns

也可以改用 json 格式輸出

pip list ——outdated ——format=json

但有時會想知道目前裝的版本跟最新版本差多少,這時可以使用 pip-check 這個工具,先安裝

pip install pip-check

進行檢查

pip-check ——cmd=pip ——hide-unchanged

加上 -u 的話,後面會補上升級指令

pip-check ——cmd=pip ——hide-unchanged -u

星期六, 8月 15, 2020

Visual Studio code snippets

 

一直都沒認真研究 vscode snippets 怎麼弄
今天去看了以後,發現蠻簡單的:

  • 啟用:選到 Preferences > Settings ,在上方的搜索框輸入 “editor.tabcompletion”,把 Tab Completion 選為 on 或 onlySnippets 就可以。在輸入 prefix (前置字串)後,按下 tab ,就會出現 snippets 讓你選擇。
  • 建立 snippets:
    • 手動建立:選到 Preferences > User snippets ,此時會要問你要編輯哪種類型檔案的 snippets,選好以後,會開啟一個 json 檔案,檔案裡面有註解說明,依照說明去編輯就可以了。編輯好,會立即生效。
    • 快速建立:安裝 Snippet Creator 這個 Extension (尋找 ryanolsonx.snippet-creator),安裝好以後,先選取你要建立 snippet 的文字,然後按下 `ctrl + shift + p` ,依照提示輸入 prefix, description 以後,就可以了。
  • 現成的 snippets:事實上,也有很多人把自己建立的 snippet 分享為 extension,供大家安裝。像 Python 就可以安裝 “Python snippets” (尋找 frhtylcn.pythonsnippets),或是 “Python extended” (尋找 tushortz.python-extended-snippets),或是 “Django” (尋找 bigonesystems.django),安裝好以後,就可以使用了。至於該 extension 有提供哪些 snippets,就得自己去挖 extension 的說明檔或是原始碼了,對,這個意思就是要稍微記一下有哪些 prefix (前置字串),否則就都沒用到,等於是白裝。

參考資料:

星期五, 7月 24, 2020

電影流水帳(2020/06/21~2020/06/30)

August 18th, 2010 | Scott Pilgrim Vs. The World European Premiere
  • Gemini Man (IMDB, Wikipedia),台譯:雙子殺手。
  • Jumanji: the next level (IMDB, Wikipedia),台譯:野蠻遊戲-全面晉級。

Gemini Man

雙子殺手,記得是去年上映的片,蠻快就在第四台上片了,我忘了是哪一台,猜想可能是疫情的關係。劇情還不錯,不過說要有什麼印象深刻的,就說不出來。

Henry 是個殺手,嗯,應該算吧,是幫國安局出不可告人的任務的。Henry 在一次任務以後,決定退下來,想要好好過生活。但是呢,國安局不放過他,派了人去監視他,並且打算將他滅口。Henry 當然沒想那麼多,也不知情。他去港口租船釣魚,在售票口遇到 Danny ,一個漂亮又風趣的女孩子,誰不動心呢?聊了幾句,確認彼此好感之後,就約了晚上吃飯。

Henry 出海釣魚,是要跟以前的好友聯絡,聊了好一陣子,好友透漏了國安局可能會派人來對付他,Henry 記住了。Henry 離開之後沒多久,他的好友就被國安局滅口了。Henry 晚上跟 Danny 吃飯,聊了幾句,Henry 就問了 Danny ,你是誰派來的?Danny 有點吃驚,看 Henry 其實沒惡意,就乾脆地說出了自己的出身跟來意,兩人反而聊開了。Henry 送 Danny 回家,Danny 發現家裡怪怪的,於是拿出了槍,準備禦敵。就在鬼影幢幢之際,Henry 闖了進來,表明這些人是來追殺他的。兩方立刻知道,除了監視Henry的人 (Danny)之外,還有另外一股勢力,兩人就開始邊打邊逃了。

然後我去處理了一些事情,隔了一陣子才回來接著看。兩人已經遇到那個以 Henry 基因製造出來的年輕人 Junior 了,他們也搞清楚了大致的事情經過,並且設計了 Junior ,告訴了他真相。最終是三人聯手去對付大魔王 Clay,並且解決了他。末了,Henry 跟 Danny 過著退休生活,Junior 到學校念書,去過他以前沒有經歷過的生活。

Jumanji: The next level

我個人覺得,不一樣的角色扮演,衍生出不一樣的趣事,我覺得編劇這一手相當不錯。新秀演員 Awkwafina ,加上老牌演員:Danny DeVito, Danny Glover ,的確也激盪出不少有趣的事情。但我仍然覺得這集續差了,沒有前集來的好看。

在上次的經歷之後,四人在現實生活成了好朋友,定期聯絡,但 Spencer 陷入了情感與社群的焦慮,他覺得自己不夠好,也不適合跟 Martha 在一起,就疏遠了 Martha。這次聖誕節從紐約回到家裡,Spencer 得跟爺爺住在一起,隔天本來是要跟其他三人碰面的,但 Spencer 左思右想,覺得應該回到遊戲裡去當 Dr. Xander,想拾回那時的感覺。於是他組裝了之前留下的遊戲機,於是就進到遊戲裡了。

隔天,其他三人,Martha, Fridge, Bethany 碰面聊了幾句以後,想說怎麼 Spencer 還沒來,打了電話也沒人接,就決定到他家裡看看。到 Spencer 家,正巧 Spencer 爺爺多年沒見、已經翻臉的朋友(Milo)來找 Spencer 爺爺(Eddie),Eddie 跟 Milo 小吵了一下,三人打過照面後,分別去找 Spencer ,找了好一會,才在地下室找到遊戲機。三人心想,事情大條了,看來得再進去野蠻遊戲裡救他出來。Fridge 不太樂意,他覺得很不爽,但在 Martha 跟 Bethany 感召下決定還是進去。不過這次有些不一樣了,Bethany 沒進去,Martha、Bethany、Eddie 跟 Milo 進去了。進去以後的角色除了Martha仍然是Ruby之外,其他人的角色都不一樣了。

一行人就跟上集一樣,邊解任務,邊尋找 Spencer 。在尋找的過程裡,Eddie 跟 Milo 化解了多年來的心結,當初 Milo 拆夥餐館,是因為覺得錢賺夠了,要好好享受生活,就把餐館轉手賣掉了。但他沒想到 Eddie 的樂趣就是在餐館工作,一拆夥,反倒惹怒了 Eddie,這一氣就氣了好幾年。Spencer 也化解了新裡的對愛情與工作的焦慮,他明白了 Martha 的想法,也讓自己恢復了跟 Dr. Xander 一樣的勇氣。

在外面的 Bethany 沒進遊戲裡,她心急如焚,趕緊去找了之前的機師 Jefferson,請他幫忙修復遊戲機,一起進去遊戲裡。一群人總算是會合了,他們也透過奇妙顏色的湖水把角色換了回來,接著打敗大魔王,結束了這個遊戲。

故事的最後,揭露了 Milo 去找 Eddie 的原因,大夥要離開遊戲,但 Milo 想要留下來,因為 Milo 得了絕症快死了,所以想留在遊戲裡。大夥尊重 Milo 想法,就離開了遊戲,大概就這樣子。
嗯,寫到這邊,我想我大概知道為什麼我覺得沒有前集來的好看了,因為導演想講的事情太多了,角色也有點太多,每個人都要有戲,導致劇情有點冗長,節奏也鬆散,沒有那種緊湊感,真的是蠻可惜的。

星期五, 7月 17, 2020

git config 的 includeIf

在推特上看到 @WanCW 的推文,才知道 git config 有 includeIf ,所以來研究一下怎麼用。
先看 git config 的說明:https://git-scm.com/docs/git-config ,用法蠻簡單的。

這邊先做假設情境

  • 使用者家目錄是 /home/user ,也就是 HOME=/home/user
  • $HOME/CompanyProjects 是公司專案目錄,而公司的 git server 是 gitlab.com
  • $HOME/SideProjects 是自己的專案目錄,自己的 git server 也是 gitlab.com
  • 公司用的 ssh key 在 $HOME/.ssh/company.pem
  • 自己用的 ssh key 在 $HOME/.ssh/id_rsa

為了避免使用者名稱、email 跟 SSH key 混用,這時候 git config 該怎麼設定呢?

首先在 $HOME 下建立 .gitconfig.d 目錄,在裡面放入 company.inc 與 side.inc,這兩個檔案的內容,在後面會提到。

先編輯 $HOME/.gitconfig 

; include if $GIT_DIR is under /home/user/CompanyProjects/
[includeIf “gitdir:/home/user/CompanyProjects/"]
    path = /home/user/.gitconfig.d/company.inc

; include if $GIT_DIR is under /home/user/SideProjects/
[includeIf “gitdir:/home/user/SideProjects/"]
    path = /home/user/.gitconfig.d/side.inc

再來是 company.inc 與 side.inc

; $HOME/.gitconfig.d/company.inc
[user]
    email = user@company.com
    name = user(company)

[core]
    sshcommand=ssh -i /home/user/.ssh/company.pem
; $HOME/.gitconfig.d/side.inc
[user]
    email = user@example.com
    name = user


在這樣設定之後,當切換到 $HOME/CompanyProjects 目錄下任一個專案目錄時,git 會使用 company.inc 裡的設定;切換到 $HOME/SideProjects 目錄下任一個專案目錄時,git 會使用 side.inc 裡的設定。

那也可以針對個別專案去設定,假設 $HOME/CompanyProjects/ProjectA 目錄會用到不一樣的,那就在 .gitconfig 加入

[includeIf “gitdir:~/CompanyProjects/ProjectA/“]
    path = ~/.gitconfig.d/projecta.inc

然後在 $HOME/.gitconfig.d 下新增 projecta.inc 即可。