fix: Исправлено образование петель.

mod: современный стиль для вьюх.
This commit is contained in:
2026-02-18 17:38:35 +03:00
parent 65feb36f77
commit 7e33997260
3 changed files with 192 additions and 75 deletions

View File

@@ -29,9 +29,10 @@ sitemaps = {
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^$', views.index),
re_path(r'^(?P<dq_id>\d{1,12})_\S*$', views.by_id),
re_path(r'^$', views.IndexView.as_view()),
re_path(r'^(?P<dq_id>\d{1,12})_\S*$', views.DictumDetailView.as_view()),
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -3,92 +3,189 @@ __author__ = "Sergei Erjemin"
__copyright__ = "Copyright 2020-2026, Sergei Erjemin"
__credits__ = ["Sergei Erjemin", ]
__license__ = "GPL"
__version__ = "0.3.9"
__version__ = "0.3.0"
__maintainer__ = "Sergei Erjemin"
__email__ = "erjemin@gmail.com"
__status__ = "in progress"
from django.shortcuts import render
from django.core.exceptions import ObjectDoesNotExist
from django.views.generic import DetailView, TemplateView
import time
import hashlib
import random
import pytils
from taggit.models import Tag
from web.models import TbOrigin, TbDictumAndQuotes, TbImages, TbAuthor
from web.models import TbDictumAndQuotes, TbImages, TbAuthor
# Create your views here.
def for_dq(dq):
to_template = {}
class CommonContextMixin:
"""
Общий миксин для представлений:
- Логика "одной цитаты" (получение контекста цитаты)
- Общий контекст (куки, тайминги)
"""
def dispatch(self, request, *args, **kwargs):
# Засекаем время в самом начале обработки запроса
self.t_start = time.process_time()
return super().dispatch(request, *args, **kwargs)
def get_filtered_queryset(self):
"""
Возвращает (queryset, tag_slug) на основе GET-параметров запроса.
Если тега нет или он не найден, возвращает (None, None).
"""
tag_slug = self.request.GET.get('tag')
if not tag_slug:
return None, None
dq_qs = TbDictumAndQuotes.objects.all()
# 1. Пробуем найти цитаты, где АВТОР имеет этот тег
author_tag_qs = dq_qs.filter(kAuthor__tags__slug__in=[tag_slug])
if author_tag_qs.exists():
return author_tag_qs, tag_slug
# 2. Если авторов нет, ищем цитаты с этим тегом
quote_tag_qs = dq_qs.filter(tags__slug__in=[tag_slug])
if quote_tag_qs.exists():
return quote_tag_qs, tag_slug
return None, None
def get_dictum_context(self, request, dq, queryset=None):
"""
Получение контекста для цитаты dq. Если queryset передан, используется для логики "следующей цитаты"
и фильтрации по тегу.
"""
context = {}
# Если queryset не передан, используем все объекты
if queryset is None:
queryset = TbDictumAndQuotes.objects.all()
# --- 1. ЛОГИКА ИСТОРИИ СЕССИИ (Предотвращение петель) ---
seen_ids = request.session.get('seen_ids', [])
if dq.id not in seen_ids:
seen_ids.append(dq.id)
if len(seen_ids) > 100:
seen_ids.pop(0)
request.session['seen_ids'] = seen_ids
# --- 2. ГЕНЕРАЦИЯ ЦВЕТОВ ---
num = int(hashlib.blake2s(dq.szContent.encode("utf-8"), digest_size=1).hexdigest(), 16)
clr = sorted([num / 2, num / 3, num / 5, num / 7, num / 11, num / 1.5], key=lambda A: random.random())
to_template.update({'CLR': clr})
to_template.update({'DQ': dq})
context.update({'CLR': clr})
context.update({'DQ': dq})
# --- 3. АВТОР И ТЕГИ ---
try:
au = TbAuthor.objects.get(id=dq.kAuthor_id)
to_template.update({'AUTHOR': au})
context.update({'AUTHOR': au})
tags = au.tags.names()
except ObjectDoesNotExist:
tags = dq.tags.names()
tag_and_slug = []
for i in tags:
tag_and_slug.append({"name": i, "slug": pytils.translit.slugify(i.lower())[:120]})
to_template.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])}) # tag_and_slug
context.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])})
# --- 4. ВЫБОР КАРТИНКИ ---
if dq.kImages_id is None:
if len(tags) != 0:
try:
# tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first()
tagged_image = TbImages.objects.filter(tags__name__in=tags)
random.shuffle(list(tagged_image))
to_template.update({'IMAGE': tagged_image[0].imFile})
except IndexError:
pass
tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first()
if tagged_image:
context.update({'IMAGE': tagged_image.imFile})
else:
to_template.update({'IMAGE': dq.kImages.imFile})
context.update({'IMAGE': dq.kImages.imFile})
# --- 5. СЧЕТЧИК ---
dq.iViewCounter += 1
dq.save()
# dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id).order_by('?').first()
dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id)
random.shuffle(list(dq_next))
to_template.update({"NEXT": dq_next[0].id})
to_template.update({"NEXT_TXT": pytils.translit.slugify(dq_next[0].szContent.lower()[:120])})
return to_template
dq.save(update_fields=['iViewCounter'])
# --- 6. ВЫБОР СЛЕДУЮЩЕЙ ЦИТАТЫ ---
dq_next = queryset.exclude(id__in=seen_ids).order_by('?').first()
def by_id(request, dq_id):
t_start = time.process_time()
template = "index.html" # шаблон
dq = TbDictumAndQuotes.objects.get(id=dq_id)
to_template = for_dq(dq)
# пероверка, что посетитель согласился со сбором даных через cookies
if request.COOKIES.get('cookie_accept'):
to_template.update({'cookie_accept': 1})
to_template.update({'ticks': float(time.process_time() - t_start)})
response = render(request, template, to_template)
return response
if dq_next is None:
request.session['seen_ids'] = []
dq_next = queryset.exclude(id=dq.id).order_by('?').first()
if dq_next:
context.update({"NEXT": dq_next.id})
context.update({"NEXT_TXT": pytils.translit.slugify(dq_next.szContent.lower()[:120])})
def index(request):
t_start = time.process_time()
# проверка на аутентификацию
# if not request.user.is_authenticated():
# return HttpResponseRedirect("/access")
template = "index.html" # шаблон
dq_ = TbDictumAndQuotes.objects
# Если мы в режиме фильтрации (tag), передаем текущий тег в контекст
if request.GET.get('tag'):
dq = dq_.filter(kAuthor__tags__slug__in=[request.GET['tag']]).order_by('?').first()
if dq is None:
dq = dq_.filter(tags__slug__in=[request.GET['tag']]).order_by('?').first()
if dq is None:
dq = dq_.order_by('?').first()
else:
dq = dq_.first()
to_template = for_dq(dq)
# пероверка, что посетитель согласился со сбором даных через cookies
if request.COOKIES.get('cookie_accept'):
to_template.update({'cookie_accept': 1})
to_template.update({'ticks': float(time.process_time() - t_start)})
response = render(request, template, to_template)
return response
context.update({"CURRENT_TAG": request.GET.get('tag')})
return context
def finalize_context(self, context):
"""
Добавляет общие данные: проверки куки и время выполнения.
"""
if self.request.COOKIES.get('cookie_accept'):
context['cookie_accept'] = 1
# Считаем время от self.t_start, заданного в dispatch
total_time = 0.0
if hasattr(self, 't_start'):
total_time = float(time.process_time() - self.t_start)
context['ticks'] = total_time
return context
class DictumDetailView(CommonContextMixin, DetailView):
model = TbDictumAndQuotes
template_name = "index.html"
pk_url_kwarg = 'dq_id'
context_object_name = 'DQ'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Определяем контекст фильтрации (если есть тег в URL)
active_qs, _ = self.get_filtered_queryset()
# Используем миксин логики цитаты с учетом фильтра
extras = self.get_dictum_context(self.request, self.object, queryset=active_qs)
context.update(extras)
# Финализируем контекст (куки, тайминги)
return self.finalize_context(context)
class IndexView(CommonContextMixin, TemplateView):
template_name = "index.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
active_qs, _ = self.get_filtered_queryset()
dq = None
if active_qs is not None:
dq = active_qs.order_by('?').first()
if dq is None:
# Если тег не задан, или по тегу ничего не нашлось совсем
# Сбрасываем active_qs на "все", так как специфический контекст пуст
active_qs = TbDictumAndQuotes.objects.all()
# Случайная цитата (с учетом истории, чтобы главная страница тоже не зацикливалась)
seen_ids = self.request.session.get('seen_ids', [])
dq = active_qs.exclude(id__in=seen_ids).order_by('?').first()
if dq is None:
self.request.session['seen_ids'] = []
dq = active_qs.order_by('?').first()
if dq:
# Используем миксин, ОБЯЗАТЕЛЬНО передаем active_qs
extras = self.get_dictum_context(self.request, dq, queryset=active_qs)
context.update(extras)
# Финализируем контекст (куки, тайминги)
return self.finalize_context(context)

View File

@@ -1,7 +1,26 @@
# DicQuo
User-Agent: *
Allow: /
Disallow:
Disallow: /admin/
Disallow: /*?tag=
Disallow: /*?
# Optimize for Yandex
Clean-param: tag /
# AI and LLM bots settings
# OpenAI GPT
# User-agent: GPTBot
# Disallow:
# Common Crawl (used by many AI models)
# User-agent: CCBot
# Disallow:
# Google Bard/Gemini
# User-agent: Google-Extended
# Disallow:
Host: dq.cube2.ru
Sitemap: https://dq.cube2.ru/sitemap.xml