Здравствуйте! Если вы читали мои предыдущие посты на тему создания своей CMS с использованием Django, то могли заметить, что урлы в страницах выглядят примерно так:
/page/parent1/parent2/some_page/
Поясню зачем это нужно. Как известно в среде сеошников, даже URL страницы иногда оказывает существенное влияние на поисковые системы и позволяет подниматься в поиске выше конкурентов, если содержит в себе ключевые слова для поиска. К примеру, вы написали статью, которая помогает «прокачивать» ваш сайт по семантическому ядру вашего сайта. Статья содержит в себе ключевые запросы по данной теме, теги H1 и H2 и т.д. Что можно еще добавить для улучшения показателей? Ответ очевиден - URL страницы.
Теперь расскажу, как это сделать в Django. Как вы уже наверное знаете, Django не очень любит русские символы в строке запроса, следовательно нужно обходиться латиницей. Создаем поле slug типа SlugFiield и в классе страницы пишем:
prepopulated_fields = {
'slug': ('title',)
}
, где title - заголовок вашей страницы. Таким образом урл будет приводится к латинице. Поисковики и латиницу прекрасно прочтут. Хотя, я не сеошник и всех таинств этой профессии не познавал.
Дальше, нам необходимо определить в классе модели метод get_absolute_url() и внутрь него добавить следующее:
def get_absolute_url(self):
if self.parent is None:
return '/page/%s/' % self.slug
if self.homepage:
return '/'
url = get_all_ancestors(self.parent, self.slug)
return '/page%s' % url
get_absolute_url.short_description = _('URL')
В моем случае, есть поле parent, которое имеет тип TreeForeignKey из mptt. А также, тип BooleanField поля homepage, которое возвращает главную страницу сайта. Это удобно. Теперь осталось разобраться с get_all_ancestors(). Это функция, которая имеет вид:
def get_all_ancestors(parent, url = ''):
url = '%s/%s' % (parent.slug, url)
if parent.parent:
return get_all_ancestors(parent.parent, url)
else:
return '/%s/' % url
Как видно, это рекурсивная функция, которая последовательно обходит всех родителей и возвращает конечный URL. Конечно, это очень расточительно, так как каждый вызов функции - это проблема производительности, и при этом каждый раз выполняется по одному SELECT запросу. Это уже не так страшно из-за кеширования, но все же. Я выбрал данный способ, потому что он наиболее очевиден. Возможно, будет лучше сделать один запрос в базу и вернуть все дерево, затем уже работать с данными. Это тоже можно сделать, но мне лень. Если у вас намечается высоконагруженный проект, то лучше вам поступить именно так :) Ах, да. Не забудьте добавить в класс Meta:
unique_together = ('parent', 'slug')
И в метод clean():
if Page.objects.filter(slug = self.slug, parent = None).exclude(
id = self.id).exists() and self.parent is None:
raise ValidationError(
_('Record with this slug already exists')
)
Это для того, чтобы избежать повторных slug без родителей. И метод save() добавить следующее для того, чтобы предотвратить создание двух домашних страниц:
def save(self, *args, **kwargs):
if self.homepage is True:
try:
page = Page.objects.get(homepage = True)
page.homepage = False
page.save()
except ObjectDoesNotExist:
self.homepage = True
return super(Page, self).save(*args, **kwargs)
А urls.py будет иметь вид:
urlpatterns = patterns('',
url(r'^$', 'ваше представление index'),
url(r'^page/[a-z0-9-_]+', 'ваше представление page'),
)
И views.py:
def index(request):
try:
page = Page.objects.get(homepage = True)
except ObjectDoesNotExist:
raise Http404
return render_to_response(
'index.html',
{'page': page},
context_instance = RequestContext(request)
)
def page(request):
try:
path = request.path.lstrip('page')
path = path.strip('/').split('/')
page = Page.objects.get(
slug = path[-1], homepage = False
) or Page.objects.get(
slug = path[-1],
parent__slug = path[-2],
homepage = False
)
except ObjectDoesNotExist:
raise Http404
return render_to_response(
'page.html',
{'page': page},
context_instance = RequestContext(request)
)
Как видно, path разбивает строку запроса и находит страницу с parent.slug равным предпоследнему в списке и slug страницы равным последнему в списке. Пожалуй, это все. Спасибо за внимание! Удачи вам в ваших проектах!