1 Commits

Author SHA1 Message Date
erjemin 9cd147b0d7 mod: оптимизированные svg, favicon-ы и другая мелочь.
Build and Push Docker Image / build-and-push (push) Successful in 1m0s
2026-03-22 16:54:36 +03:00
3 changed files with 17 additions and 39 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ upstream dq-django {
# 2. Конфигурируем сервер # 2. Конфигурируем сервер
server { server {
server_name dq.cube2.ru; # Основное доменное имя server_name dq.cube2.ru dq2.cube2.ru; # Основное доменное имя
# Слушаем 80 порт (Certbot потом добавит сюда редирект на 443 и настройки SSL) # Слушаем 80 порт (Certbot потом добавит сюда редирект на 443 и настройки SSL)
listen 80; listen 80;
+11 -34
View File
@@ -12,7 +12,6 @@ from django.core.exceptions import ObjectDoesNotExist
from django.views.generic import DetailView, TemplateView from django.views.generic import DetailView, TemplateView
import time import time
import pytils import pytils
import random
from web.models import TbDictumAndQuotes, TbImages, TbAuthor from web.models import TbDictumAndQuotes, TbImages, TbAuthor
@@ -99,11 +98,9 @@ class CommonContextMixin:
# --- 4. ВЫБОР КАРТИНКИ --- # --- 4. ВЫБОР КАРТИНКИ ---
if dq.kImages_id is None: if dq.kImages_id is None:
if len(tags) != 0: if len(tags) != 0:
# Используем Python random вместо order_by('?') для производительности tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first()
tagged_images = list(TbImages.objects.filter(tags__name__in=tags).values_list('id', 'imFile')) if tagged_image:
if tagged_images: context.update({'IMAGE': tagged_image.imFile})
tagged_image = tagged_images[random.randint(0, len(tagged_images) - 1)]
context.update({'IMAGE': tagged_image[1]})
else: else:
context.update({'IMAGE': dq.kImages.imFile}) context.update({'IMAGE': dq.kImages.imFile})
@@ -113,13 +110,7 @@ class CommonContextMixin:
# --- 6. ВЫБОР СЛЕДУЮЩЕЙ ЦИТАТЫ --- # --- 6. ВЫБОР СЛЕДУЮЩЕЙ ЦИТАТЫ ---
# Сначала пробуем найти следующую цитату, которую мы еще не видели # Сначала пробуем найти следующую цитату, которую мы еще не видели
# Используем Python random вместо order_by('?') для производительности dq_next = queryset.exclude(id__in=seen_ids).order_by('?').first()
unseen_ids = queryset.exclude(id__in=seen_ids).values_list('id', flat=True)
if unseen_ids:
dq_next_id = list(unseen_ids)[random.randint(0, len(unseen_ids) - 1)]
dq_next = queryset.filter(id=dq_next_id).first()
else:
dq_next = None
# Если таких нет (мы посмотрели все цитаты в этом контексте/теге) # Если таких нет (мы посмотрели все цитаты в этом контексте/теге)
if dq_next is None: if dq_next is None:
@@ -132,12 +123,7 @@ class CommonContextMixin:
# Вариант: Очистить seen_ids, чтобы в следующий раз (на некст странице) список был пуст? # Вариант: Очистить seen_ids, чтобы в следующий раз (на некст странице) список был пуст?
# Или просто выбрать любую КРОМЕ текущей? # Или просто выбрать любую КРОМЕ текущей?
other_ids = list(queryset.exclude(id=dq.id).values_list('id', flat=True)) dq_next = queryset.exclude(id=dq.id).order_by('?').first()
if other_ids:
dq_next_id = other_ids[random.randint(0, len(other_ids) - 1)]
dq_next = queryset.filter(id=dq_next_id).first()
else:
dq_next = None
# Если мы действительно прошли весь цикл по тегу, логично сбросить seen_ids, # Если мы действительно прошли весь цикл по тегу, логично сбросить seen_ids,
# чтобы пользователь мог заново проходить этот список случайно, а не "застревать" на последних. # чтобы пользователь мог заново проходить этот список случайно, а не "застревать" на последних.
@@ -214,19 +200,13 @@ class IndexView(CommonContextMixin, TemplateView):
if active_qs is not None: if active_qs is not None:
# Если мы в режиме фильтрации, тоже стараемся не показывать то, что уже видели # Если мы в режиме фильтрации, тоже стараемся не показывать то, что уже видели
unseen_ids = list(active_qs.exclude(id__in=seen_ids).values_list('id', flat=True)) dq = active_qs.exclude(id__in=seen_ids).order_by('?').first()
if unseen_ids:
dq = active_qs.filter(id=unseen_ids[random.randint(0, len(unseen_ids) - 1)]).first()
else:
dq = None
# Если после фильтрации ничего не осталось (мы просмотрели все цитаты тега) # Если после фильтрации ничего не осталось (мы просмотрели все цитаты тега)
if dq is None: if dq is None:
# Сбрасываем историю и берем любую # Сбрасываем историю и берем любую
self.request.session['seen_ids'] = [] self.request.session['seen_ids'] = []
all_ids = list(active_qs.values_list('id', flat=True)) dq = active_qs.order_by('?').first()
if all_ids:
dq = active_qs.filter(id=all_ids[random.randint(0, len(all_ids) - 1)]).first()
if dq is None: if dq is None:
# Если тег не задан, или по тегу ничего не нашлось совсем # Если тег не задан, или по тегу ничего не нашлось совсем
@@ -234,14 +214,11 @@ class IndexView(CommonContextMixin, TemplateView):
active_qs = TbDictumAndQuotes.objects.all() active_qs = TbDictumAndQuotes.objects.all()
# Случайная цитата (с учетом истории, чтобы главная страница тоже не зацикливалась) # Случайная цитата (с учетом истории, чтобы главная страница тоже не зацикливалась)
unseen_ids = list(active_qs.exclude(id__in=seen_ids).values_list('id', flat=True)) dq = active_qs.exclude(id__in=seen_ids).order_by('?').first()
if unseen_ids:
dq = active_qs.filter(id=unseen_ids[random.randint(0, len(unseen_ids) - 1)]).first() if dq is None:
else:
self.request.session['seen_ids'] = [] self.request.session['seen_ids'] = []
all_ids = list(active_qs.values_list('id', flat=True)) dq = active_qs.order_by('?').first()
if all_ids:
dq = active_qs.filter(id=all_ids[random.randint(0, len(all_ids) - 1)]).first()
if dq: if dq:
# Используем миксин, ОБЯЗАТЕЛЬНО передаем active_qs # Используем миксин, ОБЯЗАТЕЛЬНО передаем active_qs
+5 -4
View File
@@ -1,3 +1,4 @@
% cat docker-compose.yaml
# ============================================================================== # ==============================================================================
# Docker Compose для PRODUCTION # Docker Compose для PRODUCTION
# Этот файл запускается на боевом сервере. # Этот файл запускается на боевом сервере.
@@ -48,7 +49,7 @@ services:
mkdir -p /app/public/media/errors && mkdir -p /app/public/media/errors &&
cp /home/app/web/dicquo/templates/static_404.html /app/public/media/errors/404.html && cp /home/app/web/dicquo/templates/static_404.html /app/public/media/errors/404.html &&
cp /home/app/web/dicquo/templates/static_500.html /app/public/media/errors/500.html && cp /home/app/web/dicquo/templates/static_500.html /app/public/media/errors/500.html &&
gunicorn --workers 2 --timeout 15 --bind 0.0.0.0:8000 dicquo.wsgi:application" gunicorn --workers 3 --bind 0.0.0.0:8000 dicquo.wsgi:application"
# 4. Проброс портов (Внешний Nginx -> localhost:8010) # 4. Проброс портов (Внешний Nginx -> localhost:8010)
ports: ports:
@@ -91,9 +92,9 @@ services:
healthcheck: healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()"] test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()"]
interval: 30s # Проверка каждые 30 секунд interval: 30s # Проверка каждые 30 секунд
timeout: 5s # Таймаут ответа - 5 секунд timeout: 3s # Таймаут ответа - 3 секунды
start_period: 60s # Даем 60 секунд на стартап (миграции + collectstatic + gunicorn запуск) start_period: 10s # Даем 10 секунд на стартап перед первой проверкой
retries: 5 # Unhealthy после 5 неудачных попыток retries: 3 # Unhealthy после 3 неудачных попыток
# 9. Логирование (Ротация) # 9. Логирование (Ротация)
logging: logging: