mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #33662 -- Allowed Sitemap to customize languages for each item.
This commit is contained in:
parent
ab7a85ac29
commit
289e9a75af
@ -92,6 +92,10 @@ class Sitemap:
|
|||||||
return attr(item)
|
return attr(item)
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
def get_languages_for_item(self, item):
|
||||||
|
"""Languages for which this item is displayed."""
|
||||||
|
return self._languages()
|
||||||
|
|
||||||
def _languages(self):
|
def _languages(self):
|
||||||
if self.languages is not None:
|
if self.languages is not None:
|
||||||
return self.languages
|
return self.languages
|
||||||
@ -103,8 +107,8 @@ class Sitemap:
|
|||||||
# This is necessary to paginate with all languages already considered.
|
# This is necessary to paginate with all languages already considered.
|
||||||
items = [
|
items = [
|
||||||
(item, lang_code)
|
(item, lang_code)
|
||||||
for lang_code in self._languages()
|
|
||||||
for item in self.items()
|
for item in self.items()
|
||||||
|
for lang_code in self.get_languages_for_item(item)
|
||||||
]
|
]
|
||||||
return items
|
return items
|
||||||
return self.items()
|
return self.items()
|
||||||
@ -201,7 +205,8 @@ class Sitemap:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.i18n and self.alternates:
|
if self.i18n and self.alternates:
|
||||||
for lang_code in self._languages():
|
item_languages = self.get_languages_for_item(item[0])
|
||||||
|
for lang_code in item_languages:
|
||||||
loc = f"{protocol}://{domain}{self._location(item, lang_code)}"
|
loc = f"{protocol}://{domain}{self._location(item, lang_code)}"
|
||||||
url_info["alternates"].append(
|
url_info["alternates"].append(
|
||||||
{
|
{
|
||||||
@ -209,7 +214,7 @@ class Sitemap:
|
|||||||
"lang_code": lang_code,
|
"lang_code": lang_code,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if self.x_default:
|
if self.x_default and settings.LANGUAGE_CODE in item_languages:
|
||||||
lang_code = settings.LANGUAGE_CODE
|
lang_code = settings.LANGUAGE_CODE
|
||||||
loc = f"{protocol}://{domain}{self._location(item, lang_code)}"
|
loc = f"{protocol}://{domain}{self._location(item, lang_code)}"
|
||||||
loc = loc.replace(f"/{lang_code}/", "/", 1)
|
loc = loc.replace(f"/{lang_code}/", "/", 1)
|
||||||
|
@ -311,6 +311,15 @@ Note:
|
|||||||
The latest ``lastmod`` returned by calling the method with all
|
The latest ``lastmod`` returned by calling the method with all
|
||||||
items returned by :meth:`Sitemap.items`.
|
items returned by :meth:`Sitemap.items`.
|
||||||
|
|
||||||
|
.. method:: Sitemap.get_languages_for_item(item, lang_code)
|
||||||
|
|
||||||
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
|
**Optional.** A method that returns the sequence of language codes for
|
||||||
|
which the item is displayed. By default
|
||||||
|
:meth:`~Sitemap.get_languages_for_item` returns
|
||||||
|
:attr:`~Sitemap.languages`.
|
||||||
|
|
||||||
Shortcuts
|
Shortcuts
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
@ -145,7 +145,8 @@ Minor features
|
|||||||
:mod:`django.contrib.sitemaps`
|
:mod:`django.contrib.sitemaps`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The new :meth:`.Sitemap.get_languages_for_item` method allows customizing the
|
||||||
|
list of languages for which the item is displayed.
|
||||||
|
|
||||||
:mod:`django.contrib.sites`
|
:mod:`django.contrib.sites`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -10,7 +10,7 @@ from django.utils.deprecation import RemovedInDjango50Warning
|
|||||||
from django.utils.formats import localize
|
from django.utils.formats import localize
|
||||||
|
|
||||||
from .base import SitemapTestsBase
|
from .base import SitemapTestsBase
|
||||||
from .models import TestModel
|
from .models import I18nTestModel, TestModel
|
||||||
|
|
||||||
|
|
||||||
class HTTPSitemapTests(SitemapTestsBase):
|
class HTTPSitemapTests(SitemapTestsBase):
|
||||||
@ -440,6 +440,72 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||||||
)
|
)
|
||||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||||
|
|
||||||
|
@override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
|
||||||
|
def test_language_for_item_i18n_sitemap(self):
|
||||||
|
"""
|
||||||
|
A i18n sitemap index in which item can be chosen to be displayed for a
|
||||||
|
lang or not.
|
||||||
|
"""
|
||||||
|
only_pt = I18nTestModel.objects.create(name="Only for PT")
|
||||||
|
response = self.client.get("/item-by-lang/i18n.xml")
|
||||||
|
url, pk, only_pt_pk = self.base_url, self.i18n_model.pk, only_pt.pk
|
||||||
|
expected_urls = (
|
||||||
|
f"<url><loc>{url}/en/i18n/testmodel/{pk}/</loc>"
|
||||||
|
f"<changefreq>never</changefreq><priority>0.5</priority></url>"
|
||||||
|
f"<url><loc>{url}/pt/i18n/testmodel/{pk}/</loc>"
|
||||||
|
f"<changefreq>never</changefreq><priority>0.5</priority></url>"
|
||||||
|
f"<url><loc>{url}/pt/i18n/testmodel/{only_pt_pk}/</loc>"
|
||||||
|
f"<changefreq>never</changefreq><priority>0.5</priority></url>"
|
||||||
|
)
|
||||||
|
expected_content = (
|
||||||
|
f'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||||
|
f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
|
||||||
|
f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
|
||||||
|
f"{expected_urls}\n"
|
||||||
|
f"</urlset>"
|
||||||
|
)
|
||||||
|
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||||
|
|
||||||
|
@override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
|
||||||
|
def test_alternate_language_for_item_i18n_sitemap(self):
|
||||||
|
"""
|
||||||
|
A i18n sitemap index in which item can be chosen to be displayed for a
|
||||||
|
lang or not.
|
||||||
|
"""
|
||||||
|
only_pt = I18nTestModel.objects.create(name="Only for PT")
|
||||||
|
response = self.client.get("/item-by-lang-alternates/i18n.xml")
|
||||||
|
url, pk, only_pt_pk = self.base_url, self.i18n_model.pk, only_pt.pk
|
||||||
|
expected_urls = (
|
||||||
|
f"<url><loc>{url}/en/i18n/testmodel/{pk}/</loc>"
|
||||||
|
f"<changefreq>never</changefreq><priority>0.5</priority>"
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>'
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>'
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/></url>'
|
||||||
|
f"<url><loc>{url}/pt/i18n/testmodel/{pk}/</loc>"
|
||||||
|
f"<changefreq>never</changefreq><priority>0.5</priority>"
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>'
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>'
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/></url>'
|
||||||
|
f"<url><loc>{url}/pt/i18n/testmodel/{only_pt_pk}/</loc>"
|
||||||
|
f"<changefreq>never</changefreq><priority>0.5</priority>"
|
||||||
|
f'<xhtml:link rel="alternate" '
|
||||||
|
f'hreflang="pt" href="{url}/pt/i18n/testmodel/{only_pt_pk}/"/></url>'
|
||||||
|
)
|
||||||
|
expected_content = (
|
||||||
|
f'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||||
|
f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
|
||||||
|
f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
|
||||||
|
f"{expected_urls}\n"
|
||||||
|
f"</urlset>"
|
||||||
|
)
|
||||||
|
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||||
|
|
||||||
def test_sitemap_without_entries(self):
|
def test_sitemap_without_entries(self):
|
||||||
response = self.client.get("/sitemap-without-entries/sitemap.xml")
|
response = self.client.get("/sitemap-without-entries/sitemap.xml")
|
||||||
expected_content = (
|
expected_content = (
|
||||||
|
@ -48,6 +48,22 @@ class XDefaultI18nSitemap(AlternatesI18nSitemap):
|
|||||||
x_default = True
|
x_default = True
|
||||||
|
|
||||||
|
|
||||||
|
class ItemByLangSitemap(SimpleI18nSitemap):
|
||||||
|
def get_languages_for_item(self, item):
|
||||||
|
if item.name == "Only for PT":
|
||||||
|
return ["pt"]
|
||||||
|
return super().get_languages_for_item(item)
|
||||||
|
|
||||||
|
|
||||||
|
class ItemByLangAlternatesSitemap(AlternatesI18nSitemap):
|
||||||
|
x_default = True
|
||||||
|
|
||||||
|
def get_languages_for_item(self, item):
|
||||||
|
if item.name == "Only for PT":
|
||||||
|
return ["pt"]
|
||||||
|
return super().get_languages_for_item(item)
|
||||||
|
|
||||||
|
|
||||||
class EmptySitemap(Sitemap):
|
class EmptySitemap(Sitemap):
|
||||||
changefreq = "never"
|
changefreq = "never"
|
||||||
priority = 0.5
|
priority = 0.5
|
||||||
@ -168,6 +184,14 @@ xdefault_i18n_sitemaps = {
|
|||||||
"i18n-xdefault": XDefaultI18nSitemap,
|
"i18n-xdefault": XDefaultI18nSitemap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item_by_lang_i18n_sitemaps = {
|
||||||
|
"i18n-item-by-lang": ItemByLangSitemap,
|
||||||
|
}
|
||||||
|
|
||||||
|
item_by_lang_alternates_i18n_sitemaps = {
|
||||||
|
"i18n-item-by-lang-alternates": ItemByLangAlternatesSitemap,
|
||||||
|
}
|
||||||
|
|
||||||
simple_sitemaps_not_callable = {
|
simple_sitemaps_not_callable = {
|
||||||
"simple": SimpleSitemap(),
|
"simple": SimpleSitemap(),
|
||||||
}
|
}
|
||||||
@ -358,6 +382,18 @@ urlpatterns = [
|
|||||||
{"sitemaps": sitemaps_lastmod_ascending},
|
{"sitemaps": sitemaps_lastmod_ascending},
|
||||||
name="django.contrib.sitemaps.views.sitemap",
|
name="django.contrib.sitemaps.views.sitemap",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"item-by-lang/i18n.xml",
|
||||||
|
views.sitemap,
|
||||||
|
{"sitemaps": item_by_lang_i18n_sitemaps},
|
||||||
|
name="django.contrib.sitemaps.views.sitemap",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"item-by-lang-alternates/i18n.xml",
|
||||||
|
views.sitemap,
|
||||||
|
{"sitemaps": item_by_lang_alternates_i18n_sitemaps},
|
||||||
|
name="django.contrib.sitemaps.views.sitemap",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"lastmod-sitemaps/descending.xml",
|
"lastmod-sitemaps/descending.xml",
|
||||||
views.sitemap,
|
views.sitemap,
|
||||||
|
Loading…
Reference in New Issue
Block a user