mod: рефакторинг "каталога компаний" (вьюшки и шаблоны)

This commit is contained in:
2026-04-25 08:06:15 +03:00
parent 509bce5111
commit 74220ab3a3
4 changed files with 612 additions and 201 deletions

View File

@@ -1,11 +1,27 @@
# -*- coding: utf-8 -*-
"""
Каталог производителей и компаний.
Модуль предоставляет views для отображения:
1. Списка всех производителей с их ключевыми показателями (рейтинг, количество
предложений, среднюю цену и т.п.)
2. Детальную информацию о конкретном производителе со всеми его оконными наборами
Все запросы переведены на Django ORM для лучшей производительности и чистоты кода.
"""
from django.shortcuts import render, redirect
from django.http import HttpRequest, HttpResponse
from django.utils import timezone
from django.http import HttpRequest, HttpResponse, Http404
from django.db.models import Count, Avg, Max, Min, DecimalField
from oknardia.models import (
MerchantBrand,
SetKit,
PriceOffer,
)
from web.report1 import (
get_last_all_user_visit_list,
get_last_user_visit_cookies,
get_last_user_visit_list
)
from web.report1 import get_last_all_user_visit_list, get_last_user_visit_cookies, get_last_user_visit_list
from web.add_func import get_rating_set_for_stars
import django.utils.dateformat
import time
@@ -14,148 +30,500 @@ import re
import pytils
def _get_company_statistics() -> list:
"""
Получает список компаний (MerchantBrand) с агрегированной статистикой.
Статистика включает:
- Количество оконных наборов от компании
- Средний рейтинг наборов
- Количество ценовых предложений
- Среднюю цену предложений
- Дату последнего обновления цены
Оптимизировано для минимизации запросов к БД.
Returns:
list: Список словарей с данными компаний
"""
# 1. Статистика по наборам (SetKit) для каждой компании
set_stats = (
SetKit.objects
.filter(kSet2User__kMerchantOffice__kMerchantName__isnull=False)
.values('kSet2User__kMerchantOffice__kMerchantName_id')
.annotate(
num_sets=Count('id', distinct=True),
avg_rating=Avg('fSetRating')
)
)
set_stats_dict = {
stat['kSet2User__kMerchantOffice__kMerchantName_id']: {
'num_sets': stat['num_sets'],
'avg_rating': stat['avg_rating'] or 0
}
for stat in set_stats
}
# 2. Статистика по ценовым предложениям (PriceOffer)
companies_data = (
PriceOffer.objects
.filter(
sOfferActive=True,
kOfferFromUser__kMerchantOffice__kMerchantName__isnull=False
)
.values('kOfferFromUser__kMerchantOffice__kMerchantName_id')
.annotate(
num_offers=Count('id', distinct=True),
price_avg=Avg('fOfferPrice', output_field=DecimalField()),
last_update=Max('dOfferModify')
)
.order_by('-last_update')
)
# 3. Получаем все объекты MerchantBrand одним запросом (решение проблемы N+1)
company_ids = [
offer['kOfferFromUser__kMerchantOffice__kMerchantName_id']
for offer in companies_data
]
merchants = MerchantBrand.objects.in_bulk(company_ids)
# 4. Собираем финальный результат
result = []
for offer in companies_data:
company_id = offer['kOfferFromUser__kMerchantOffice__kMerchantName_id']
merchant = merchants.get(company_id)
if not merchant:
continue
set_stat = set_stats_dict.get(company_id, {
'num_sets': 0,
'avg_rating': 0
})
result.append({
'id': merchant.id,
'sMerchantName': merchant.sMerchantName,
'pMerchantLogo': merchant.pMerchantLogo,
'NumSets': set_stat['num_sets'],
'RatingAVG': set_stat['avg_rating'],
'NumOffers': offer['num_offers'],
'PriceAVG': offer['price_avg'],
'lastUpdate': offer['last_update']
})
# Сортируем по среднему рейтингу (убывание)
result.sort(key=lambda x: x['RatingAVG'], reverse=True)
return result
def _format_company_for_template(company_data: dict) -> dict:
"""
Форматирует данные компании для вывода в шаблон.
Применяет:
- Конвертацию времени в читаемый формат (e.g., "3 дня назад")
- Склонение существительных (plural forms)
- Вычисление звёзд рейтинга
- Скатывание имени в slug для URL
Args:
company_data (dict): Словарь с данными компании
Returns:
dict: Отформатированные данные компании
"""
formatted = company_data.copy()
# Вычисляем звёзды на основе рейтинга
formatted['STARS'] = get_rating_set_for_stars(
formatted['RatingAVG']
)
# Применяем правильные формы множественного числа
formatted['NumSets'] = pytils.numeral.get_plural(
formatted['NumSets'],
"оконный набор, оконных набора, оконных наборов"
)
formatted['NumOffers'] = pytils.numeral.get_plural(
formatted['NumOffers'],
"вариант, варианта, вариантов"
)
# Конвертируем время последнего обновления в читаемый формат
if formatted['lastUpdate']:
timestamp = int(
django.utils.dateformat.format(
formatted['lastUpdate'],
'U'
)
)
formatted['lastUpdate'] = pytils.dt.distance_of_time_in_words(
timestamp
)
# Генерируем slug из имени компании для URL
formatted['sMerchantMainURL'] = pytils.translit.slugify(
formatted['sMerchantName']
)
return formatted
def catalog_company(request: HttpRequest) -> HttpResponse:
time_start = time.perf_counter()
to_template: dict[str, object] = {} # словарь, для передачи шаблону
q_company = MerchantBrand.objects.raw('SELECT'
' oknardia_merchantbrand.id,'
' oknardia_merchantbrand.sMerchantName,'
' oknardia_merchantbrand.pMerchantLogo,'
' oknardia_merchantbrand.sMerchantMainURL,'
' COUNT(oknardia_priceoffer.id) AS NumOffers,'
' AVG(oknardia_priceoffer.fOfferPrice) AS PriceAVG,'
' MAX(oknardia_priceoffer.dOfferModify) AS lastUpdate,'
' Q.NumSets,'
' Q.RatingAVG,'
' 1 AS STARS '
'FROM (SELECT'
' COUNT(oknardia_setkit.sSetName) AS NumSets,'
' oknardia_merchantoffice.kMerchantName_id AS Q_ID,'
' AVG(oknardia_setkit.fSetRating) AS RatingAVG'
' FROM oknardia_merchantoffice'
' INNER JOIN oknardia_ouruser'
' ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id'
' INNER JOIN oknardia_setkit'
' ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id'
' GROUP BY oknardia_merchantoffice.id,'
' oknardia_merchantoffice.kMerchantName_id) AS Q,'
' oknardia_ouruser'
' INNER JOIN oknardia_merchantoffice'
' ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id'
' INNER JOIN oknardia_priceoffer'
' ON oknardia_priceoffer.kOfferFromUser_id = oknardia_ouruser.id'
' INNER JOIN oknardia_merchantbrand'
' ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id'
' WHERE Q_ID = oknardia_merchantoffice.kMerchantName_id '
'GROUP BY oknardia_merchantoffice.kMerchantName_id '
'ORDER BY Q.RatingAVG DESC;')
list_company = list(q_company)
for i in list_company:
i.STARS = get_rating_set_for_stars(i.RatingAVG)
i.NumSets = pytils.numeral.get_plural(i.NumSets, u"оконный набор, оконных набора, оконных наборов")
i.NumOffers = pytils.numeral.get_plural(i.NumOffers, u"вариант, варианта, вариантов")
i.lastUpdate = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.lastUpdate, 'U')))
i.sMerchantMainURL = pytils.translit.slugify(i.sMerchantName)
# print("NAME:", i.sMerchantName, "\tNumSets:", i.NumSets, "\tNumOffers:", i.NumOffers,
# "\t:AverageRating:", i.RatingAVG, "\tAveragePrice:", i.PriceAVG, "\tSTARS:", i.STARS)
to_template.update({
'COMPANIES': list_company,
# получаем последние визиты клиента через куки
'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]),
# получаем последние визиты всех посетителей из базы
# id2log, log_visit = get_last_all_user_visit_list()
"""
Показывает список всех производителей с ключевыми показателями.
GET параметры: опционально могут использоваться для фильтрации
Контекст шаблона:
- COMPANIES (list): Список компаний с статистикой
- LAST_VISIT (list): Последние визиты текущего пользователя
- LOG_VISIT (list): Последние визиты всех пользователей
Args:
request (HttpRequest): HTTP запрос от клиента
Returns:
HttpResponse: Отрендеренная HTML страница со списком компаний
"""
# Получаем статистику по компаниям с использованием ORM
companies_list = _get_company_statistics()
# Форматируем каждую компанию для вывода в шаблон
formatted_companies = [
_format_company_for_template(company)
for company in companies_list
]
# Получаем информацию о посещениях для персонализации
to_template: dict[str, object] = {
'COMPANIES': formatted_companies,
'LAST_VISIT': get_last_user_visit_list(
get_last_user_visit_cookies(request)[:3]
),
'LOG_VISIT': get_last_all_user_visit_list(),
'ticks': float(time.perf_counter() - time_start)
})
}
return render(request, "catalog/catalog_company.html", to_template)
def catalog_company_detail(request: HttpRequest, company_id: str, company_name_slug: str) -> HttpResponse:
def _lowercase_first_char(text: str) -> str:
"""
Преобразует первый символ строки в нижний регистр.
Args:
text (str): Исходная строка
Returns:
str: Строка с строчным первым символом (если длина > 0)
"""
return text[0].lower() + text[1:] if len(text) > 0 else text
def _clean_text_field(text: str, empty_values: list) -> str:
"""
Очищает текстовое поле, удаляя типичные маркеры "пусто" и преобразуя
первый символ в нижний регистр.
Args:
text (str): Исходный текст
empty_values (list): Список значений, которые считаются "пустыми"
Returns:
str: Очищенный текст или пустая строка если значение в empty_values
"""
if text.lower() in empty_values:
return ""
return _lowercase_first_char(text)
def _get_company_sets_detail(company_id: int) -> list:
"""
Получает все оконные наборы для компании с полной статистикой по ценам.
Использует оптимизированные select_related и prefetch_related для минимизации
запросов к БД. Группирует данные по наборам (SetKit) с уникальностью.
Args:
company_id (int): ID компании (MerchantBrand)
Returns:
list: Список словарей с данными наборов, отсортированные по рейтингу
"""
# Получаем активные ценовые предложения для компаний с агрегацией по наборам
price_stats = (
PriceOffer.objects
.filter(
sOfferActive=True,
kOfferFromUser__kMerchantOffice__kMerchantName_id=company_id
)
.values('kOffer2SetKit_id')
.annotate(
num_offers=Count('id'),
price_avg=Avg('fOfferPrice', output_field=DecimalField()),
last_update=Max('dOfferModify'),
early_creation=Min('dOfferCreate')
)
)
# Преобразуем в словарь для быстрого доступа по ID набора
price_stats_dict = {
stat['kOffer2SetKit_id']: {
'num_offers': stat['num_offers'],
'price_avg': stat['price_avg'],
'last_update': stat['last_update'],
'early_creation': stat['early_creation']
}
for stat in price_stats
}
# Получаем все наборы компании с их зависимостями
# select_related оптимизирует ForeignKey запросы (профиль, стеклопакет)
sets_queryset = (
SetKit.objects
.filter(
kSet2User__kMerchantOffice__kMerchantName_id=company_id
)
.select_related(
'kSet2User',
'kSet2User__kMerchantOffice',
'kSet2User__kMerchantOffice__kMerchantName',
'kSet2PVCprofiles',
'kSet2Glazing'
)
.order_by('-fSetRating')
)
# Собираем результат, комбинируя данные SetKit с агрегированной статистикой
result = []
seen_set_ids = set()
for setkit in sets_queryset:
# Пропускаем дубликаты наборов (может быть несколько ценовых предложений
# для одного набора)
if setkit.id in seen_set_ids:
continue
seen_set_ids.add(setkit.id)
# Получаем статистику по ценам для этого набора
price_stat = price_stats_dict.get(setkit.id, {
'num_offers': 0,
'price_avg': None,
'last_update': None,
'early_creation': None
})
# Собираем все данные в один объект
result.append({
'setkit': setkit,
'num_offers': price_stat['num_offers'],
'price_avg': price_stat['price_avg'],
'last_update': price_stat['last_update'],
'early_creation': price_stat['early_creation'],
'merchant_office': setkit.kSet2User.kMerchantOffice,
'merchant_brand': setkit.kSet2User.kMerchantOffice.kMerchantName,
'profile': setkit.kSet2PVCprofiles,
'glazing': setkit.kSet2Glazing
})
return result
def _format_set_for_template(set_data: dict, empty_values: list) -> dict:
"""
Форматирует данные оконного набора для вывода в шаблон.
Применяет:
- Преобразование URL в удобный для отображения формат
- Разделение email адресов на части (для обфускации)
- Вычисление звёзд рейтинга
- Конвертация времени в читаемый формат
- Создание slugs для названий и производителей
- Склонение числительных(контуры, швы и т.п.)
- Очистку пустых полей от стандартных маркеров ("нет", "" и т.п.)
Args:
set_data (dict): Данные набора с объектами моделей
empty_values (list): Список значений, считаемых "пустыми"
Returns:
dict: Отформатированные данные для шаблона
"""
set_kit = set_data['setkit']
merchant_office = set_data['merchant_office']
merchant_brand = set_data['merchant_brand']
profile = set_data['profile']
glazing = set_data['glazing']
formatted = {
# Ключи ниже оставлены в legacy-формате, т.к. шаблон использует именно их имена.
'sSetName': set_kit.sSetName,
'sMerchantName': merchant_brand.sMerchantName,
'sMerchantDescription': merchant_brand.sMerchantDescription,
'fSetRating': {
'RATING': set_kit.fSetRating,
'STARS': get_rating_set_for_stars(set_kit.fSetRating)
},
'num_offers': set_data['num_offers'],
'price_avg': set_data['price_avg'],
'bSetDelivery': set_kit.bSetDelivery,
'bSetUninstallInstall': set_kit.bSetUninstallInstall,
'sSetImplementAll': set_kit.sSetImplementAll,
'sSetImplementHandles': set_kit.sSetImplementHandles,
'sMerchantMainURL': {
'URL': merchant_office.kMerchantName.sMerchantMainURL,
'URL_VIEW': re.sub(
r"^https?://|/$|www\.",
"",
merchant_office.kMerchantName.sMerchantMainURL
)
},
'sOfficePhones': merchant_office.sOfficePhones,
'sOfficeDescription': merchant_office.sOfficeDescription,
'sOfficeEmails': merchant_office.sOfficeEmails,
'sOfficeName': merchant_office.sOfficeName,
'sOfficeAddress': merchant_office.sOfficeAddress,
'fOfficeGeoCode_Latitude': merchant_office.fOfficeGeoCode_Latitude,
'fOfficeGeoCode_Longitude': merchant_office.fOfficeGeoCode_Longitude,
'sOfficeDiscountMetaFormula': merchant_office.sOfficeDiscountMetaFormula,
'pMerchantLogo': merchant_office.kMerchantName.pMerchantLogo,
'idPVC': profile.id,
'sProfileBriefDescription': profile.sProfileBriefDescription,
'iProfileCameras': profile.iProfileCameras,
'sProfileName': {
'NAME': profile.sProfileName,
'NAME_T': pytils.translit.slugify(profile.sProfileName)
},
'sProfileManufacturer': {
'NAME': profile.sProfileManufacturer,
'NAME_T': pytils.translit.slugify(profile.sProfileManufacturer)
},
'sProfileColor': profile.sProfileColor,
'sProfileSealDescription': profile.sProfileSealDescription,
'fProfileSeals': pytils.numeral.sum_string(
profile.fProfileSeals,
pytils.numeral.MALE,
"контур, контура, контуров"
),
'sGlazingBriefDescription': glazing.sGlazingBriefDescription,
'sGlazingManufacturer': glazing.sGlazingManufacturer,
'sGlazingMark': glazing.sGlazingMark,
'sGlazingToning': glazing.sGlazingToning,
'sSetImplementCatch': _clean_text_field(set_kit.sSetImplementCatch, empty_values),
'sSetClimateControl': _clean_text_field(set_kit.sSetClimateControl, empty_values),
'sProfileReinforcement': _lowercase_first_char(profile.sProfileReinforcement),
'sSetSill': _lowercase_first_char(set_kit.sSetSill),
'sSetPanes': _lowercase_first_char(set_kit.sSetPanes),
'sSetSlope': _lowercase_first_char(set_kit.sSetSlope),
'sSetUninstallInstall': _lowercase_first_char(set_kit.sSetUninstallInstall),
'sSetDelivery': _lowercase_first_char(set_kit.sSetDelivery),
'sSetOtherConditions': _lowercase_first_char(set_kit.sSetOtherConditions),
}
# Конвертируем даты в читаемый формат
if set_data['last_update']:
timestamp = int(django.utils.dateformat.format(set_data['last_update'], 'U'))
formatted['lastUpdate'] = pytils.dt.distance_of_time_in_words(timestamp)
if set_data['early_creation']:
timestamp = int(django.utils.dateformat.format(set_data['early_creation'],'U'))
formatted['earlyCreation'] = pytils.dt.distance_of_time_in_words(timestamp)
# Разделяем email на части для обфускации (показываем середину отдельно)
# На фронтенде JS собирает все обратно в валидный e-mail
if formatted['sOfficeEmails']:
try:
email_len = len(formatted['sOfficeEmails'])
k = random.randint(1, max(1, int(email_len / 2) - 1))
formatted['sOfficeEmails'] = [
formatted['sOfficeEmails'][0:k],
formatted['sOfficeEmails'][k:-k],
formatted['sOfficeEmails'][-k:]
]
except (ValueError, ZeroDivisionError):
# Если ошибка при случайном разделении, оставляем как есть
pass
return formatted
def catalog_company_detail(
request: HttpRequest,
company_id: str,
company_name_slug: str
) -> HttpResponse:
"""
Показывает детальную информацию о компании и все её оконные наборы.
Производит редирект если slug в URL не совпадает с актуальным.
GET параметры: опционально могут использоваться для фильтрации
Контекст шаблона:
- COMPANY (str): Название компании
- COMPANY_ID (int): ID компании
- COMPANY_T (str): Slug компании
- SETS (list): Список оконных наборов с их полной информацией
- IMG_FOR_BLOG (str): Логотип компании
- LIST_NOT (list): Стандартные маркеры "пусто"
- LAST_VISIT (list): Последние визиты текущего пользователя
- LOG_VISIT (list): Последние визиты всех пользователей
- ticks (float): Время выполнения представления (в секундах)
Args:
request (HttpRequest): HTTP запрос от клиента
company_id (str): ID компании в виде строки
company_name_slug (str): Slug названия компании из URL
Returns:
HttpResponse: Отрендеренная HTML страница с деталью компании или редирект
"""
time_start = time.perf_counter()
to_template: dict[str, object] = {} # словарь, для передачи шаблону
company_id = int(company_id)
q_by_id = MerchantBrand.objects.get(id=company_id)
if pytils.translit.slugify(q_by_id.sMerchantName) != company_name_slug:
return redirect('/catalog/company/%d-%s' % (company_id, pytils.translit.slugify(q_by_id.sMerchantName)))
to_template.update({'COMPANY': q_by_id.sMerchantName})
to_template.update({'COMPANY_ID': company_id})
to_template.update({'COMPANY_T': company_name_slug})
list_not = [u"нет", u"", ""]
to_template.update({'LIST_NOT': list_not})
q_sets = MerchantBrand.objects.raw(f"SELECT"
f" COUNT(oknardia_priceoffer.id) AS NumOffers,"
f" AVG(oknardia_priceoffer.fOfferPrice) AS priceAVG,"
f" MAX(oknardia_priceoffer.dOfferModify) AS lastUpdate,"
f" MIN(oknardia_priceoffer.dOfferCreate) AS earlyCreation,"
f" oknardia_merchantbrand.*,"
f" oknardia_merchantoffice.*,"
f" oknardia_merchantoffice.id AS idMERCH,"
f" oknardia_setkit.*,"
f" oknardia_setkit.id AS idSET,"
f" oknardia_pvcprofiles.*,"
f" oknardia_pvcprofiles.id AS idPVC,"
f" oknardia_glazing.*, "
f" oknardia_glazing.id AS idGLAZ "
f"FROM oknardia_ouruser"
f" INNER JOIN oknardia_merchantoffice"
f" ON oknardia_ouruser.kMerchantOffice_id = oknardia_merchantoffice.id"
f" INNER JOIN oknardia_merchantbrand"
f" ON oknardia_merchantoffice.kMerchantName_id = oknardia_merchantbrand.id"
f" INNER JOIN oknardia_setkit"
f" ON oknardia_setkit.kSet2User_id = oknardia_ouruser.id"
f" INNER JOIN oknardia_priceoffer"
f" ON oknardia_priceoffer.kOffer2SetKit_id = oknardia_setkit.id"
f" INNER JOIN oknardia_pvcprofiles"
f" ON oknardia_setkit.kSet2PVCprofiles_id = oknardia_pvcprofiles.id"
f" INNER JOIN oknardia_glazing"
f" ON oknardia_setkit.kSet2Glazing_id = oknardia_glazing.id "
f"WHERE oknardia_merchantbrand.id = {company_id} "
f"AND oknardia_priceoffer.sOfferActive = TRUE "
f"GROUP BY oknardia_merchantoffice.id,"
f" oknardia_setkit.id,"
f" oknardia_setkit.fSetRating "
f"ORDER BY oknardia_setkit.fSetRating DESC;")
list_sets = list(q_sets)
for i in list_sets:
i.sMerchantMainURL = {"URL": i.sMerchantMainURL,
"URL_VIEW": re.sub(r"(?:^http://|^https://|/$|www\.)", "", i.sMerchantMainURL)}
k = random.randint(1, int(len(i.sOfficeEmails)/2) - 1)
i.sOfficeEmails = [i.sOfficeEmails[0:k], i.sOfficeEmails[k:-k], i.sOfficeEmails[-k:]]
to_template.update({'IMG_FOR_BLOG': i.pMerchantLogo})
i.fSetRating = {"RATING": i.fSetRating,
"STARS": get_rating_set_for_stars(i.fSetRating)}
i.lastUpdate = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.lastUpdate, 'U')))
i.earlyCreation = pytils.dt.distance_of_time_in_words(int(django.utils.dateformat.format(i.earlyCreation, 'U')))
i.sProfileName = {"NAME": i.sProfileName,
"NAME_T": pytils.translit.slugify(i.sProfileName)}
i.sProfileManufacturer = {"NAME": i.sProfileManufacturer,
"NAME_T": pytils.translit.slugify(i.sProfileManufacturer)}
i.fProfileSeals = pytils.numeral.sum_string(i.fProfileSeals, pytils.numeral.MALE, u"контур, контура, контуров")
if i.sSetImplementCatch.lower() in list_not:
i.sSetImplementCatch = ""
if i.sSetClimateControl.lower() in list_not:
i.sSetClimateControl = ""
if len(i.sProfileReinforcement) > 0:
i.sProfileReinforcement = i.sProfileReinforcement[0].lower()+i.sProfileReinforcement[1:]
if len(i.sSetSill) > 0:
i.sSetSill = i.sSetSill[0].lower()+i.sSetSill[1:]
if len(i.sSetPanes) > 0:
i.sSetPanes = i.sSetPanes[0].lower()+i.sSetPanes[1:]
if len(i.sSetSlope) > 0:
i.sSetSlope = i.sSetSlope[0].lower()+i.sSetSlope[1:]
if len(i.sSetUninstallInstall) > 0:
i.sSetUninstallInstall = i.sSetUninstallInstall[0].lower()+i.sSetUninstallInstall[1:]
if len(i.sSetDelivery) > 0:
i.sSetDelivery = i.sSetDelivery[0].lower()+i.sSetDelivery[1:]
if len(i.sSetOtherConditions) > 0:
i.sSetOtherConditions = i.sSetOtherConditions[0].lower()+i.sSetOtherConditions[1:]
to_template.update({
'SETS': list_sets,
# получаем последние визиты клиента через куки
'LAST_VISIT': get_last_user_visit_list(get_last_user_visit_cookies(request)[:3]),
# получаем последние визиты всех посетителей из базы
# id2log, log_visit = get_last_all_user_visit_list()
company_id_int = int(company_id)
# Получаем компанию или возвращаем 404
try:
company = MerchantBrand.objects.get(id=company_id_int)
except MerchantBrand.DoesNotExist:
raise Http404("Компания не найдена")
# Проверяем что slug совпадает (для SEO и красивых URL)
actual_slug = pytils.translit.slugify(company.sMerchantName)
if actual_slug != company_name_slug:
return redirect(
f'/catalog/company/{company_id_int}-{actual_slug}'
)
# Типичные маркеры, которые означают что поле пусто
empty_values = ["нет", "", ""]
# Получаем все наборы компании с ценовой статистикой
sets_list = _get_company_sets_detail(company_id_int)
# Форматируем каждый набор для вывода в шаблон
formatted_sets = [
_format_set_for_template(set_data, empty_values)
for set_data in sets_list
]
to_template: dict[str, object] = {
'COMPANY': company.sMerchantName,
'COMPANY_ID': company_id_int,
'COMPANY_T': company_name_slug,
'SETS': formatted_sets,
'HEADER': f'Изготовитель окон «{company.sMerchantName}»',
'META_KEYWORDS': company.sMerchantName,
'IMG_FOR_BLOG': company.pMerchantLogo,
'LIST_NOT': empty_values,
'LAST_VISIT': get_last_user_visit_list(
get_last_user_visit_cookies(request)[:3]
),
'LOG_VISIT': get_last_all_user_visit_list(),
'ticks': float(time.perf_counter() - time_start)
})
}
# Добавляем метрику выполнения представления
to_template['ticks'] = float(time.perf_counter() - time_start)
return render(request, "catalog/catalog_company_detail.html", to_template)