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 = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
re_path(r'^$', views.index), re_path(r'^$', views.IndexView.as_view()),
re_path(r'^(?P<dq_id>\d{1,12})_\S*$', views.by_id), 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'), path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
] ]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 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" __copyright__ = "Copyright 2020-2026, Sergei Erjemin"
__credits__ = ["Sergei Erjemin", ] __credits__ = ["Sergei Erjemin", ]
__license__ = "GPL" __license__ = "GPL"
__version__ = "0.3.9" __version__ = "0.3.0"
__maintainer__ = "Sergei Erjemin" __maintainer__ = "Sergei Erjemin"
__email__ = "erjemin@gmail.com" __email__ = "erjemin@gmail.com"
__status__ = "in progress" __status__ = "in progress"
from django.shortcuts import render
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.views.generic import DetailView, TemplateView
import time import time
import hashlib import hashlib
import random import random
import pytils import pytils
from taggit.models import Tag from web.models import TbDictumAndQuotes, TbImages, TbAuthor
from web.models import TbOrigin, TbDictumAndQuotes, TbImages, TbAuthor
# Create your views here. # Create your views here.
def for_dq(dq):
to_template = {} class CommonContextMixin:
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}) - Общий контекст (куки, тайминги)
try: """
au = TbAuthor.objects.get(id=dq.kAuthor_id) def dispatch(self, request, *args, **kwargs):
to_template.update({'AUTHOR': au}) # Засекаем время в самом начале обработки запроса
tags = au.tags.names() self.t_start = time.process_time()
except ObjectDoesNotExist: return super().dispatch(request, *args, **kwargs)
tags = dq.tags.names()
tag_and_slug = [] def get_filtered_queryset(self):
for i in tags: """
tag_and_slug.append({"name": i, "slug": pytils.translit.slugify(i.lower())[:120]}) Возвращает (queryset, tag_slug) на основе GET-параметров запроса.
to_template.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])}) # tag_and_slug Если тега нет или он не найден, возвращает (None, None).
if dq.kImages_id is None: """
if len(tags) != 0: tag_slug = self.request.GET.get('tag')
try: if not tag_slug:
# tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first() return None, None
tagged_image = TbImages.objects.filter(tags__name__in=tags)
random.shuffle(list(tagged_image)) dq_qs = TbDictumAndQuotes.objects.all()
to_template.update({'IMAGE': tagged_image[0].imFile})
except IndexError: # 1. Пробуем найти цитаты, где АВТОР имеет этот тег
pass author_tag_qs = dq_qs.filter(kAuthor__tags__slug__in=[tag_slug])
else: if author_tag_qs.exists():
to_template.update({'IMAGE': dq.kImages.imFile}) return author_tag_qs, tag_slug
dq.iViewCounter += 1
dq.save() # 2. Если авторов нет, ищем цитаты с этим тегом
# dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id).order_by('?').first() quote_tag_qs = dq_qs.filter(tags__slug__in=[tag_slug])
dq_next = TbDictumAndQuotes.objects.exclude(id=dq.id) if quote_tag_qs.exists():
random.shuffle(list(dq_next)) return quote_tag_qs, tag_slug
to_template.update({"NEXT": dq_next[0].id})
to_template.update({"NEXT_TXT": pytils.translit.slugify(dq_next[0].szContent.lower()[:120])}) return None, None
return to_template
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())
context.update({'CLR': clr})
context.update({'DQ': dq})
# --- 3. АВТОР И ТЕГИ ---
try:
au = TbAuthor.objects.get(id=dq.kAuthor_id)
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]})
context.update({'TAGS': sorted(tag_and_slug, key=lambda x: x["name"])})
# --- 4. ВЫБОР КАРТИНКИ ---
if dq.kImages_id is None:
if len(tags) != 0:
tagged_image = TbImages.objects.filter(tags__name__in=tags).order_by('?').first()
if tagged_image:
context.update({'IMAGE': tagged_image.imFile})
else:
context.update({'IMAGE': dq.kImages.imFile})
# --- 5. СЧЕТЧИК ---
dq.iViewCounter += 1
dq.save(update_fields=['iViewCounter'])
# --- 6. ВЫБОР СЛЕДУЮЩЕЙ ЦИТАТЫ ---
dq_next = queryset.exclude(id__in=seen_ids).order_by('?').first()
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])})
# Если мы в режиме фильтрации (tag), передаем текущий тег в контекст
if request.GET.get('tag'):
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
def by_id(request, dq_id): class DictumDetailView(CommonContextMixin, DetailView):
t_start = time.process_time() model = TbDictumAndQuotes
template = "index.html" # шаблон template_name = "index.html"
dq = TbDictumAndQuotes.objects.get(id=dq_id) pk_url_kwarg = 'dq_id'
to_template = for_dq(dq) context_object_name = 'DQ'
# пероверка, что посетитель согласился со сбором даных через cookies
if request.COOKIES.get('cookie_accept'): def get_context_data(self, **kwargs):
to_template.update({'cookie_accept': 1}) context = super().get_context_data(**kwargs)
to_template.update({'ticks': float(time.process_time() - t_start)})
response = render(request, template, to_template) # Определяем контекст фильтрации (если есть тег в URL)
return response 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)
def index(request): class IndexView(CommonContextMixin, TemplateView):
t_start = time.process_time() template_name = "index.html"
# проверка на аутентификацию
# if not request.user.is_authenticated(): def get_context_data(self, **kwargs):
# return HttpResponseRedirect("/access") context = super().get_context_data(**kwargs)
template = "index.html" # шаблон
dq_ = TbDictumAndQuotes.objects active_qs, _ = self.get_filtered_queryset()
if request.GET.get('tag'):
dq = dq_.filter(kAuthor__tags__slug__in=[request.GET['tag']]).order_by('?').first() dq = None
if active_qs is not None:
dq = active_qs.order_by('?').first()
if dq is None: if dq is None:
dq = dq_.filter(tags__slug__in=[request.GET['tag']]).order_by('?').first() # Если тег не задан, или по тегу ничего не нашлось совсем
if dq is None: # Сбрасываем active_qs на "все", так как специфический контекст пуст
dq = dq_.order_by('?').first() active_qs = TbDictumAndQuotes.objects.all()
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
# Случайная цитата (с учетом истории, чтобы главная страница тоже не зацикливалась)
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 # DicQuo
User-Agent: * User-Agent: *
Allow: / 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 Host: dq.cube2.ru
Sitemap: https://dq.cube2.ru/sitemap.xml Sitemap: https://dq.cube2.ru/sitemap.xml