Архітектура Django MTV

Проектний сайт:

https://github.com/skywalker0803r/django_ecommerce

pic

Django — це веб-фреймворк, заснований на архітектурі MTV. MTV — це абревіатура від Model (Модель), Template (Шаблон) і View (Подання). Це схоже на MVC-архітектуру, але з деякими відмінностями в найменуванні. Ось детальне пояснення архітектури Django MTV:

1. Model (Модель)

Функції:

  • Взаємодіє з базою даних, є логічним рівнем даних додатку.
  • Відповідає за визначення структури даних (наприклад, структури таблиць) і виконання операцій з даними.
  • Управляє запитами до бази даних і операціями через ORM (об'єктно-реляційне відображення) Django, що дозволяє розробникам не писати SQL-запити вручну.
from django.db import models  

class Product(models.Model):  
 name = models.CharField(max_length=100)  
 price = models.DecimalField(max_digits=10, decimal_places=2)  
 description = models.TextField()  

 def __str__(self):  
 return self.name

У цьому прикладі модель Product визначає таблицю даних, що містить поля для назви продукту, ціни та опису.
Template (Шаблон)

Функції:

  • Відповідає за представлення даних користувачеві, є рівнем відображення додатку.
  • Використовує мову шаблонів Django (Django Template Language, DTL) для динамічного генерування HTML-сторінок, підтримує вставку змінних, умовні вирази, цикли та інше.
  • Окремо обробляє логіку Python від фронтенду HTML.







Список товарів
    {% for product in products %}    
{{ product.name }} - {{ product.price }}
    {% endfor %}    
       ```  Цей шаблон приймає дані `products` і динамічно генерує список товарів.
View (Вид)

**Функції:**

- Відповідає за бізнес-логіку та обробку даних, є рівнем контролю додатку.
- Приймає запити від користувачів, викликає моделі для обробки даних і передає результат у шаблон.
- Вид Django зазвичай реалізується у вигляді Python-функцій або класів.

from django.shortcuts import render
from .models import Product

def productlist(request):
products = Product.objects.all() # Отримати всі товари
return render(request, 'product
list.html', {'products': products})
```

Ця функція виду витягує всі дані про товари з моделі та передає їх шаблону product_list.html для рендерингу.

Відповідність MTV та MVC

У Django структура MTV може бути трохи заплутаною через назву View. У Django:

  • Model (M): відповідає за Model у MVC.
  • Template (T): відповідає за View (вид/фронтенд) у MVC.
  • View (V): відповідає за Controller (контролер) у MVC.

«View» у Django обробляє запити та відповіді, а «Template» відповідає за інтерфейс користувача.

Загальний робочий процес

  1. Запит користувача: Користувач надсилає запит через браузер.
  2. Маршрутизація URL: URL-конфігурація Django спрямовує запит до відповідної функції виду.
  3. Обробка логіки у View: Функція виду обробляє бізнес-логіку, отримує або оновлює дані з моделі.
  4. Рендеринг шаблону: Функція виду передає дані до шаблону, який генерує динамічний HTML.
    Відповідь на запит: згенеровану HTML-сторінку повертають користувачу.

Використовуючи MTV-архітектуру Django для створення інтернет-магазину, ми можемо поділити його структуру на наступні каталоги та відповідно до функціональних потреб вебсайту спроектувати Model (Моделі), Template (Шаблони) та View (Види).

ecommerce_project/  
│  
├── ecommerce/ # Основна директорія застосунку  
│ ├── migrations/ # Файли міграцій бази даних  
│ ├── static/ # Статичні файли (CSS, JS, зображення тощо)  
│ ├── templates/ # HTML шаблони  
│ │ ├── base.html # Шаблон основної розкладки  
│ │ ├── product_list.html # Шаблон списку товарів  
│ │ └── product_detail.html # Шаблон деталей товару  
│ ├── admin.py # Налаштування панелі адміністратора  
│ ├── apps.py # Конфігурація застосунку  
│ ├── models.py # Визначення моделей даних  
│ ├── views.py # Визначення функцій видів  
│ ├── urls.py # Налаштування маршрутизації  
│ └── tests.py # Юніт-тести  
├── ecommerce_project/  
│ ├── __init__.py # Файл ініціалізації  
│ ├── settings.py # Конфігураційний файл  
│ ├── urls.py # Головна точка входу для маршрутизації  
│ ├── asgi.py # Налаштування ASGI  
│ └── wsgi.py # Налаштування WSGI  
└── manage.py # Точка входу для керування командами

Проектування Model (Моделей)

У файлі models.py визначаємо наступні моделі з коментарями китайською мовою:

from django.db import models  

# Модель товару  
class Product(models.Model):  
 name = models.CharField(max_length=100) # Назва товару  
 price = models.DecimalField(max_digits=10, decimal_places=2) # Ціна товару  
 description = models.TextField() # Опис товару  
 stock = models.PositiveIntegerField() # Кількість товарів на складі  
 created_at = models.DateTimeField(auto_now_add=True) # Час створення  
 updated_at = models.DateTimeField(auto_now=True) # Час оновлення  

 def __str__(self):  
 return self.name # Повертає назву товару як рядкове представлення моделі  

# Модель категорії  
class Category(models.Model):  
 name = models.CharField(max_length=50) # Назва категорії  
 description = models.TextField(blank=True, null=True) # Опис категорії  
 created_at = models.DateTimeField(auto_now_add=True) # Час створення  

 def __str__(self):  
 return self.name # Повертає назву категорії як рядкове представлення моделі

Проектування View (Видів)

У файлі views.py визначаємо наступні функції видів з коментарями китайською мовою:

from django.shortcuts import render, get_object_or_404  
from .models import Product  

# Функція виду для списку товарів  
def product_list(request):  
 products = Product.objects.all() # Отримуємо всі товари  
 return render(request, 'product_list.html', {'products': products}) # Рендеримо шаблон списку товарів  

# Функція виду для деталей товару  
def product_detail(request, product_id):  
 product = get_object_or_404(Product, id=product_id) # Отримуємо товар за ID, або повертаємо 404, якщо не знайдено  
 return render(request, 'product_detail.html', {'product': product}) # Рендеримо шаблон деталей товару

Налаштування маршрутизації URL

У файлі urls.py визначаємо маршрути з коментарями китайською мовою:

У застосунку ecommerce, який ми створили, потрібно додати файл urls.py для керування маршрутизацією всіх URL. Припустимо, що ім'я нашого застосунку — ecommerce, можна зробити наступне:

У файлі ecommerce/urls.py додаємо маршрути:

from django.urls import path  
from .
import views  

urlpatterns = [  
 path('', views.product_list, name='product_list'), # Маршрут для сторінки списку товарів  
 path('product/<int:product_id>/', views.product_detail, name='product_detail'), # Маршрут для сторінки деталей товару  
]

Зміни в основному файлі urls.py

Основний файл urls.py застосунку використовується для підключення конфігурацій URL інших застосунків та прив'язки їх до кореневого маршруту.

Припустимо, що назва вашого основного проекту — ecommerce_project, у файлі ecommerce_project/urls.py вам потрібно підключити маршрути застосунку ecommerce. Ось як це виглядатиме:

from django.contrib import admin  
from django.urls import path, include # include використовується для підключення URL конфігурацій інших застосунків  

urlpatterns = [  
 path('admin/', admin.site.urls), # Маршрут до адміністративної панелі Django  
 path('', include('ecommerce.urls')), # Підключаємо маршрути для застосунку ecommerce, щоб корінь маршруту вказував на відповідні вигляди цього застосунку  
]

Проектування Template (Шаблонів)

1.

base.html` (Базовий шаблон)

<!DOCTYPE html>
<html lang="uk">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Електронна комерція{% endblock %}</title>
</head>
<body>
    <header>
        <h1>Електронна комерція</h1>
        <nav>
            <a href="/">Головна</a>
        </nav>
    </header>

    {% block content %}{% endblock %}

    <footer>
        <p>© 2024 Електронна комерція</p>
    </footer>
</body>
</html>

2. product_list.html (Шаблон списку товарів)

{% extends 'base.html' %}

{% block title %}Список товарів{% endblock %}

{% block content %}
<h2>Список товарів</h2>
    {% for product in products %}
        <p>{{ product.name }} - {{ product.price }} грн</p>
    {% endfor %}
{% endblock %}

3.

product_detail.html` (Шаблон деталей товару)

{% extends 'base.html' %}  

{% block title %}{{ product.name }} Деталі{% endblock %}  

{% block content %}  

<h2>{{ product.name }}</h2>
<p>Ціна: {{ product.price }} грн</p>
<p>Опис: {{ product.description }}</p>
<p>Наявність на складі: {{ product.stock }} одиниць</p>
<a href="{% url 'product_list' %}">Повернутись до списку товарів</a>

{% endblock %}

Цей набір шаблонів надає основні функції управління товарами, включаючи:
1. Відображення списку товарів.
2.
Перегляд деталей товару

Зміна INSTALLED_APPS

У файлі settings.py знайдіть та змініть INSTALLED_APPS, додавши ваше застосування ecommerce:

INSTALLED_APPS = [  
 'django.contrib.admin', # Адміністративна панель Django  
 'django.contrib.auth', # Система аутентифікації користувачів  
 'django.contrib.contenttypes', # Система типів контенту Django  
 'django.contrib.sessions', # Управління сесіями  
 'django.contrib.messages', # Повідомлення  
 'django.contrib.staticfiles', # Обробка статичних файлів  
 'ecommerce', # Додайте ваше застосування 'ecommerce'  
]

Міграція бази даних

Якщо ви створили нові моделі (наприклад, модель товару Product), вам потрібно здійснити міграцію бази даних. Це дозволить Django створити або оновити таблиці бази даних відповідно до ваших моделей.

Виконайте наступні команди в терміналі:

python manage.py makemigrations  
python manage.py migrate

Створення суперкористувача (необов'язково)

Якщо ви хочете отримати доступ до адміністративної панелі Django, ви можете створити обліковий запис суперкористувача. Виконайте наступну команду:

python manage.py createsuperuser

Запуск серверу розробки

Запустіть сервер розробки Django, щоб перевірити, чи правильно працює сайт:

python manage.py runserver

Ця команда запустить сервер розробки, і за замовчуванням він буде працювати на http://127.0.0.1:8000/.

Відвідування вашого застосунку

  • Відкрийте браузер і перейдіть за адресою http://127.0.0.1:8000/, ви повинні побачити сторінку списку товарів.
  • Відвідайте http://127.0.0.1:8000/product/1/ (якщо товар з ID 1 вже існує), щоб переглянути деталі товару.

pic

5. Адміністративна панель (необов'язково)

  • Якщо ви створили суперкористувача, можете увійти в адміністративну панель Django для управління товарами.
  • Відкрийте браузер і перейдіть за адресою http://127.0.0.1:8000/admin/, використовуючи обліковий запис суперкористувача для входу.

pic

В адміністративній панелі Django ви можете додавати нові товари, але для цього потрібно спершу зареєструвати модель Product в адміністративній панелі Django, щоб мати змогу здійснювати операції додавання, редагування, видалення та перегляду товарів.

1. Реєстрація моделі в admin.py

Відкрийте файл ecommerce/admin.py і зареєструйте модель Product, щоб вона відображалася в адміністративній панелі. Це можна зробити так:

from django.contrib import admin  
from .models import Product, Category  

# Реєстрація моделі товару  
admin.site.register(Product)  

# За потреби можна зареєструвати модель категорії товару  
admin.site.register(Category)

pic

  • В адміністративній панелі Django ви повинні побачити моделі Product та Category. Натискаючи на них, ви зможете додавати, редагувати або видаляти товари.

5. Додавання товару

В адміністративній панелі:

  1. Натисніть на Products.
  2. Натисніть кнопку Add product у верхньому правому куті.
    3.
    Заповніть інформацію про товар: назву, ціну, опис, кількість на складі тощо.
  3. Натисніть Save, щоб зберегти товар.

Таким чином, ви зможете легко керувати товарами через адміністративну панель.

Додавання підтримки зображень товарів в модель Django

Щоб модель товару в Django підтримувала зображення і зберігала їх у папці images, вам потрібно виконати кілька кроків для налаштування функціоналу завантаження файлів.

1. Оновлення моделі Product

У моделі Product додайте поле ImageField, яке буде зберігати зображення товару. Наприклад:

from django.db import models  

class Product(models.Model):  
 name = models.CharField(max_length=100) # Назва товару  
 price = models.DecimalField(max_digits=10, decimal_places=2) # Ціна товару  
 description = models.TextField() # Опис товару  
 stock = models.PositiveIntegerField() # Кількість товару на складі  
 created_at = models.DateTimeField(auto_now_add=True) # Час створення  
 updated_at = models.DateTimeField(auto_now=True) # Час оновлення  
 image = models.ImageField(upload_to='images/', blank=True, null=True) # Зображення товару, зберігається в директорії 'media/images/'  

 def __str__(self):  
 return self.name

Налаштування медіа-файлів

У файлі `settings.py` налаштуйте шлях для зберігання медіа-файлів:

import os

Налаштування медіа-файлів

MEDIAURL = '/media/' # Префікс URL для доступу до медіа-файлів
MEDIA
ROOT = os.path.join(BASE_DIR, 'media') # Шлях для фактичного зберігання медіа-файлів
```

Таким чином, всі завантажені зображення будуть зберігатися у папці media/ в кореневому каталозі проекту, а шлях до зображень буде виглядати як media/images/.

3. Налаштування URL маршрутів для обробки медіа-файлів

У файлі urls.py вам потрібно додати налаштування для доступу до медіа-файлів. Це дозволить вам в режимі розробки легко доступати зображення через URL.

Відкрийте файл ecommerce_project/urls.py і додайте наступний код наприкінці файлу:

from django.conf import settings  
from django.conf.urls.static import static  

urlpatterns = [  
 path('admin/', admin.site.urls),  
 path('', include('ecommerce.urls')),  
]  

# URL маршрути для медіа-файлів  
if settings.DEBUG:  
 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Таким чином, у режимі розробки Django автоматично оброблятиме файли, що знаходяться за шляхом MEDIA_URL.
Відображення зображень у адміністративній панелі

Якщо ви хочете, щоб зображення товарів відображалися в адміністративній панелі, ви можете налаштувати відображення товарів через клас ProductAdmin у файлі admin.py.

Наприклад:

from django.contrib import admin  
from .models import Product  
from django.utils.html import format_html  

class ProductAdmin(admin.ModelAdmin):  
 list_display = ('name', 'price', 'stock', 'image_display') # Відображення поля зображення  
 search_fields = ('name',)  

 # Налаштування відображення зображення  
 def image_display(self, obj):  
 if obj.image:  
 return format_html('<img src="{}" width="100" />', obj.image.url)  
 return 'Без зображення'  

 image_display.short_description = 'Зображення товару' # Назва поля  

admin.site.register(Product, ProductAdmin)

Таким чином, у адміністративній панелі зображення товарів буде відображатися у вигляді ескізів.

5. Завантаження зображень товарів

Коли ви додаєте або редагуєте товар у адміністративній панелі Django, ви побачите поле image. Після того, як ви виберете зображення, воно буде завантажено в папку media/images/, і з'явиться на сторінці з деталями товару.
Відображення зображення товару

У файлі product_detail.html ви можете відобразити зображення товару за допомогою URL зображення:

{% extends 'base.html' %}  

{% block title %}{{ product.name }} Деталі{% endblock %}  

{% block content %}  

{{ product.name }}
Ціна: {{ product.price }} грн
Опис: {{ product.description }}
Запаси: {{ product.stock }} шт
      {% if product.image %}  
        <img src="{{ product.image.url }}" alt="{{ product.name }}" />  
      {% else %}    
Без зображення  
   {% endif %}  
Повернутися до списку товарів  
{% endblock %}  

Таким чином, якщо товар має зображення, воно буде відображатися; якщо зображення немає, з'явиться напис "Без зображення".

Підсумок

  • Використовуйте ImageField для обробки зображень товарів.
  • Налаштуйте MEDIA_URL та MEDIA_ROOT, щоб вказати директорію для зберігання зображень.
  • Змініть urls.py, щоб забезпечити доступ до медіа-файлів за URL.
  • Відображення зображень у адміністративній панелі та на передньому плані.

Після цієї конфігурації ви зможете успішно завантажувати та відображати зображення товарів у Django.

Помилка: table ecommerce_product has no column named image

Коли ви зустрічаєте помилку table ecommerce_product has no column named image, це зазвичай означає, що ви додали поле image у модель, але ще не виконали міграцію або міграція не була застосована правильно до бази даних.

Кроки для вирішення:

1.
Переконайтеся, що поле image коректно додано до моделі

По-перше, переконайтесь, що в моделі Product дійсно є поле image, як показано нижче:

from django.db import models  

class Product(models.Model):  
 name = models.CharField(max_length=100) # Назва товару  
 price = models.DecimalField(max_digits=10, decimal_places=2) # Ціна товару  
 description = models.TextField() # Опис товару  
 stock = models.PositiveIntegerField() # Кількість товару на складі  
 created_at = models.DateTimeField(auto_now_add=True) # Час створення  
 updated_at = models.DateTimeField(auto_now=True) # Час оновлення  
 image = models.ImageField(upload_to='images/', blank=True, null=True) # Зображення товару, зберігається в директорії 'media/images/'  

 def __str__(self):  
 return self.name

Якщо цей код вірний, наступним кроком буде перевірка, чи застосовано міграцію.

1.
Виконання міграції

У Django зміни в моделях (наприклад, додавання полів) потрібно застосовувати до бази даних через міграції. Виконайте наступні кроки для створення та застосування міграції:

Крок 1: Створення файлу міграції

Виконайте наступну команду для створення файлу міграції, який перевірить зміни в моделях та створить файл міграції:

python manage.py makemigrations

Це створить файл міграції в директорії migrations/ вашого додатку, який описує зміни в базі даних.

Крок 2: Застосування міграції

Виконайте наступну команду, щоб застосувати міграцію до бази даних:

python manage.py migrate

Це застосує зміни в моделях (наприклад, додавання поля image) до бази даних та оновить таблицю ecommerce_product.

Крок 3: Перевірка бази даних

Після завершення міграції переконайтеся, що в таблиці ecommerce_product з'явилось поле image. Ви можете використати інструменти для керування базою даних Django (наприклад, SQLite з DB Browser або інші інструменти баз даних), щоб перевірити структуру бази даних і переконатися, що поле додано.
1.
Перезапуск серверу розробки

Якщо ви завершили вищезгадані кроки, перезапустіть сервер розробки, щоб зміни в моделях та структурі бази даних набрали чинності:

python manage.py runserver

pic

Зміна product_list.html для відображення товарних зображень

Спочатку переконайтесь, що у шаблоні вгорі є підключення до статичних та медіафайлів:

{% extends 'base.html' %}  
{% load static %}  

{% block title %}Список товарів{% endblock %}  

{% block content %}  

Список товарів
    {% for product in products %}    
{{ product.name }}
Ціна: {{ product.price }} грн
Кількість на складі: {{ product.stock }} шт
Опис: {{ product.description }}
       {% if product.image %}            {% else %}    
Без зображення
    {% endif %}       Переглянути деталі    
    {% endfor %}   
      {% endblock %} 

pic
Сторінка товару

pic

Зміна сторінки товару product_detail.html для відображення зображення

{% extends 'base.html' %}      
{% block title %}{{ product.name }} Деталі{% endblock %}      
{% block content %}   
{{ product.name }}
Ціна: {{ product.price }} грн
Опис: {{ product.description }}
Кількість на складі: {{ product.stock }} шт
      {% if product.image %}           {% else %}    
Без зображення
   {% endif %}      
Повернутися до списку товарів   
{% endblock %}

pic
pic

Додавання файлу style.css

Використання підключеного зовнішнього стилю для покращення зовнішнього вигляду HTML

Крок 1: Налаштування папки static

1.
Створення папки static в додатку

Припустимо, у вас є додаток з назвою ecommerce. Ви можете створити папку static в каталозі ecommerce і помістити ваші CSS файли туди. Структура папок буде виглядати так:

ecommerce/  
├── static/  
│ └── css/  
│ └── style.css # Ваш CSS файл  
└── models.py  
└── views.py  
└── admin.py

Крок 2: Підключення CSS файлу в HTML

У шаблоні HTML вам потрібно використовувати тег {% load static %} для завантаження статичних ресурсів, а потім використовувати {% static 'css/style.css' %} для підключення вашого CSS файлу.

Припустимо, ваш файл style.css знаходиться в папці static/css/, підключіть його наступним чином у файлі base.html або іншому шаблоні:

{% load static %}  












 {% block content %}  

 {% endblock %}  


Крок 3: Написання CSS в style.css

Тепер ви можете написати стилі в файлі style.css, щоб покращити вигляд вашої сторінки. Наприклад:

/* static/css/style.css */  

body {
font-family: Arial, sans-serif;  
 background-color: #f4f4f4;  
 margin: 0;  
 padding: 0;  
}  

h1 {  
 color: #333;  
 text-align: center;  
 margin-top: 50px;  
}  

ul {  
 list-style-type: none;  
 padding: 0;  
}  

li {  
 background-color: #fff;  
 margin: 10px 0;  
 padding: 20px;  
 border-radius: 8px;  
 box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);  
}
/* static/css/style.css */  

/* Контейнер для списку товарів */  
.product-list {  
 display: flex;  
 flex-wrap: wrap; /* Дозволяє товарам переноситися на новий рядок */  
 gap: 20px; /* Проміжок між товарами */  
 justify-content: space-between; /* Вирівнює товари по краях */  
}  

/* Картка кожного товару */  
.product-item {  
 flex: 1 1 18%; /* Кожен товар займає 18% ширини, так що на одному рядку буде максимум 5 товарів */  
 background-color: #fff;  
 border-radius: 8px;  
 box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);  
 padding: 20px;  
 text-align: center;  
 transition: transform 0.3s;  
}  

/* Налаштування зображень товарів */  
.product-item img {  
 width: 100%; /* Робить ширину зображення рівною ширині контейнера */  
 height: auto; /* Автоматично регулює висоту зображення */  
 object-fit: contain; /* Масштабує зображення, зберігаючи пропорції, без обрізки */
max-height: 200px; /* Встановлює максимальну висоту зображення, щоб уникнути занадто високих зображень */  
 margin: 0 auto; /* Горизонтальне вирівнювання */  
 display: block; /* Робить зображення блочним елементом, що полегшує вирівнювання */  
}  

/* Ефект при наведенні курсору на картку товару */  
.product-item:hover {  
 transform: translateY(-5px); /* Піднімає картку товару при наведенні курсору */  
}  

h1 {  
 text-align: center;  
 margin-top: 50px;  
 color: #333;  
}  

ul {  
 list-style-type: none;  
 padding: 0;  
}  

li {  
 background-color: #fff;  
 margin: 10px 0;  
 padding: 20px;  
 border-radius: 8px;  
 box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);  
}  

/* static/css/style.css */  

/* Контейнер для сторінки детальної інформації про товар */  
.product-detail-container {  
 width: 80%; /* Встановлює ширину контейнера на 80%, можна коригувати за потребою */  
 max-width: 1200px; /* Встановлює максимальну ширину, щоб не було занадто великим */  
 margin: 0 auto; /* Горизонтальне вирівнювання */  
 padding: 20px; /* Відступи, щоб контент не прилягає до країв */  
 background-color: #fff; /* Колір фону */  
 border-radius: 8px; /* Закруглені кути */  
 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* Легкий ефект тіні */  
}  

/* Налаштування зображень товару */  
.product-detail-container img {  
 width: 100%;  
 height: auto;  
 object-fit: contain; /* Зберігає пропорції зображення */
max-height: 400px; /* Встановлює максимальну висоту зображення */  
 margin: 0 auto; /* Горизонтальне вирівнювання */  
 display: block; /* Робить зображення блочним елементом */  
}  

/* Вирівнювання тексту по центру */  
.product-detail-container h2,  
.product-detail-container p {  
 text-align: center;  
}  

/* Кнопка повернення */  
.product-detail-container a {  
 display: inline-block;  
 margin-top: 20px;  
 padding: 10px 20px;  
 background-color: #007bff;  
 color: #fff;  
 border-radius: 5px;  
 text-decoration: none;  
}  

.product-detail-container a:hover {  
 background-color: #0056b3;  
 text-decoration: none;  
}

Список товарів

{% extends 'base.html' %}  

{% block title %}Список товарів{% endblock %}  

{% block content %}  

Список товарів
    {% for product in products %}    
{{ product.name }}
Ціна: {{ product.price }} грн
На складі: {{ product.stock }} шт
Опис: {{ product.description }}
       {% if product.image %}
{% else %}  

Без зображення
    {% endif %}       Переглянути деталі        {% endfor %}         {% endblock %} ```  Деталі товару  ``` {% extends 'base.html' %}      {% block title %}{{ product.name }} Деталі{% endblock %}      {% block content %}   
{{ product.name }}
Ціна: {{ product.price }} грн
Опис: {{ product.description }}
На складі: {{ product.stock }} шт
       {% if product.image %}            {% else %}    
Без зображення
    {% endif %}       Повернутися до списку товарів   
   {% endblock %} ```  Щоб зробити `base.html` більш привабливим, забезпечити центроване вирівнювання, налаштувати кольорову палітру тощо, можна виконати наступні покращення:  1.
**Загальне розташування**: зробіть контент горизонтально вирівняним і встановіть розумну ширину, щоб сторінка виглядала охайно.  
2. **Кольорова схема**: виберіть чіткі та гармонійні кольори, щоб сторінка виглядала більш професійно.  
3. **Шрифт**: використовуйте зручний шрифт для покращення досвіду читання.  
4. **Стиль кнопок**: розробіть сучасний стиль кнопок.  
5.
**Тіні та ефекти закруглення**: використовуйте помірковані тіні та закруглені кути для покращення візуального ефекту.

Ось приклад оптимізованих файлів `base.html` та `style.css`:

{% load static %}

Електронна комерція
Головна
{% if user.is_authenticated %}
Ласкаво просимо, {{ user.username }}
Вийти
{% else %}
Увійти
{% endif %}
{% block content %} {% endblock %}
© 2024 Електронна комерція.

Авторські права захищені.

/* static/css/style.css */

/* Основні налаштування */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Arial', sans-serif;
    background-color: #f9fafb; /* Світло-сірий фон для свіжого вигляду */
    color: #333;
    line-height: 1.6;
}

/* Глобальний контейнер */
.container {
    width: 85%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

/* Верхнє навігаційне меню */
header {
    background-color: #007bff;
    color: #fff;
    padding: 20px 0;
    margin-bottom: 40px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

header h1 {
    text-align: center;
    font-size: 2.5em;
}

header nav {
    text-align: center;
    margin-top: 10px;
}

header nav ul {
    list-style-type: none;
}

header nav ul li {
    display: inline-block;
    margin: 0 20px;
}

header nav ul li a {
    color: #fff;
    font-size: 1.2em;
    text-decoration: none;
    padding: 10px 15px;
    border-radius: 5px;
    transition: background-color 0.3s;
}

header nav ul li a:hover {
    background-color: #0056b3;
}

/* Основний вміст */
main {
    background-color: #fff;
    padding: 40px 30px;
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

/* Нижній колонтитул */
footer {
    background-color: #333;
    color: #fff;
    padding: 20px;
    text-align: center;
    margin-top: 40px;
}

footer p {
    font-size: 1em;
}

/* Сторінка зі списком товарів */
.product-list {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    justify-content: space-around;
}

/* Картка товару */
.product-item {
    flex: 1 1 18%;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    padding: 20px;
    text-align: center;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.product-item:hover {
    transform: translateY(-5px);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}

/* Налаштування зображень товарів */
.product-item img {
    width: 100%;
    height: auto;
    object-fit: contain;
    max-height: 200px;
    display: block;
    margin: 0 auto;
}

/* Назва товару та ціна */
.product-item h3 {
    font-size: 1.2em;
    margin-top: 10px;
    color: #333;
}

.product-item p {
    font-size: 1.1em;
    color: #777;
    margin: 10px 0;
}

/* Сторінка з детальними відомостями про товар */
.product-detail-container {
    text-align: center;
    max-width: 900px;
    margin: 0 auto;
    background-color: #fff;
    padding: 40px;
    border-radius: 8px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

.product-detail-container h2 {
    font-size: 2em;
    margin-bottom: 20px;
}

.product-detail-container img {
    width: 80%;
    height: auto;
    object-fit: contain;
    margin-bottom: 30px;
}

.product-detail-container p {
    font-size: 1.2em;
    color: #555;
}

/* Кнопка "Назад" */
.product-detail-container a {
    display: inline-block;
    margin-top: 30px;
    padding: 10px 20px;
    background-color: #007bff;
    color: #fff;
    border-radius: 5px;
    text-decoration: none;
    transition: background-color 0.3s ease;
}

.product-detail-container a:hover {
    background-color: #0056b3;
}

/* Регулювання шрифтів для заголовків і абзаців у верхній та нижній частині сторінки */
h1, h2, h3, p {
    margin-bottom: 20px;
}

/* Налаштування кнопок */
button {
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 1em;
}

button:hover {
    background-color: #0056b3;
}

Додати функціонал входу

Оновіть файл urls.py, щоб налаштувати шляхи для входу та виходу користувачів

# ecommerce/urls.py
from django.urls import path
from .

import views # Тут повинно бути .views, а не ecommerce_project.views  
from django.contrib.auth import views as auth_views  

urlpatterns = [  
    path('', views.product_list, name='product_list'),  
    path('product//', views.product_detail, name='product_detail'),  
    path('signup/', views.signup, name='signup'), # Шлях для сторінки реєстрації  
    path('login/', auth_views.LoginView.as_view(), name='login'), # Вхід  
    path('logout/', views.user_logout, name='logout'), # Користувацький шлях для виходу  
    path('profile/', views.profile, name='profile'), # Сторінка профілю користувача  
]

Створення шаблону для сторінки входу

Створіть шаблон з назвою registration/login.html у папці templates.

{% extends 'base.html' %}  

{% block title %}Вхід{% endblock %}  

{% block content %}  

Будь ласка, увійдіть  
        {% csrf_token %}    {{ form.as_p }}    Увійти        
Немає акаунту? Зареєструйтесь
   {% endblock %} 

Тут ми використовуємо {{ form.as_p }}, щоб відобразити форму входу, і надаємо посилання на сторінку реєстрації.

Оновлення base.html для відображення імені користувача

Щоб відобразити ім'я користувача у правому верхньому куті, потрібно змінити частину навігації у base.html. Тут ми перевіримо, чи авторизований користувач, і відобразимо його ім'я.

{% load static %}  


    Електронна торгівля  
    Головна  
       {% if user.is_authenticated %}    
    Вітаємо, {{ user.username }}
    Вихід  
    {% else %}    
    Вхід  
    {% endif %}    
    {% block content %}    {% endblock %}    
    © 2024 Електронна торгівля.

Авторські права збережено.

Налаштування URL для входу та виходу в settings.py
Встановіть у settings.py параметр LOGIN_REDIRECT_URL, щоб вказати сторінку, на яку користувач буде перенаправлений після успішного входу. Зазвичай це буде сторінка profile, щоб користувачі могли переглядати свій профіль після входу.
```

Налаштування сторінки після входу

LOGINREDIRECTURL = '/profile/' # Налаштування сторінки після виходу
LOGOUTREDIRECTURL = '/'
```

Створення виду та шаблону для profile

Створіть вигляд profile, щоб показати особисту інформацію користувача.

from django.shortcuts import render, redirect, get_object_or_404  
from .models import Product  
from django.contrib.auth.decorators import login_required  
from django.contrib.auth.forms import UserCreationForm  
from django.contrib.auth import login, logout  

# Вигляд для списку товарів
def product_list(request):  
    products = Product.objects.all() # Отримати всі товари  
    return render(request, 'product_list.html', {'products': products}) # Відображення шаблону списку товарів  

# Вигляд для детальної інформації про товар
def product_detail(request, product_id):  
    product = get_object_or_404(Product, id=product_id) # Отримати товар за ID, якщо не знайдено - повертає 404  
    return render(request, 'product_detail.html', {'product': product}) # Відображення шаблону детальної інформації про товар  

# Вигляд для профілю користувача
@login_required  
def profile(request):  
    return render(request, 'profile.html')  

def signup(request):  
    if request.method == 'POST':  
        form = UserCreationForm(request.POST)  
        if form.is_valid():  
            user = form.save() # Зберегти користувача  
            login(request, user) # Автоматичний вхід  
            return redirect('profile') # Перенаправлення на профіль після реєстрації  
    else:  
        form = UserCreationForm()  
    return render(request, 'registration/signup.html', {'form': form})  

def user_logout(request):  
    logout(request) # Виконати вихід  
    return redirect('/') # Перенаправлення на головну сторінку після виходу  

У шаблоні profile.html можна показати деталі користувача:

{% extends 'base.html' %}  

{% block title %}Профіль{% endblock %}  

{% block content %}  
    Вітаємо, {{ user.username }}!  
    Це ваша сторінка профілю.  
    Ваша електронна адреса: {{ user.email }}  
    <a href="{% url 'logout' %}">Вихід</a>  
{% endblock %}

Створення функціоналу реєстрації в views.py

def signup(request):  
    if request.method == 'POST':  
        form = UserCreationForm(request.POST)  
        if form.is_valid():  
            user = form.save() # Зберегти користувача  
            login(request, user) # Автоматичний вхід  
            return redirect('profile') # Перенаправлення на профіль після реєстрації  
    else:  
        form = UserCreationForm()  
    return render(request, 'registration/signup.html', {'form': form})  

urls.py

urlpatterns = [  
    path('signup/', views.signup, name='signup'), # Шлях для сторінки реєстрації  
    # Інші шляхи...  
]  

текст перекладу

templates/registration/signup.html
```

{% extends 'base.html' %}

{% block title %}Реєстрація{% endblock %}

{% block content %}

Реєстрація нового акаунта
{% csrftoken %} {{ form.asp }} Зареєструватися
{% endblock %}
```

Реалізація функції виходу з акаунта

В views.py обробляється логіка виходу з акаунта

# views.py
from django.shortcuts import redirect
from django.contrib.auth import logout

def user_logout(request):
    logout(request)  # Виконуємо вихід
    return redirect('/')  # Після виходу перенаправляємо на головну сторінку (або на іншу, яку ви бажаєте)

Налаштування маршруту для виходу в urls.py

Щоб логіка виходу працювала, потрібно налаштувати маршрут для виходу в urls.py. Таким чином, коли користувач перейде за адресою /logout/, виконається функція user_logout.

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('logout/', views.user_logout, name='logout'),  # Користувацький маршрут для виходу
    # Інші маршрути...
]

текст перекладу

Оновлення посилання для виходу в base.html

Тепер, коли у вас є налаштована власна логіка виходу, переконайтеся, що ви оновили посилання для виходу в base.html або іншому шаблоні, щоб воно вказувало на новий маршрут для виходу.


{% if user.is_authenticated %}  
 Вихід  
{% else %}  
 Увійти  
{% endif %}

Додавання управління замовленнями та кошиком

Щоб додати функціонал управління замовленнями та кошиком, вам потрібно розробити відповідні моделі (models), види (views), шаблони (templates) та обробку логіки. Далі я покроково покажу, як реалізувати ці функції.

1.

текст перекладу
```
Проєктування моделей (Models)

1.1 Модель кошика (Cart)

Спочатку нам потрібна модель кошика для зберігання вибраних користувачем товарів. Можна створити просту модель Cart, щоб зв'язати товари з кількістю.

# models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    description = models.TextField()
    stock = models.PositiveIntegerField()

    def __str__(self):
        return self.name

class Cart(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField()
    added_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return f'{self.product.name} ({self.quantity}) для {self.user.username}'

Таким чином, кожен елемент кошика буде пов'язаний з користувачем та товаром, а також вказуватиметься кількість товару.

Модель замовлення (Order)

Модель замовлення може містити основну інформацію про замовлення, таку як статус, користувач, замовлені товари тощо.

# models.py
class Order(models.Model):

текст перекладу

user = models.ForeignKey(User, ondelete=models.CASCADE)
products = models.ManyToManyField(Product, through='OrderItem')
status = models.CharField(max
length=50, default='Pending') # Статус замовлення, наприклад, очікує обробки, відправлено тощо
createdat = models.DateTimeField(autonowadd=True)
updated
at = models.DateTimeField(auto_now=True)

def str(self):
return f'Order #{self.id} by {self.user.username}'

class OrderItem(models.Model):
order = models.ForeignKey(Order, ondelete=models.CASCADE)
product = models.ForeignKey(Product, on
delete=models.CASCADE)
quantity = models.PositiveIntegerField()
price = models.DecimalField(maxdigits=10, decimalplaces=2)

def str(self):
return f'{self.quantity} x {self.product.name}'
```

  • Order містить поле ManyToManyField, яке зв'язує товари з замовленням через модель OrderItem.
  • OrderItem зберігає кількість та ціну кожного товару, що дозволяє записати деталі товару в замовленні.

Проєктування видів (Views)

2.1 Вид кошика

Вид кошика відображає вміст кошика користувача і дозволяє йому додавати товари або видаляти їх з кошика.

# views.py
from django.shortcuts import render, redirect

текст перекладу

from .models import Cart, Product
from django.contrib.auth.decorators import login_required

@loginrequired
def view
cart(request):
cartitems = Cart.objects.filter(user=request.user)
total
price = sum(item.product.price * item.quantity for item in cartitems)
return render(request, 'cart/view
cart.html', {'cartitems': cartitems, 'totalprice': totalprice})

@loginrequired
def add
tocart(request, productid):
product = Product.objects.get(id=productid)
cart
item, created = Cart.objects.getorcreate(user=request.user, product=product)
if not created:
cartitem.quantity += 1 # Збільшуємо кількість товару
cart
item.save()
return redirect('view_cart')

@loginrequired
def remove
fromcart(request, productid):
cartitem = Cart.objects.get(user=request.user, productid=productid)
cart
item.delete()
return redirect('view_cart')
```

Ці види мають таку функціональність:

  • view_cart: відображає вміст кошика користувача та обчислює загальну вартість.
  • add_to_cart: додає товар до кошика, якщо товар вже є в кошику, збільшує його кількість.
    текст перекладу
    ``
    -
    removefromcart`: видаляє товар з кошика.

Види замовлень

Після завершення покупок користувач може перетворити товари з кошика в замовлення.

# views.py
from .models import Order, OrderItem

@login_required
def checkout(request):
    cart_items = Cart.objects.filter(user=request.user)
    if not cart_items:
        return redirect('view_cart')  # Якщо кошик порожній, повертаємось до сторінки кошика

    order = Order.objects.create(user=request.user)
    for item in cart_items:
        OrderItem.objects.create(order=order, product=item.product, quantity=item.quantity, price=item.product.price)
        item.delete()  # Очищаємо кошик

    order.status = 'Pending'  # Встановлюємо статус замовлення на "Очікує обробки"
    order.save()

    return render(request, 'order/order_confirmation.html', {'order': order})

Проєктування шаблонів (Templates)

3.1 Шаблон кошика

view_cart.html використовується для відображення вмісту кошика користувача:


{% extends 'base.html' %}  

{% block title %}Кошик{% endblock %}  

{% block content %}  

Кошик
текст перекладу

{% for item in cart_items %}

{{ item.product.name }}
{{ item.quantity }}
{{ item.product.price }}
{{ item.product.price * item.quantity }}
Видалити

{% endfor %}

Загальна сума: {{ totalprice }}
Оформити замовлення {% endblock %} ``## 3.2 Сторінка підтвердження замовленняorder
confirmation.htmlвикористовується для відображення сторінки підтвердження замовлення:
{% extends 'base.html' %}
{% block title %}Підтвердження замовлення{% endblock %}
{% block content %}
Підтвердження замовлення
Ваше замовлення було успішно оформлено, номер замовлення: {{ order.id }}.
{% for item in order.orderitem_set.all %}
{{ item.product.name }} x {{ item.quantity }} - {{ item.price * item.quantity }} грн
{% endfor %}
Статус замовлення: {{ order.status }}
{% endblock %}
## Налаштування маршрутизації URL Додайте відповідні маршрути вurls.pyдля обробки запитів кошика та замовлення.# urls.py
текст перекладу
`
from django.urls import path
from .

текст перекладу
```
import views

urlpatterns = [
path('cart/', views.viewcart, name='viewcart'),
path('cart/add//', views.addtocart, name='addtocart'),
path('cart/remove//', views.removefromcart, name='removefromcart'),
path('checkout/', views.checkout, name='checkout'),
]
```

При оформленні замовлення виникає помилка TemplateSyntaxError at /checkout/ Could not parse the remainder: ‘ * item.quantity’ from ‘item.price * item.quantity’

TemplateSyntaxError: Could not parse the remainder означає, що у шаблоні є синтаксична помилка. Це зазвичай трапляється, коли ви намагаєтесь виконати непідтримуваний вираз.

У шаблонах Django хоча і можна отримати доступ до простих змінних, але не підтримується виконання складних виразів (наприклад, множення) безпосередньо в шаблоні.

Причина проблеми

У шаблоні, що викликає помилку, можливо, є такий вираз:

{{ item.price * item.quantity }}

Рішення

Необхідно заздалегідь обчислити ці дані у функції виду та передати результат у шаблон. Ось як можна вирішити цю проблему:

1.

текст перекладу
```

Зміна виду checkout

У файлі views.py обчислюємо загальну вартість кожного товару та загальну суму замовлення, а потім передаємо ці дані до шаблону:

# Замовлення  
@login_required  
def checkout(request):  
    # Отримуємо товари з кошика  
    cart_items = Cart.objects.filter(user=request.user)  
    if not cart_items:  
        return redirect('view_cart')  # Якщо кошик порожній, перенаправляємо на сторінку кошика  
    total_price = 0  # Ініціалізація загальної вартості  

    # Ініціалізація замовлення  
    order = Order.objects.create(user=request.user)   
    for item in cart_items:  # Перебираємо товари в кошику  
        item.total = item.product.price * item.quantity  # Загальна вартість для кожного товару  
        total_price += item.total  # Додаємо до загальної вартості  
        # Додаємо товар до замовлення  
        OrderItem.objects.create(order=order, product=item.product, quantity=item.quantity, price=item.product.price)  
        item.delete()  # Очищаємо кошик  

    order.status = 'Pending'  # Встановлюємо статус замовлення "Очікується"  
    order.save()  

    return render(request, 'order/order_confirmation.html', {  
        'order': order,  
        'total_price': total_price  
    })

Зміна шаблону order_confirmation.html

У шаблоні використовуємо передані змінні для відображення обчислених даних:


{% extends 'base.html' %}  

{% block title %}Підтвердження замовлення{% endblock %}  

{% block content %}  

Підтвердження замовлення  
Ваше замовлення успішно відправлено, номер замовлення: {{ order.id }}.  
    {% for item in order.orderitem_set.all %}    
{{ item.product.name }} x {{ item.quantity }} - {{ item.price }} грн  
    {% endfor %}    
Загальна вартість: {{ total_price }} грн  
Статус замовлення: {{ order.status }}  
{% endblock %}  

pic
pic

Інтеграція з платіжною системою PayPal

Використання PayPal Checkout SDK

1.

текст перекладу

Реєстрація облікового запису PayPal Developer

  1. Перейдіть на сайт PayPal Developer, зареєструйтесь і увійдіть у свій обліковий запис.
    2.

текст перекладу

Створення додатку Sandbox для отримання Client ID та Secret Key.

Встановлення необхідних пакетів

Встановіть paypalrestsdk у вашому Django проекті:

pip install paypalrestsdk

Налаштування PayPal SDK

Додайте конфігурацію PayPal у вашому файлі settings.py:

# Конфігурація PayPal  
PAYPAL_CLIENT_ID = 'ваш-client-id'  
PAYPAL_CLIENT_SECRET = 'ваш-client-secret'  
PAYPAL_MODE = 'sandbox' # 'live' для реального середовища

Реалізація логіки оплати в views.py

Обробіть замовлення на сторінці оформлення замовлення та перенаправте на PayPal:

@login_required  
def paypal_checkout(request):  
 # Створення об'єкта PayPal для оплати  
 cart_items = Cart.objects.filter(user=request.user)  
 if not cart_items:  
 messages.warning(request, "Ваш кошик порожній, не можна оформити замовлення.")  
 return redirect('view_cart')  

 total_price = sum(item.product.price * item.quantity for item in cart_items)  

 payment = paypalrestsdk.Payment({  
 "intent": "sale",  
 "payer": {"payment_method": "paypal"},  
 "redirect_urls": {  
 "return_url": request.build_absolute_uri('/paypal/execute/'),  
 "cancel_url": request.build_absolute_uri('/paypal/cancel/')  
 },  
 "transactions": [{  
 "item_list": {  
 "items": [

текст перекладу
{  
 "name": item.product.name,  
 "sku": str(item.product.id),  
 "price": str(item.product.price),  
 "currency": "USD",  
 "quantity": item.quantity  
 } for item in cart_items  
 ]  
},  
 "amount": {"total": str(total_price), "currency": "USD"},  
 "description": "Оплата замовлення"  
 }]  
 })  
 if payment.create():  
 # Очищення кошика  
 cart_items.delete() # Очищення всіх товарів у кошику  
 for link in payment.links:  
 if link.rel == "approval_url":  
 # Перенаправлення на сторінку оплати PayPal  
 return redirect(link.href)  
 else:  
 messages.error(request, "Не вдалося створити оплату через PayPal, спробуйте ще раз.")  
 return redirect('view_cart')  

@login_required  
def paypal_execute(request):  
 payment_id = request.GET.get('paymentId')  
 payer_id = request.GET.get('PayerID')  

 payment = paypalrestsdk.Payment.find(payment_id)  
 if payment.execute({"payer_id": payer_id}):  
 messages.success(request, "Оплата успішна!")  
 # Очищення кошика  
 Cart.objects.filter(user=request.user).delete()  
 return redirect('order_success') # Перехід до сторінки успішної оплати  
 else:  
 messages.error(request, "Оплата не вдалася!")
текст перекладу
return redirect('view_cart')

В конфігурації маршруту в urls.py

path('paypal/checkout/', views.paypal_checkout, name='paypal_checkout'),  
path('paypal/execute/', views.paypal_execute, name='paypal_execute'),  
path('paypal/cancel/', views.paypal_cancel, name='paypal_cancel'),

Оновлення кнопки для оформлення замовлення в view_cart.html


{% extends 'base.html' %}  

{% block title %}Кошик{% endblock %}  

{% block content %}  

Кошик
        {% for item in cart_items %}        {% endfor %}    
Назва товару    Кількість    Ціна (грн)    Операції                {{ item.product.name }}    {{ item.quantity }}    {{ item.product.price }}        Видалити            
Загальна сума: {{ total_price }} грн
текст перекладу
結帳  


{% endblock %}

Таким чином, при натисканні на кнопку "Оформити замовлення" в view_cart.html

користувач буде перенаправлений на маршрут paypal_checkout в urls

що відповідає функції paypal_checkout в views.py

і веде на сторінку оплати PayPal

Зміна логіки оплати

Оновіть paypal_checkout та paypal_execute в views.py

# paypal оплата  
@login_required  
def paypal_checkout(request):  
 # Проходимо по всіх товарах в кошику  
 cart_items = Cart.objects.filter(user=request.user)  
 if not cart_items:  
 messages.warning(request, "Ваш кошик порожній, неможливо оформити замовлення.")  
 return redirect('view_cart')  
 # Розрахунок загальної вартості  
 total_price = sum(item.product.price * item.quantity for item in cart_items)  
 # Оголошення платежу  
 payment = paypalrestsdk.Payment({  
 "intent": "sale",  
 "payer": {"payment_method": "paypal"},  
 "redirect_urls": {  
 "return_url": request.build_absolute_uri('/paypal/execute/'),  
 "cancel_url": request.build_absolute_uri('/paypal/cancel/')  
 },  
 "transactions": [{  
 "item_list": {  
 "items": [  
 {  
 "name": item.product.name,  
 "sku": str(item.product.id),
текст перекладу
"price": str(item.product.price),  
 "currency": "USD",  
 "quantity": item.quantity  
 } for item in cart_items  
 ]  
 },  
 "amount": {"total": str(total_price), "currency": "USD"},  
 "description": "Оплата замовлення"  
 }]  
 })  
 # Створення платежу  
 if payment.create():  
 # Шукаємо посилання на сторінку оплати  
 for link in payment.links:  
 # Якщо є посилання для оплати  
 if link.rel == "approval_url":  
 # Перенаправляємо на сторінку оплати PayPal  
 return redirect(link.href)  
 else:  
 messages.error(request, "Не вдалося створити платіж PayPal, спробуйте ще раз.")  
 return redirect('view_cart')  

# Виконання платежу  
@login_required  
def paypal_execute(request):  
 # Отримуємо інформацію про платіж  
 payment_id = request.GET.get('paymentId')  
 payer_id = request.GET.get('PayerID')  
 payment = paypalrestsdk.Payment.find(payment_id)  
 # Якщо платіж виконано успішно  
 if payment.execute({"payer_id": payer_id}):  
 # Отримуємо товар у кошику користувача  
 cart_items = Cart.objects.filter(user=request.user)  
 # Ініціалізація загальної суми  
 total_price = 0   
 # Ініціалізація замовлення користувача  
 order = Order.objects.create(user=request.user)   
 # Проходимо по всіх товарів у кошику  
 for item in cart_items:
текст перекладу
item.total = item.product.price * item.quantity # Загальна вартість кожного товару  
 total_price += item.total # Додаємо до загальної суми  
 # Створення запису в замовленні користувача і очищення кошика  
 OrderItem.objects.create(order=order, product=item.product, quantity=item.quantity, price=item.product.price)  
 item.delete()   
 # Встановлюємо статус замовлення як "OK" і зберігаємо  
 order.status = 'OK'   
 order.save()  
 # Перенаправляємо на сторінку успішної оплати  
 return render(request, 'order/order_confirmation.html', {'order': order,'total_price': total_price})  
 else:  
 messages.error(request, "Не вдалося здійснити платіж!")  
 return redirect('view_cart')

Відповідний шаблон order_confirmation


{% extends 'base.html' %}  

{% block title %}Підтвердження замовлення{% endblock %}  

{% block content %}  

Підтвердження замовлення  
Ваше замовлення успішно надіслано, номер замовлення {{ order.id }}.  
    {% for item in order.orderitem_set.all %}    
{{ item.product.name }} x {{ item.quantity }} - {{ item.price }} грн  
    {% endfor %}    
Загальна сума:{{ total_price }} грн  
Статус замовлення:{{ order.status }}  
   {% endblock %} 

текст перекладу
## Відповідна модель замовлення

Модель замовлення

class Order(models.Model):
user = models.ForeignKey(User, ondelete=models.CASCADE)
products = models.ManyToManyField(Product, through='OrderItem')
status = models.CharField(max
length=50, default='Pending') # Статус замовлення, наприклад: очікує обробки, відправлено тощо
createdat = models.DateTimeField(autonowadd=True)
updated
at = models.DateTimeField(auto_now=True)

def str(self):
return f'Замовлення #{self.id} від {self.user.username}'

class OrderItem(models.Model):
order = models.ForeignKey(Order, ondelete=models.CASCADE)
product = models.ForeignKey(Product, on
delete=models.CASCADE)
quantity = models.PositiveIntegerField()
price = models.DecimalField(maxdigits=10, decimalplaces=2)

def str(self):
return f'{self.quantity} x {self.product.name}'
```

Безкоштовне розгортання в хмарі

Використання requirements.txt:

  • В кореневій директорії вашого проєкту запустіть наступну команду для створення файлу залежностей:
pip freeze > requirements.txt

Переконайтесь, що використовується WSGI сервер (наприклад, Gunicorn):

  • Додайте gunicorn до файлу requirements.txt

текст перекладу

gunicorn==20.1.0

Налаштування settings.py:

  • Дозволені хости: Додайте домен Render до ALLOWED_HOSTS

pic

ALLOWED_HOSTS = ['your-app-name.onrender.com', 'localhost']

Налаштування статичних файлів: Додайте наступне до settings.py:

import os  

STATIC_URL = '/static/'  
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

Налаштування для розгортання на Render

1.
текст перекладу
Увійдіть або зареєструйтесь на Render
Натисніть тут, щоб перейти на Render та увійти/зареєструватися.
2. Створення нового Web Service

  • Натисніть “New +” → “Web Service”.
  • Виберіть “Connect a Git Repository”, підключіть ваш репозиторій GitHub/GitLab.
  • Виберіть репозиторій вашого проекту Django.

1.
текст перекладу
Налаштування сервісу:

  • Назва: Налаштуйте назву вашого сервісу (наприклад, my-django-app).
  • Оточення: Виберіть Python.
  • Команда збірки: Встановіть так:
pip install -r requirements.txt && python manage.py collectstatic --noinput

Команда запуску: Встановіть так:

gunicorn .wsgi:application

Вибір безкоштовного плану
Переконайтесь, що вибрано безкоштовний план для розгортання.

pic

pic

Здається, CSS верстка порушена.

Ось налаштування, які я використовую у файлі settings.py:

STATIC_URL = 'static'   
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

У вас є невеличка проблема з налаштуваннями STATIC_URL та STATIC_ROOT у файлі settings.py. Зокрема, STATIC_URL має починатися з косої риски (/), а STATIC_ROOT має вказувати на абсолютний шлях (для збереження зібраних статичних файлів). Давайте виправимо це:

import os  

# URL статичних файлів для доступу через браузер  
STATIC_URL = '/static/'  

# Шлях зберігання статичних файлів (для виробничого середовища)  
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # Рекомендується зберігати статичні файли в директорії `staticfiles`

Пояснення:

1.
текст перекладу
STATIC_URL

  • Це URL шлях для доступу до статичних файлів через браузер. Він повинен починатися з косої риски /, що вказує на каталог статичних ресурсів сайту. Наприклад, URL для вашого CSS файлу буде виглядати так: http://localhost:8000/static/css/style.css.

1.
текст перекладу
STATIC_ROOT

  • Це фізичний шлях до статичних файлів. Команда collectstatic збирає всі статичні файли в цей каталог. У режимі розробки зазвичай не потрібно налаштовувати STATIC_ROOT, але в виробничому середовищі це обов'язково. Рекомендується налаштувати його на staticfiles, щоб статичні файли зберігались в окремій папці для зручності управління та розгортання.

Інші налаштування

Якщо ви працюєте в режимі розробки Django і хочете безпосередньо завантажувати статичні файли під час розробки, потрібно також переконатися, що налаштування STATICFILES_DIRS вірне:

STATICFILES_DIRS = [  
 os.path.join(BASE_DIR, 'ecommerce/static'),  
]

Збір статичних файлів

Коли ви будете готові до розгортання в виробничому середовищі, потрібно виконати команду collectstatic, щоб зібрати статичні файли в каталог, вказаний в STATIC_ROOT:

python manage.py collectstatic

Переконайтесь, що статичні файли підключені

У шаблонах переконайтеся, що ви правильно використовуєте тег {% static %} для підключення CSS файлів, наприклад:

{% load static %}  


Таким чином, Django автоматично згенерує правильний URL згідно з STATIC_URL.

settings.py

# Статичні файли (CSS, JavaScript, Зображення)  
# https://docs.djangoproject.com/en/5.1/howto/static-files/  
# URL для статичних файлів, для доступу через браузер  
STATIC_URL = '/static/'  

# Шлях для збереження статичних файлів (для виробничого середовища)  
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # Рекомендується зберігати статичні файли в папці `staticfiles`  

STATICFILES_DIRS = [  
 os.path.join(BASE_DIR, 'ecommerce/static'),  
]

urls.py

urlpatterns = [  
 path('admin/', admin.site.urls), # Адміністративна панель Django  
 path('', include('ecommerce.urls')), # Цей рядок включає URL вашого застосунку  
]  
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)  
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Успіх

pic

[

Список товарів

Опис редагування

django-ecommerce-kt79.onrender.com

](https://django-ecommerce-kt79.onrender.com/?source=post_page-----28e6be32024b--------------------------------)

Перекладено з: Django MTV 架構

Leave a Reply

Your email address will not be published. Required fields are marked *