Оптимизация под поисковую выдачу

This commit is contained in:
NikDizell 2025-11-12 17:55:07 +03:00
parent 32b1ec4379
commit cfd318955b
21 changed files with 274 additions and 1 deletions

View File

@ -40,6 +40,8 @@ INSTALLED_APPS = [
'programmer.apps.ProgrammerConfig', 'programmer.apps.ProgrammerConfig',
'django_bootstrap5', 'django_bootstrap5',
'django_extensions', 'django_extensions',
'django.contrib.sites',
'django.contrib.sitemaps',
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -0,0 +1,27 @@
from django.core.management.base import BaseCommand
from django.contrib.sitemaps import Sitemap
from django.urls import reverse
from programmer.models import Home, Solution, Competence, Recall
class Command(BaseCommand):
help = 'Test sitemap generation'
def handle(self, *args, **options):
from programmer.sitemaps import sitemaps
self.stdout.write('Testing sitemap generation...')
for name, sitemap in sitemaps.items():
self.stdout.write(f'\n{name}:')
items = sitemap().items()
self.stdout.write(f' Items found: {len(items)}')
for item in items[:3]: # Показываем первые 3 элемента
try:
url = sitemap().location(item)
self.stdout.write(f' - {url}')
except Exception as e:
self.stdout.write(f' - Error: {e}')
self.stdout.write('\nSitemap test completed!')

View File

@ -1,6 +1,9 @@
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.cache import cache
class Recall(models.Model): class Recall(models.Model):
@ -125,3 +128,12 @@ class Visitor(models.Model):
indexes = [ indexes = [
models.Index(fields=['ip_address']), models.Index(fields=['ip_address']),
] ]
@receiver([post_save, post_delete], sender=Home)
@receiver([post_save, post_delete], sender=Solution)
@receiver([post_save, post_delete], sender=Competence)
@receiver([post_save, post_delete], sender=Recall)
def clear_sitemap_cache(sender, **kwargs):
"""Очищаем кэш sitemap при изменении контента"""
cache.delete('sitemap_cache')

View File

@ -0,0 +1,64 @@
from django.contrib.sitemaps import Sitemap
from django.urls import reverse
from .models import Home, Solution, Competence, Recall
class StaticViewSitemap(Sitemap):
priority = 1.0
changefreq = 'monthly'
def items(self):
return ['home', 'about', 'solution', 'ability', 'recall']
def location(self, item):
return reverse(item)
class HomeSitemap(Sitemap):
changefreq = 'weekly'
priority = 1.0
def items(self):
return Home.objects.filter(is_published=True)
def lastmod(self, obj):
return obj.time_update
# УБИРАЕМ метод location - используем default
# Django автоматически сгенерирует правильные URL
class SolutionSitemap(Sitemap):
changefreq = 'weekly'
priority = 0.9
def items(self):
return Solution.objects.filter(is_published=True)
def lastmod(self, obj):
return obj.time_update
class CompetenceSitemap(Sitemap):
changefreq = 'monthly'
priority = 0.8
def items(self):
return Competence.objects.filter(is_published=True)
def lastmod(self, obj):
return obj.time_update
class RecallSitemap(Sitemap):
changefreq = 'monthly'
priority = 0.7
def items(self):
return Recall.objects.filter(is_published=True)
def lastmod(self, obj):
return obj.time_update
# Упрощаем sitemaps - убираем HomeSitemap если он дублирует главную
sitemaps = {
'static': StaticViewSitemap,
'solutions': SolutionSitemap,
'competence': CompetenceSitemap,
'recall': RecallSitemap,
}

View File

@ -118,4 +118,37 @@
</div> </div>
</div> </div>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"serviceType": "1С программирование",
"provider": {
"@type": "Person",
"name": "Николай Сердюк"
},
"areaServed": "Россия",
"hasOfferCatalog": {
"@type": "OfferCatalog",
"name": "Услуги программиста 1С",
"itemListElement": [
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Разработка конфигураций 1С"
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Интеграция 1С с веб-сервисами"
}
}
]
}
}
</script>
{% endblock %} {% endblock %}

View File

@ -5,6 +5,28 @@
<html lang="ru"> <html lang="ru">
<head> <head>
<title>{{title}} - Программист 1С</title> <title>{{title}} - Программист 1С</title>
<!-- Основные мета-теги -->
<meta name="description" content="{% block meta_description %}Профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция и оптимизация систем 1С.{% endblock %}">
<meta name="keywords" content="{% block meta_keywords %}программист 1С, разработка 1С, интеграция 1С, оптимизация 1С, 1С предприятие{% endblock %}">
<meta name="author" content="Николай Сердюк">
<!-- Open Graph для соцсетей -->
<meta property="og:title" content="{{title}} - Программист 1С">
<meta property="og:description" content="Профессиональный программист 1С с более чем 10-летним опытом">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ request.build_absolute_uri }}">
<meta property="og:image" content="{% static 'programmer/images/og-image.jpg' %}">
<meta property="og:site_name" content="Программист 1С - Николай Сердюк">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{title}} - Программист 1С">
<meta name="twitter:description" content="Профессиональный программист 1С с более чем 10-летним опытом">
<meta name="twitter:image" content="{% static 'programmer/images/og-image.jpg' %}">
<!-- Дополнительные SEO-теги -->
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
<link rel="canonical" href="{{ request.build_absolute_uri }}">
{% bootstrap_css %} {% bootstrap_css %}
<!-- Основной CSS файл (темная тема по умолчанию) --> <!-- Основной CSS файл (темная тема по умолчанию) -->
@ -339,6 +361,58 @@
{% block extra_css %} {% block extra_css %}
<!-- Дополнительные CSS файлы для конкретных страниц --> <!-- Дополнительные CSS файлы для конкретных страниц -->
{% endblock %} {% endblock %}
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-X3W9YSQHRM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-X3W9YSQHRM');
</script>
<!-- Яндекс.Метрика -->
<script type="text/javascript">
(function(m,e,t,r,i,k,a){
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=105278924', 'ym');
ym(105278924, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", accurateTrackBounce:true, trackLinks:true});
</script>
<noscript><div><img src="https://mc.yandex.ru/watch/105278924" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Николай Сердюк",
"jobTitle": "Программист 1С",
"description": "Профессиональный программист 1С с более чем 10-летним опытом",
"url": "https://nikdizell.ru",
"email": "{{ CONTACT_EMAIL }}",
"telephone": "{{ CONTACT_PHONE }}",
"knowsAbout": [
"1С:Предприятие 8.3",
"Управление торговлей",
"Бухгалтерия предприятия",
"Зарплата и управление персоналом",
"SQL",
"Веб-сервисы",
"API интеграция"
],
"hasOccupation": {
"@type": "Occupation",
"name": "Программист 1С",
"description": "Разработка и сопровождение систем на платформе 1С",
"occupationLocation": "Россия"
}
}
</script>
</head> </head>
<body> <body>
<!-- Header --> <!-- Header -->

View File

@ -0,0 +1,13 @@
/* TEAM */
Developer: Николай Сердюк
Site: https://nikdizell.ru
Email: {{ CONTACT_EMAIL }}
/* THANKS */
Django Framework
Bootstrap
/* SITE */
Last update: 2025
Language: Russian
Doctype: HTML5

View File

@ -0,0 +1,18 @@
User-agent: *
Allow: /
# Основной сайт
Sitemap: {{ request.scheme }}://{{ request.get_host }}/sitemap.xml
# Запрещаем служебные разделы
Disallow: /admin/
Disallow: /static/admin/
Disallow: /callback/
Disallow: /api/
# Разрешаем индексацию статических файлов
Allow: /static/
Allow: /media/
# Указываем главное зеркало
Host: {{ request.scheme }}://{{ request.get_host }}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
{% for url in urlset %}
<url>
<loc>{{ url.location }}</loc>
{% if url.lastmod %}<lastmod>{{ url.lastmod|date:"Y-m-d" }}</lastmod>{% endif %}
{% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
{% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
</url>
{% endfor %}
</urlset>

View File

@ -3,6 +3,9 @@ from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from .views import * from .views import *
from django.contrib.sitemaps.views import sitemap
from .sitemaps import sitemaps
urlpatterns = [ urlpatterns = [
path('', index, name='home'), path('', index, name='home'),
@ -13,7 +16,12 @@ urlpatterns = [
path('post/<int:post_id>/', show_post, name='post'), path('post/<int:post_id>/', show_post, name='post'),
path('callback/', callback_request, name='callback'), path('callback/', callback_request, name='callback'),
path('admin/statistics/', statistics_view, name='statistics'), path('admin/statistics/', statistics_view, name='statistics'),
# Sitemap
path('sitemap.xml', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
path('robots.txt', robots_txt, name='robots'),
] ]
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -9,6 +9,7 @@ from datetime import timedelta
from .models import PageView, Visitor from .models import PageView, Visitor
from django.db.models import Count from django.db.models import Count
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, user_passes_test
from django.views.decorators.http import require_GET
menu = [ menu = [
@ -107,6 +108,8 @@ def index(request):
'posts': posts, 'posts': posts,
'menu': menu, 'menu': menu,
'title': "Главная страница", 'title': "Главная страница",
'meta_description': "Профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция и оптимизация систем 1С. Закажите консультацию.",
'meta_keywords': "программист 1С, разработка 1С, интеграция 1С, оптимизация 1С, 1С предприятие 8.3",
'form': CallbackForm() 'form': CallbackForm()
} }
return render(request, 'programmer/index.html', context=context) return render(request, 'programmer/index.html', context=context)
@ -116,7 +119,9 @@ def index(request):
def about(request): def about(request):
context = { context = {
'menu': menu, 'menu': menu,
'title': "Обо мне" 'title': "Обо мне - Программист 1С",
'meta_description': "Николай Сердюк - профессиональный программист 1С с более чем 10-летним опытом. Разработка, интеграция, оптимизация бизнес-процессов.",
'meta_keywords': "программист 1С Николай Сердюк, опыт работы 1С, компетенции 1С, проекты 1С"
} }
return render(request, 'programmer/about.html', context=context) return render(request, 'programmer/about.html', context=context)
@ -233,3 +238,8 @@ def statistics_view(request):
} }
return render(request, 'admin/statistics.html', context) return render(request, 'admin/statistics.html', context)
@require_GET
def robots_txt(request):
return render(request, 'robots.txt', content_type='text/plain')