{"id":20619767,"url":"https://github.com/twtrubiks/django-shop-tutorial","last_synced_at":"2025-10-08T06:29:13.376Z","repository":{"id":84518930,"uuid":"100846033","full_name":"twtrubiks/django-shop-tutorial","owner":"twtrubiks","description":"Use Django To Create A Simple Shopping Site Tutorial","archived":false,"fork":false,"pushed_at":"2024-04-23T15:28:22.000Z","size":49,"stargazers_count":159,"open_issues_count":8,"forks_count":46,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-06T21:45:52.696Z","etag":null,"topics":["django","ngrok","paypal","shopping","signals","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twtrubiks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-20T07:38:26.000Z","updated_at":"2025-03-23T06:33:14.000Z","dependencies_parsed_at":"2024-11-20T11:02:46.678Z","dependency_job_id":null,"html_url":"https://github.com/twtrubiks/django-shop-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/twtrubiks/django-shop-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-shop-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-shop-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-shop-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-shop-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twtrubiks","download_url":"https://codeload.github.com/twtrubiks/django-shop-tutorial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twtrubiks%2Fdjango-shop-tutorial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260495499,"owners_count":23017910,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","ngrok","paypal","shopping","signals","tutorial"],"created_at":"2024-11-16T12:12:28.121Z","updated_at":"2025-10-08T06:29:08.340Z","avatar_url":"https://github.com/twtrubiks.png","language":"Python","readme":"# django-shop-tutorial\n\n Django-shop-tutorial 基本教學 - 從無到有 Django-shop-tutorial 📝\n\n大家一定常看到購物網站，今天要教大家使用 [Django](https://github.com/django/django) 建立一個簡易版購物網站 :smile:\n\n* [Youtube Tutorial - part1](https://youtu.be/S_4nld8XDY8)\n\n* [Youtube Tutorial - part2](https://youtu.be/9pekT1AZ_T8)\n\n建議對 [Django](https://github.com/django/django) 不熟悉的朋友，可以先觀看我之前寫的文章（ 進入 [Django](https://github.com/django/django)  的世界）\n\n* [Django 基本教學 - 從無到有 Django-Beginners-Guide](https://github.com/twtrubiks/django-tutorial)\n\n* [使用 Django 實現一個可以使用社交平台登入並且註冊的網站](https://github.com/twtrubiks/django_social_login_tutorial)\n\n## 特色\n\n* 簡易版購物網站\n* [PayPal](https://www.paypal.com/tw/webapps/mpp/home) 金流\n\n## 安裝套件\n\n請在 cmd ( 命令提示字元 ) 輸入以下指令\n\n```python\npip install -r requirements.txt\n```\n\n## 我可以從這篇學到什麼\n\n* 購物網站\n* 認識 [Django](https://github.com/django/django) 的 sessions\n* 認識 [Django](https://github.com/django/django) 的 context-processors\n* PayPal Tutorial\n* 認識 [Django](https://github.com/django/django) 的 Signals\n* 認識 ngrok\n\n## 教學\n\n請先確認電腦有安裝 [Python](https://www.python.org/)\n\nclone 專案\n\n```cmd\ngit clone https://github.com/twtrubiks/django-shop-tutorial.git\n```\n\n執行 makemigrations 以及 migrate ，建立 DATABASE\n\n請在 cmd ( 命令提示字元 ) 輸入以下指令\n\n```python\npython manage.py makemigrations\n```\n\n```python\npython manage.py migrate\n```\n\n如果對上方操作不理解，可以參考我之前寫的  [django-field-tutorial](https://github.com/twtrubiks/django-field-tutorial) ，帶你認識 Django ORM and Relationship Field。\n\n建立 admin 帳號\n\n請在 cmd ( 命令提示字元 ) 輸入以下指令\n\n```python\npython manage.py createsuperuser\n```\n\n我有將 database 傳上去，大家也可以直接使用我的 database。後台帳號密碼如下，\n\n帳號 : twtrubiks\n\n密碼 : password123\n\n### 認識 [Django](https://github.com/django/django) 的 sessions\n\n首先需要確定 [settings.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/django_shop_tutorial/settings.py) 裡面的 MIDDLEWARE 包含 'django.contrib.sessions.middleware.SessionMiddleware' ,\n\n一般預設 [settings.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/django_shop_tutorial/settings.py) 裡面就已經設定了，所以通常不用特別另外設定。\n\n以下介紹 session 一些基本用法，\n\n設置 session\n\n```python\nrequest.session['yo'] = 'yo'\n```\n\n取得 session\n\n```python\nrequest.session.get('yo',None)\n```\n\n刪除 session\n\n```python\ndel request.session['yo']\n```\n\nP.S 假如 key 已經不存在了，則會噴錯 ( KeyError )\n\n查看目前所有的 session key\n\n```python\nrequest.session.keys()\n```\n\n 基本上，sessions 的使用和 python 中的字典是類似的。\n\n更多的資料可參考 [https://docs.djangoproject.com/en/1.11/topics/http/sessions/](https://docs.djangoproject.com/en/1.11/topics/http/sessions/)\n\nsession 在專案中使用的地方可查看 [cart.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/cart/cart.py)，在裡面有用到  `__iter__` 以及  `__len__`，\n\n如果不了解 `__iter__` 是什麼 ，可以參考我之前寫的簡單範例\n\n[python-notes  __iter__tutorial.py](https://github.com/twtrubiks/python-notes/blob/master/__iter__tutorial.py)\n\n從上面這個範例你可以了解到，基本上就是使用 for in 的時候，他會開始迭代，並且呼叫 `__iter__`\n\n如果不了解 `__len__` 是什麼 ，可以參考我之前寫的簡單範例\n\n[python-notes _len_tutorial.py](https://github.com/twtrubiks/python-notes/blob/master/_len_tutorial.py)\n\n從上面這個範例你可以了解到，基本上就是使用 len() 方法時，會呼叫 `__len__`\n\n### 認識 [Django](https://github.com/django/django) 的 context-processors\n\ncontext-processors 可以讓你在 code 的任何地方存取他，\n\n換個說法，在任何一個 template 中都可以存取這個變數。\n\n可參考\n\n[https://docs.djangoproject.com/en/1.11/ref/templates/api/#built-in-template-context-processors](https://docs.djangoproject.com/en/1.11/ref/templates/api/#built-in-template-context-processors)\n\n[https://docs.djangoproject.com/en/1.11/ref/templates/api/#using-requestcontext](https://docs.djangoproject.com/en/1.11/ref/templates/api/#using-requestcontext)\n\n在  [settings.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/django_shop_tutorial/settings.py) 裡面的 TEMPLATES 有一個名稱為 context_processors，我們在裡面加入一行\n\n```python\n'cart.context_processors.cart'\n```\n\n這行是我們自己定義的路徑 [context_processors.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/cart/context_processors.py)。\n\n```python\nTEMPLATES = [\n    {\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n        'DIRS': [os.path.join(BASE_DIR, 'templates')],\n        'APP_DIRS': True,\n        'OPTIONS': {\n            'context_processors': [\n                'django.template.context_processors.debug',\n                'django.template.context_processors.request',\n                'django.contrib.auth.context_processors.auth',\n                'django.contrib.messages.context_processors.messages',\n                'cart.context_processors.cart',\n            ],\n        },\n    },\n]\n```\n\n設定完之後，你就可以在任何的 template 中存取這個 cart 這個變數。\n\n之前也有介紹過 自定義模版，也是可以在 template 中使用，\n\n可以參考之前寫的 [\n瞭解 django template tag ( 自定義模板 )](https://github.com/twtrubiks/django_social_login_tutorial#%E4%BA%86%E8%A7%A3-django-template-tag---%E8%87%AA%E5%AE%9A%E7%BE%A9%E6%A8%A1%E6%9D%BF-)\n\n### PayPal Tutorial\n\n如果你想看 flask 版本 ，可以參考我之前寫的 [PayPal_flask](https://github.com/twtrubiks/PayPal_flask)。\n\n首先，建立一個 PayPal 帳號  [PayPal](https://www.paypal.com/us/home)，安裝 [django-paypal](https://github.com/spookylukey/django-paypal)\n\n```python\npip install django-paypal\n```\n\n可參考文件說明\n\n[https://github.com/spookylukey/django-paypal](https://github.com/spookylukey/django-paypal)\n\n[https://django-paypal.readthedocs.io/en/stable/standard/ipn.html](https://django-paypal.readthedocs.io/en/stable/standard/ipn.html)\n\n使用 PayPal Standard IPN ， IPN 全名為 Instant Payment Notification，\n\n編輯你的  [settings.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/django_shop_tutorial/settings.py) 文件，並加上 'paypal.standard.ipn' 到 INSTALLED_APPS 中\n\n```python\nINSTALLED_APPS = (\n    ....\n    'paypal.standard.ipn',\n    ...\n)\n```\n\n請在 settings.py 加入以下的設定\n\n```python\nPAYPAL_RECEIVER_EMAIL = 'dikeooel3ski-facilitator@gmail.com'\nPAYPAL_TEST = True\n```\n\nPAYPAL_RECEIVER_EMAIL : 你的測試 PayPal 帳號，也就是 dikeooel3ski-facilitator@gmail.com 這組帳號，\n\ndikeooel3ski-facilitator@gmail.com 這是你的測試帳號，說明可以參考文章後面的說明。\n\nPAYPAL_TEST：告訴 PayPal 是在沙盒環境下。\n\n更新 database\n\n```python\npython manage.py migrate\n```\n\n請在 urls.py 底下增加下方的設定\n\n```python\nurl(r'^paypal/', include('paypal.standard.ipn.urls')),\n```\n\ndjango-paypal  提供兩種 IPN signals\n\n`valid_ipn_received`：\n\n正確的資料，而且不是從現有資料庫中複製的訊息。\n\n`invalid_ipn_received`：\n\n失敗的資料，而且這筆資料會有一個 flag。\n\n可參考 [http://django-paypal.readthedocs.io/en/stable/standard/ipn.html](http://django-paypal.readthedocs.io/en/stable/standard/ipn.html)\n\n### PayPal 的沙盒 ( sandbox ) 教學\n\n請先到 [https://developer.paypal.com/](https://developer.paypal.com/) 登入你的帳號\n\n先使用你註冊的帳號登入\n\n登入後，請點 Sandbox -\u003e Accounts 這個\n\n![](http://i.imgur.com/t0J4DWX.png)\n\n裡面預設會有兩組帳號（ 記得去重改這兩組測試帳號的密碼 ）\n\n修改測試帳號密碼的方式可參考下方\n\n![](http://i.imgur.com/UsZlbBE.png)\n\n![](http://i.imgur.com/bI5jNh0.png)\n\n簡單說明一下這兩組測試帳號，\n\nxxxxxxxx-facilitator   這組帳號是賣家\n\nxxxxxxxx-buyer   這組帳號是買家\n\n測試購買時，請用 xxxxxxxx-buyer 這組帳號登入，\n\n要確認收款時， 請用 xxxxxxxx-facilitator 這組帳號登入，\n\n以上兩組帳號可以登入下方沙盒  ( sandbox )  測試\n\n[https://www.sandbox.paypal.com/signin](https://www.sandbox.paypal.com/signin)\n\nP.S\n\n當你成功使用 xxxxxxxx-buyer 測試帳號購買後，\n\n請記得要用 xxxxxxxx-facilitator 這組帳號登入 去確認收款。\n\n使用 xxxxxxxx-buyer 這組帳號登入沙盒  ( sandbox ) 畫面\n\n![](http://i.imgur.com/sA2Lo9W.png)\n\n使用 xxxxxxxx-facilitator 這組帳號登入沙盒  ( sandbox ) 畫面\n\n![](http://i.imgur.com/QNIy8Qq.png)\n....\n\n### 認識 [Django](https://github.com/django/django) 的 Signals\n\ndjango 裡的 signals 你可以把他想成是一種觸發器，當某種事件被觸發時，去處理一些事情，看下面這個例子，\n\n這個例子就是當 Request 結束時，my_callback 會被觸發。\n\n```python\nfrom django.core.signals import request_finished\nfrom django.dispatch import receiver\n\n@receiver(request_finished)\ndef my_callback(sender, **kwargs):\n    print(\"Request finished!\")\n```\n\n我們換個方向思考，不知道大家有沒有玩過 database 的 trigger，下圖為 MySQL trigger，\n\n![](http://i.imgur.com/Nkr2AHY.png)\n\n這時候你可能會想，那我們可以透過 signals 建立類似行為的功能嗎 ?\n\n答案是可以的 ! 我們在這裡就暫時不介紹，下次我會在對 signals 做更深\n\n入的介紹，這邊大家先知道一個概念就好 :kissing_smiling_eyes:\n\n開始介紹範例的 signals，\n\n設定 signals，以該專案為例，先在 payment/[apps.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/payment/apps.py) 裡面增加以下程式碼\n\n```python\nfrom django.apps import AppConfig\n\n\nclass PaymentConfig(AppConfig):\n    name = 'payment'\n    verbose_name = 'Payment'\n\n    def ready(self):\n        # import signal handlers\n        import payment.signals\n```\n\n AppConfig.ready() 的說明可以參考\n[https://docs.djangoproject.com/en/1.11/ref/signals/#class-prepared](https://docs.djangoproject.com/en/1.11/ref/signals/#class-prepared)\n\n以下擷取官方說明\n\n***If you provide an AppConfig instance as the sender argument, please ensure that the signal is registered in ready(). AppConfigs are recreated for tests that run with a modified set of INSTALLED_APPS (such as when settings are overridden) and such signals should be connected for each new AppConfig instance.***\n\n接著再將  payment/[__init__.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/payment/__init__.py) 裡面增加以下程式碼，用意主要是告訴 Django 我們設定的路徑\n\n```python\ndefault_app_config = 'payment.apps.PaymentConfig'\n```\n\n更多詳細的說明可參考官網\n[https://docs.djangoproject.com/en/1.11/topics/signals/](https://docs.djangoproject.com/en/1.11/topics/signals/)\n\n說明\n\n***Since this signal is sent during the app registry population process, and AppConfig.ready() runs after the app registry is fully populated, receivers cannot be connected in that method. One possibility is to connect them AppConfig.__init__() instead, taking care not to import models or trigger calls to the app registry.***\n\npayment/[signals.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/payment/signals.py)\n\n```python\ndef payment_notification(sender, **kwargs):\n    ipn_obj = sender\n    if ipn_obj.payment_status == ST_PP_COMPLETED:\n\n        # Check that the receiver email is the same we previously\n        # set on the `business` field. (The user could tamper with\n        # that fields on the payment form before it goes to PayPal)\n        if ipn_obj.receiver_email != settings.PAYPAL_RECEIVER_EMAIL:\n            # Not a valid payment\n            return\n\n        # payment was successful\n        order = get_object_or_404(Order, id=ipn_obj.invoice)\n        # mark the order as paid\n        order.paid = True\n        order.save()\n\n\nvalid_ipn_received.connect(payment_notification)\n```\n\n所以當 `payment_notification` 收到來自 PayPal 的 signals，就會去處理對應的事情。\n\n### 認識 ngrok\n\n先說明一下為什麼會需要使用到 ngrok，當我們付款成功時， PayPal 要發送一個\n\n付款的狀態通知我們的網站，但因為我們現在是在本機測試，所以並不是一個公開\n\n的網址 ( PayPal 無法通知我們 )，所以我們要透過 ngrok 接收  IPN 的通知，在 PayPal\n\n的文件 [https://django-paypal.readthedocs.io/en/stable/standard/ipn.html#testing](https://django-paypal.readthedocs.io/en/stable/standard/ipn.html#testing) 中\n\n也有說明，如要測試 IPN ，必須透過 ngrok ，不能使用 localhost ( 本機 )。\n\n請去下載 [Ngrok](https://ngrok.com/) ，免安裝版本，解壓縮即可使用，簡易的使用可以參考我之\n\n前寫的 [如何使用-ngrok](https://github.com/twtrubiks/facebook-messenger-bot-tutorial#如何使用-ngrok)，使用方法很簡單 :laughing:\n\n## 執行畫面\n\n [Django](https://www.djangoproject.com/) 預設後台\n\n [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/)\n\n![alt tag](http://i.imgur.com/f805kiP.png)\n\n![alt tag](http://i.imgur.com/TO5FV93.png)\n\n![alt tag](http://i.imgur.com/Zv0yKfL.png)\n\n首頁 - 商品清單頁\n\n[http://127.0.0.1:8000/](http://127.0.0.1:8000/)\n\n![alt tag](http://i.imgur.com/cOmDHa3.png)\n\n商品說明頁\n\n![alt tag](http://i.imgur.com/hNtpyT4.png)\n\n簡易購物車\n\n![alt tag](http://i.imgur.com/one3ZcU.png)\n\n![alt tag](http://i.imgur.com/IWJ19We.png)\n\n確認購物明細\n\n![alt tag](http://i.imgur.com/nB8nxZz.png)\n\n輸入個人資料\n\n![alt tag](http://i.imgur.com/d5MvfRv.png)\n\n使用 PayPal 付款\n\n![alt tag](http://i.imgur.com/qWqj12R.png)\n\n輸入測試買家帳號，\n\n以下提供我自己的測試買家帳號\n\n帳號 : dikeooel3ski-buyer@gmail.com\n\n密碼 : djurwo,wfeqwe3\n\n![alt tag](http://i.imgur.com/gyupZoO.png)\n\n![alt tag](http://i.imgur.com/5plwaSA.png)\n\n![alt tag](http://i.imgur.com/OJ0FBny.png)\n\n![alt tag](http://i.imgur.com/R6zIl6M.png)\n\n付款後，你會發現 python console，發送了一封信，\n\n![alt tag](http://i.imgur.com/IoT40Xn.png)\n\n因為在  [settings.py](https://github.com/twtrubiks/django-shop-tutorial/blob/master/django_shop_tutorial/settings.py) 中是使用 console 做測試\n\n```python\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\n```\n\n如要真的要寄出一封信，可參考 [使用 Django 發送信件](https://github.com/twtrubiks/django_social_login_tutorial#%E4%BD%BF%E7%94%A8-django--%E7%99%BC%E9%80%81%E4%BF%A1%E4%BB%B6) ，基本上改一下設定就可以使用了。\n\n之後再到後台觀看\n\n![alt tag](http://i.imgur.com/CmYkF2s.png)\n\n![alt tag](http://i.imgur.com/5W2RWnL.png)\n\n接著我們再到 PayPal 沙盒  ( sandbox )  中\n\n[https://www.sandbox.paypal.com/signin](https://www.sandbox.paypal.com/signin)\n\n使用測試  facilitator 登入，\n\n以下提供我自己的測試買家帳號\n\n帳號 : dikeooel3ski-facilitator@gmail.com\n\n密碼 : djurwo,wfeqwe3\n\n接受這筆付款\n\n![alt tag](http://i.imgur.com/zqB3ju4.png)\n\n當你一按接受，PayPal 就會發送一個 IPN 的通知。\n\n請記得，這裡就是我們要用 ngrok 的原因，透過 ngrok 接收  IPN 的通知，如下圖\n\n![alt tag](http://i.imgur.com/UqfGLVB.png)\n\n![alt tag](http://i.imgur.com/xaRwZtT.png)\n\n從圖中可以看到我們收到了一個新的 IPN 通知，並且狀態是 Completed\n\n## 後記\n\n相信大家有認識到不少東西，像是  [Django](https://github.com/django/django)  的 signals 以及 context-processors ，\n\n甚至是簡單的 PayPal 付款。\n\n寄送信件的部分，其實可以搭配 [celery](http://www.celeryproject.org/) ，未來我會再介紹這個東西，也因為這次\n\n介紹的東西非常多，所以可能有解釋不清楚的地方，如果你有任何問題歡迎詢問我。\n\n## 執行環境\n\n* Python 3.6.2\n\n## Reference\n\n* [Django](https://www.djangoproject.com/)\n* [django-paypal](https://github.com/spookylukey/django-paypal)\n\n## Donation\n\n文章都是我自己研究內化後原創，如果有幫助到您，也想鼓勵我的話，歡迎請我喝一杯咖啡:laughing:\n\n![alt tag](https://i.imgur.com/LRct9xa.png)\n\n[贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8)\n\n## License\n\nMIT license\n","funding_links":["https://www.paypal.com/tw/webapps/mpp/home","https://www.paypal.com/us/home","https://developer.paypal.com/","https://www.sandbox.paypal.com/signin"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwtrubiks%2Fdjango-shop-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwtrubiks%2Fdjango-shop-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwtrubiks%2Fdjango-shop-tutorial/lists"}