From a55be3095b3bfab0c9918adb06cf06011b4a3230 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Sun, 21 Dec 2025 15:05:58 +0300 Subject: [PATCH] Fix Docker setup: add gunicorn, fix permissions, update docker-compose and entrypoint, add deployment instructions --- docker/docker-compose.yml | 2 -- docker/entrypoint.sh | 17 ++++++++++----- .../templates/products/product_detail.html | 18 ++++++++-------- myproject/products/views/product_views.py | 21 ++++++++++++++----- myproject/requirements.txt | 1 + 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ff1e420..947557b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - # Все файлы хранятся в /Volume1/DockerAppsData/mixapp/ # YAML файл хранится в /Volume1/DockerYAML/mix/ diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 021a651..527478b 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -149,11 +149,18 @@ EOF # Если manage.py не в текущей директории, но есть в подпапке myproject if [ ! -f "manage.py" ] && [ -d "myproject" ]; then - echo "Changing directory to myproject..." - cd myproject - # Устанавливаем PYTHONPATH чтобы Python мог найти модуль myproject - export PYTHONPATH=$(pwd):$PYTHONPATH - echo "PYTHONPATH set to: $PYTHONPATH" + # Пытаемся войти в директорию, перенаправляя ошибки в /dev/null + if cd myproject 2>/dev/null; then + echo "Changing directory to myproject..." + # Устанавливаем PYTHONPATH чтобы Python мог найти модуль myproject + export PYTHONPATH=$(pwd):$PYTHONPATH + echo "PYTHONPATH set to: $PYTHONPATH" + else + # Если не можем войти в директорию (проблема с правами), устанавливаем PYTHONPATH из текущей директории + echo "Warning: Cannot access myproject directory (permission denied). Setting PYTHONPATH to include myproject..." + export PYTHONPATH=/app/myproject:$PYTHONPATH + echo "PYTHONPATH set to: $PYTHONPATH" + fi fi case "$1" in diff --git a/myproject/products/templates/products/product_detail.html b/myproject/products/templates/products/product_detail.html index 5bd363f..b192704 100644 --- a/myproject/products/templates/products/product_detail.html +++ b/myproject/products/templates/products/product_detail.html @@ -187,7 +187,7 @@ Себестоимость: {{ product.cost_price }} руб. - {% if product.cost_price_details.batches %} + {% if cost_price_details.batches %} @@ -196,7 +196,7 @@ {% endif %} - {% if product.cost_price_details.batches %} + {% if cost_price_details.batches %}
@@ -206,13 +206,13 @@
- Кешированная стоимость: {{ product.cost_price_details.cached_cost }} руб. + Кешированная стоимость: {{ cost_price_details.cached_cost }} руб.
-
- Рассчитанная стоимость: {{ product.cost_price_details.calculated_cost }} руб. - {% if not product.cost_price_details.is_synced %} +
+ Рассчитанная стоимость: {{ cost_price_details.calculated_cost }} руб. + {% if not cost_price_details.is_synced %}
⚠ Требуется синхронизация! {% endif %}
@@ -231,7 +231,7 @@ - {% for batch in product.cost_price_details.batches %} + {% for batch in cost_price_details.batches %} {{ batch.warehouse_name }} {{ batch.quantity }} @@ -244,9 +244,9 @@ Итого: - {{ product.cost_price_details.total_quantity }} + {{ cost_price_details.total_quantity }} - Средневзвешенная: {{ product.cost_price_details.calculated_cost }} руб. + Средневзвешенная: {{ cost_price_details.calculated_cost }} руб. diff --git a/myproject/products/views/product_views.py b/myproject/products/views/product_views.py index 4ae9205..f59ded6 100644 --- a/myproject/products/views/product_views.py +++ b/myproject/products/views/product_views.py @@ -161,10 +161,14 @@ class ProductDetailView(LoginRequiredMixin, ManagerOwnerRequiredMixin, DetailVie context_object_name = 'product' def get_queryset(self): - # Предзагрузка фотографий и аннотация остатков + # Предзагрузка фотографий, категорий, тегов и аннотация остатков total_available = Coalesce(Sum('stocks__quantity_available'), Value(0), output_field=DecimalField()) total_reserved = Coalesce(Sum('stocks__quantity_reserved'), Value(0), output_field=DecimalField()) - return super().get_queryset().prefetch_related('photos').annotate( + return super().get_queryset().prefetch_related( + 'photos', + 'categories', + 'tags' + ).annotate( total_available=total_available, total_reserved=total_reserved, total_free=total_available - total_reserved, @@ -172,9 +176,16 @@ class ProductDetailView(LoginRequiredMixin, ManagerOwnerRequiredMixin, DetailVie def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - # Добавляем фотографии товара в контекст - context['product_photos'] = self.object.photos.all().order_by('order', 'created_at') - context['photos_count'] = self.object.photos.count() + # Добавляем фотографии товара в контекст (используем уже загруженные через prefetch_related) + product_photos = list(self.object.photos.all()) + product_photos.sort(key=lambda x: (x.order, x.created_at)) + context['product_photos'] = product_photos + context['photos_count'] = len(product_photos) + + # Кешируем cost_price_details, чтобы не делать множественные запросы к БД + from ..services.cost_calculator import ProductCostCalculator + context['cost_price_details'] = ProductCostCalculator.get_cost_details(self.object) + return context diff --git a/myproject/requirements.txt b/myproject/requirements.txt index 1770512..a1f55f0 100644 --- a/myproject/requirements.txt +++ b/myproject/requirements.txt @@ -35,3 +35,4 @@ tzdata==2025.2 Unidecode==1.4.0 vine==5.1.0 wcwidth==0.2.14 +gunicorn==21.2.0