Django, htmx, Stripe을 사용하여 단일 제품 전자상거래 웹사이트를 만드는 두 부분으로 구성된 시리즈 중 첫 번째입니다. 이 부분에서는 Django 프로젝트를 시작하고 이를 htmx와 통합하겠습니다.
2부에서는 Stripe로 주문을 처리해보겠습니다.
가자!
다음과 같은 이유로 Django, htmx 및 Stripe를 사용하여 웹사이트를 만들 예정입니다.
최종 제품의 작동 방식은 다음과 같습니다.
이제 Django 프로젝트를 구성하고, 초기 보기를 생성하고, htmx로 구매 양식을 작성해 보겠습니다.
프로젝트를 설정하려면 가상 환경을 생성하고 활성화한 후 필요한 패키지를 설치해야 합니다. 그런 다음 Django 프로젝트와 Django 앱을 만들 수 있습니다.
가상 환경을 만드는 것부터 시작하여 종속성을 분리해 보겠습니다.
python -m venv .venv
Linux/Mac에서 활성화하는 방법은 다음과 같습니다.
source .venv/bin/activate
Windows의 경우:
.venv\Scripts\activate
활성화된 가상 환경 내에서 이 작업을 수행하려면 몇 가지 패키지가 필요합니다.
pip install django stripe django-htmx python-dotenv
여기에는 다음이 설치되어 있습니다.
가상 환경과 동일한 디렉터리에 ecommerce_site라는 Django 프로젝트를 생성해 보겠습니다.
django-admin startproject ecommerce_site .
Django에서는 하나 이상의 "앱"으로 코드를 구성하는 것이 좋습니다. 각 앱은 특정한 작업을 수행하는 패키지입니다. 프로젝트에는 여러 개의 앱이 있을 수 있지만 이 간단한 상점의 경우 전자 상거래 플랫폼의 보기, 양식 및 모델과 같은 대부분의 코드를 포함하는 하나의 앱만 있으면 됩니다. 이를 만들어 전자상거래라고 부르겠습니다.
python manage.py startapp ecommerce
이 앱을 ecommerce_site/settings.py의 INSTALLED_APPS에 추가하세요.
# ecommerce_site/settings.py INSTALLED_APPS = [ # ... the default django apps "ecommerce", # ⬅️ new ]
이 설정에 문제가 있는 경우 최종 제품을 확인하세요. 이 단계에서 파일 구조는 다음과 같아야 합니다.
ecommerce_site/ ├── .venv/ # ⬅️ the virtual environment ├── ecommerce_site/ # ⬅️ the django project configuration │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── ecommerce/ # ⬅️ our app setup │ ├── templates/ │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ └── views.py └── manage.py
이제 프로젝트 구성이 완료되었으므로 기본 레이아웃을 생성해야 합니다. 템플릿 디렉토리에 다른 모든 템플릿이 상속받을 템플릿인 base.html 파일을 추가하세요. 사용자 상호 작용을 위한 htmx, 기본 스타일을 위한 mvp.css 및 Django 생성 메시지를 템플릿에 추가합니다.
<!-- ecommerce/templates/base.html --> <!DOCTYPE html> <html lang="en"> <head> <title>One-Product E-commerce Site</title> <!-- include htmx ⬇️ --> <script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous" ></script> <!-- include mvp.css ⬇️ --> <link rel="stylesheet" href="https://unpkg.com/mvp.css" /> </head> <body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}' hx-boost="true"> <header> <h1>One-Product E-commerce Site</h1> </header> <main> <section> {% if messages %} {% for message in messages %} <p><mark>{{ message }}</mark></p> {% endfor %} {% endif %} </section> {% block content %} {% endblock %} </main> </body> </html>
동일한 templates 디렉토리에 홈 뷰용 home.html 템플릿을 만듭니다. base.html을 확장하고 해당 콘텐츠 섹션을 채워야 합니다.
<!-- ecommerce/templates/home.html --> {% extends "base.html" %} {% block content %} <section>{% include "product.html" %}</section> {% endblock %}
이 템플릿에는 product.html 템플릿이 포함되어 있습니다. product.html은 제품 및 자리 표시자 이미지에 대한 일부 세부정보를 렌더링합니다. 동일한 templates 디렉토리에 생성해 보겠습니다.
<!-- ecommerce/templates/product.html --> <form> <img src="https://picsum.photos/id/30/400/250" alt="mug" /> <h3>mug<sup>on sale!</sup></h3> <p>mugs are great - you can drink coffee on them!</p> <p><strong>5€</strong></p> <button type="submit" id="submit-btn">Buy</button> </form>
ecommerce/views.py에서 홈 템플릿을 렌더링할 뷰를 생성합니다.
# ecommerce/views.py from django.shortcuts import render def home(request): return render(request, 'home.html')
ecommerce_site/urls.py의 urlpatterns에 추가하세요.
# ecommerce_site/urls.py from django.contrib import admin from django.urls import path from ecommerce import views # ⬅️ new urlpatterns = [ path("admin/", admin.site.urls), path("", views.home, name="home"), # ⬅️ new ]
이제 다음을 사용하여 서버를 실행할 수 있습니다.
python manage.py runserver
브라우저에서 http://127.0.0.1:8000으로 이동하면 다음과 같은 내용이 표시됩니다.
It might feel like overkill to add a dedicated product.html template instead of just the product details in the home.html template, but product.html will be useful for the htmx integration.
Great! We now have a view that looks good. However, it doesn’t do much yet. We'll add a form and set up the logic to process our product purchase. Here’s what we want to do:
Let's go step by step.
Let’s first create and add a simple order form to our view allowing a user to select the number of mugs they want. In ecommerce/forms.py, add the following code:
# ecommerce/forms.py from django import forms class OrderForm(forms.Form): quantity = forms.IntegerField(min_value=1, max_value=10, initial=1)
In ecommerce/views.py, we can initialize the form in the home view:
# ecommerce/views.py from ecommerce.forms import OrderForm # ⬅️ new def home(request): form = OrderForm() # ⬅️ new - initialize the form return render(request, "home.html", {"form": form}) # ⬅️ new - pass the form to the template
And render it in the template:
<!-- ecommerce/templates/product.html --> <form method="post"> <!-- Same product details as before, hidden for simplicity --> <!-- render the form fields ⬇️ --> {{ form }} <!-- the same submit button as before ⬇️ --> <button type="submit" id="submit-btn">Buy</button> </form>
When the user clicks "Buy", we want to process the corresponding POST request in a dedicated view to separate the different logic of our application. We will use htmx to make this request. In the same ecommerce/templates/product.html template, let's extend the form attributes:
<!-- ecommerce/templates/product.html --> <!-- add the hx-post html attribute ⬇️ --> <form method="post" hx-post="{% url 'purchase' %}"> <!-- Same product details as before, hidden for simplicity --> {{ form }} <button type="submit" id="submit-btn">Buy</button> </form>
With this attribute, htmx will make a POST request to the purchase endpoint and stop the page from reloading completely. Now we just need to add the endpoint.
The purchase view can be relatively simple for now:
# ecommerce/views.py import time # ⬅️ new # new purchase POST request view ⬇️ @require_POST def purchase(request): form = OrderForm(request.POST) if form.is_valid(): quantity = form.cleaned_data["quantity"] # TODO - add stripe integration to process the order # for now, just wait for 2 seconds to simulate the processing time.sleep(2) return render(request, "product.html", {"form": form})
In this view, we validate the form, extract the quantity from the cleaned data, and simulate Stripe order processing. In the end, we return the same template (product.html). We also need to add the view to the urlpatterns:
# ecommerce_site/urls.py # ... same imports as before urlpatterns = [ path("admin/", admin.site.urls), path("", views.home, name="home"), path("purchase", views.purchase, name="purchase"), # ⬅️ new ]
We now need to tell htmx what to do with this response.
Htmx has a hx-swap attribute which replaces targeted content on the current page with a request's response.
In our case, since the purchase view returns the same template, we want to swap its main element — the