Fixed #25916 -- Added lastmod support to sitemap index view.
Co-authored-by: Matthew Downey <matthew.downey@webit.com.au>
This commit is contained in:
parent
2ce03a2bac
commit
480191244d
|
@ -157,6 +157,17 @@ class Sitemap:
|
|||
domain = self.get_domain(site)
|
||||
return self._urls(page, protocol, domain)
|
||||
|
||||
def get_latest_lastmod(self):
|
||||
if not hasattr(self, 'lastmod'):
|
||||
return None
|
||||
if callable(self.lastmod):
|
||||
try:
|
||||
return max([self.lastmod(item) for item in self.items()])
|
||||
except TypeError:
|
||||
return None
|
||||
else:
|
||||
return self.lastmod
|
||||
|
||||
def _urls(self, page, protocol, domain):
|
||||
urls = []
|
||||
latest_lastmod = None
|
||||
|
@ -226,3 +237,8 @@ class GenericSitemap(Sitemap):
|
|||
if self.date_field is not None:
|
||||
return getattr(item, self.date_field)
|
||||
return None
|
||||
|
||||
def get_latest_lastmod(self):
|
||||
if self.date_field is not None:
|
||||
return self.queryset.order_by('-' + self.date_field).values_list(self.date_field, flat=True).first()
|
||||
return None
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{% for location in sitemaps %}<sitemap><loc>{{ location }}</loc></sitemap>{% endfor %}
|
||||
{% spaceless %}
|
||||
{% for site in sitemaps %}
|
||||
<sitemap>
|
||||
<loc>{{ site.location }}</loc>
|
||||
{% if site.last_mod %}
|
||||
<lastmod>{{ site.last_mod|date:"c" }}</lastmod>
|
||||
{% endif %}
|
||||
</sitemap>
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
</sitemapindex>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import datetime
|
||||
import warnings
|
||||
from dataclasses import dataclass
|
||||
from functools import wraps
|
||||
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
|
@ -7,9 +9,22 @@ from django.http import Http404
|
|||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.deprecation import RemovedInDjango50Warning
|
||||
from django.utils.http import http_date
|
||||
|
||||
|
||||
@dataclass
|
||||
class SitemapIndexItem:
|
||||
location: str
|
||||
last_mod: bool = None
|
||||
|
||||
# RemovedInDjango50Warning
|
||||
def __str__(self):
|
||||
msg = 'Calling `__str__` on SitemapIndexItem is deprecated, use the `location` attribute instead.'
|
||||
warnings.warn(msg, RemovedInDjango50Warning, stacklevel=2)
|
||||
return self.location
|
||||
|
||||
|
||||
def x_robots_tag(func):
|
||||
@wraps(func)
|
||||
def inner(request, *args, **kwargs):
|
||||
|
@ -19,6 +34,18 @@ def x_robots_tag(func):
|
|||
return inner
|
||||
|
||||
|
||||
def _get_latest_lastmod(current_lastmod, new_lastmod):
|
||||
"""
|
||||
Returns the latest `lastmod` where `lastmod` can be either a date or a
|
||||
datetime.
|
||||
"""
|
||||
if not isinstance(new_lastmod, datetime.datetime):
|
||||
new_lastmod = datetime.datetime.combine(new_lastmod, datetime.time.min)
|
||||
if timezone.is_naive(new_lastmod):
|
||||
new_lastmod = timezone.make_aware(new_lastmod, timezone.utc)
|
||||
return new_lastmod if current_lastmod is None else max(current_lastmod, new_lastmod)
|
||||
|
||||
|
||||
@x_robots_tag
|
||||
def index(request, sitemaps,
|
||||
template_name='sitemap_index.xml', content_type='application/xml',
|
||||
|
@ -28,6 +55,8 @@ def index(request, sitemaps,
|
|||
req_site = get_current_site(request)
|
||||
|
||||
sites = [] # all sections' sitemap URLs
|
||||
all_indexes_lastmod = True
|
||||
latest_lastmod = None
|
||||
for section, site in sitemaps.items():
|
||||
# For each section label, add links of all pages of its sitemap
|
||||
# (usually generated by the `sitemap` view).
|
||||
|
@ -36,13 +65,29 @@ def index(request, sitemaps,
|
|||
protocol = req_protocol if site.protocol is None else site.protocol
|
||||
sitemap_url = reverse(sitemap_url_name, kwargs={'section': section})
|
||||
absolute_url = '%s://%s%s' % (protocol, req_site.domain, sitemap_url)
|
||||
sites.append(absolute_url)
|
||||
site_lastmod = site.get_latest_lastmod()
|
||||
if all_indexes_lastmod:
|
||||
if site_lastmod is not None:
|
||||
latest_lastmod = _get_latest_lastmod(latest_lastmod, site_lastmod)
|
||||
else:
|
||||
all_indexes_lastmod = False
|
||||
sites.append(SitemapIndexItem(absolute_url, site_lastmod))
|
||||
# Add links to all pages of the sitemap.
|
||||
for page in range(2, site.paginator.num_pages + 1):
|
||||
sites.append('%s?p=%s' % (absolute_url, page))
|
||||
|
||||
return TemplateResponse(request, template_name, {'sitemaps': sites},
|
||||
content_type=content_type)
|
||||
sites.append(SitemapIndexItem('%s?p=%s' % (absolute_url, page), site_lastmod))
|
||||
# If lastmod is defined for all sites, set header so as
|
||||
# ConditionalGetMiddleware is able to send 304 NOT MODIFIED
|
||||
if all_indexes_lastmod and latest_lastmod:
|
||||
headers = {'Last-Modified': http_date(latest_lastmod.timestamp())}
|
||||
else:
|
||||
headers = None
|
||||
return TemplateResponse(
|
||||
request,
|
||||
template_name,
|
||||
{'sitemaps': sites},
|
||||
content_type=content_type,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
|
||||
@x_robots_tag
|
||||
|
@ -72,21 +117,23 @@ def sitemap(request, sitemaps, section=None,
|
|||
if all_sites_lastmod:
|
||||
site_lastmod = getattr(site, 'latest_lastmod', None)
|
||||
if site_lastmod is not None:
|
||||
if not isinstance(site_lastmod, datetime.datetime):
|
||||
site_lastmod = datetime.datetime.combine(site_lastmod, datetime.time.min)
|
||||
if timezone.is_naive(site_lastmod):
|
||||
site_lastmod = timezone.make_aware(site_lastmod, timezone.utc)
|
||||
lastmod = site_lastmod if lastmod is None else max(lastmod, site_lastmod)
|
||||
lastmod = _get_latest_lastmod(lastmod, site_lastmod)
|
||||
else:
|
||||
all_sites_lastmod = False
|
||||
except EmptyPage:
|
||||
raise Http404("Page %s empty" % page)
|
||||
except PageNotAnInteger:
|
||||
raise Http404("No page '%s'" % page)
|
||||
response = TemplateResponse(request, template_name, {'urlset': urls},
|
||||
content_type=content_type)
|
||||
if all_sites_lastmod and lastmod is not None:
|
||||
# if lastmod is defined for all sites, set header so as
|
||||
# ConditionalGetMiddleware is able to send 304 NOT MODIFIED
|
||||
response.headers['Last-Modified'] = http_date(lastmod.timestamp())
|
||||
return response
|
||||
# If lastmod is defined for all sites, set header so as
|
||||
# ConditionalGetMiddleware is able to send 304 NOT MODIFIED
|
||||
if all_sites_lastmod:
|
||||
headers = {'Last-Modified': http_date(lastmod.timestamp())} if lastmod else None
|
||||
else:
|
||||
headers = None
|
||||
return TemplateResponse(
|
||||
request,
|
||||
template_name,
|
||||
{'urlset': urls},
|
||||
content_type=content_type,
|
||||
headers=headers,
|
||||
)
|
||||
|
|
|
@ -65,6 +65,8 @@ details on these changes.
|
|||
See the :ref:`Django 4.1 release notes <deprecated-features-4.1>` for more
|
||||
details on these changes.
|
||||
|
||||
* The ``SitemapIndexItem.__str__()`` method will be removed.
|
||||
|
||||
.. _deprecation-removed-in-4.1:
|
||||
|
||||
4.1
|
||||
|
|
|
@ -294,6 +294,23 @@ Note:
|
|||
fallback entry with a value of :setting:`LANGUAGE_CODE`. The default is
|
||||
``False``.
|
||||
|
||||
.. method:: Sitemap.get_latest_lastmod()
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
**Optional.** A method that returns the latest value returned by
|
||||
:attr:`~Sitemap.lastmod`. This function is used to add the ``lastmod``
|
||||
attribute to :ref:`Sitemap index context
|
||||
variables<sitemap-index-context-variables>`.
|
||||
|
||||
By default :meth:`~Sitemap.get_latest_lastmod` returns:
|
||||
|
||||
* If :attr:`~Sitemap.lastmod` is an attribute:
|
||||
:attr:`~Sitemap.lastmod`.
|
||||
* If :attr:`~Sitemap.lastmod` is a method:
|
||||
The latest ``lastmod`` returned by calling the method with all
|
||||
items returned by :meth:`Sitemap.items`.
|
||||
|
||||
Shortcuts
|
||||
=========
|
||||
|
||||
|
@ -306,7 +323,8 @@ The sitemap framework provides a convenience class for a common case:
|
|||
a ``queryset`` entry. This queryset will be used to generate the items
|
||||
of the sitemap. It may also have a ``date_field`` entry that
|
||||
specifies a date field for objects retrieved from the ``queryset``.
|
||||
This will be used for the :attr:`~Sitemap.lastmod` attribute in the
|
||||
This will be used for the :attr:`~Sitemap.lastmod` attribute and
|
||||
:meth:`~Sitemap.get_latest_lastmod` methods in the in the
|
||||
generated sitemap.
|
||||
|
||||
The :attr:`~Sitemap.priority`, :attr:`~Sitemap.changefreq`,
|
||||
|
@ -413,6 +431,10 @@ both :file:`sitemap-flatpages.xml` and :file:`sitemap-blog.xml`. The
|
|||
:class:`~django.contrib.sitemaps.Sitemap` classes and the ``sitemaps``
|
||||
dict don't change at all.
|
||||
|
||||
If all sitemaps have a ``lastmod`` returned by
|
||||
:meth:`Sitemap.get_latest_lastmod` the sitemap index will have a
|
||||
``Last-Modified`` header equal to the latest ``lastmod``.
|
||||
|
||||
You should create an index file if one of your sitemaps has more than 50,000
|
||||
URLs. In this case, Django will automatically paginate the sitemap, and the
|
||||
index will reflect that.
|
||||
|
@ -433,6 +455,9 @@ with a caching decorator -- you must name your sitemap view and pass
|
|||
{'sitemaps': sitemaps}, name='sitemaps'),
|
||||
]
|
||||
|
||||
.. versionchanged:: 4.1
|
||||
|
||||
Use of the ``Last-Modified`` header was added.
|
||||
|
||||
Template customization
|
||||
======================
|
||||
|
@ -468,10 +493,23 @@ When customizing the templates for the
|
|||
:func:`~django.contrib.sitemaps.views.sitemap` views, you can rely on the
|
||||
following context variables.
|
||||
|
||||
.. _sitemap-index-context-variables:
|
||||
|
||||
Index
|
||||
-----
|
||||
|
||||
The variable ``sitemaps`` is a list of absolute URLs to each of the sitemaps.
|
||||
The variable ``sitemaps`` is a list of objects containing the ``location`` and
|
||||
``lastmod`` attribute for each of the sitemaps. Each URL exposes the following
|
||||
attributes:
|
||||
|
||||
- ``location``: The location (url & page) of the sitemap.
|
||||
- ``lastmod``: Populated by the :meth:`~Sitemap.get_latest_lastmod`
|
||||
method for each sitemap.
|
||||
|
||||
.. versionchanged:: 4.1
|
||||
|
||||
The context was changed to a list of objects with ``location`` and optional
|
||||
``lastmod`` attributes.
|
||||
|
||||
Sitemap
|
||||
-------
|
||||
|
|
|
@ -88,7 +88,11 @@ Minor features
|
|||
:mod:`django.contrib.sitemaps`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ...
|
||||
* The default sitemap index template ``<sitemapindex>`` now includes the
|
||||
``<lastmod>`` timestamp where available, through the new
|
||||
:meth:`~django.contrib.sitemaps.Sitemap.get_latest_lastmod` method. Custom
|
||||
sitemap index templates should be updated for the adjusted :ref:`context
|
||||
variables <sitemap-index-context-variables>`.
|
||||
|
||||
:mod:`django.contrib.sites`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -253,7 +257,10 @@ Features deprecated in 4.1
|
|||
Miscellaneous
|
||||
-------------
|
||||
|
||||
* ...
|
||||
* The context for sitemap index templates of a flat list of URLs is deprecated.
|
||||
Custom sitemap index templates should be updated for the adjusted
|
||||
:ref:`context variables <sitemap-index-context-variables>`, expecting a list
|
||||
of objects with ``location`` and optional ``lastmod`` attributes.
|
||||
|
||||
Features removed in 4.1
|
||||
=======================
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This is a customised template -->
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{% spaceless %}
|
||||
{% for site in sitemaps %}
|
||||
<sitemap>
|
||||
<loc>{{ site.location }}</loc>
|
||||
{% if site.last_mod %}
|
||||
<lastmod>{{ site.last_mod|date:"c" }}</lastmod>
|
||||
{% endif %}
|
||||
</sitemap>
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
</sitemapindex>
|
|
@ -85,3 +85,12 @@ class GenericViewsSitemapTests(SitemapTestsBase):
|
|||
)
|
||||
with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
|
||||
sitemap.get_protocol()
|
||||
|
||||
def test_generic_sitemap_index(self):
|
||||
TestModel.objects.update(lastmod=datetime(2013, 3, 13, 10, 0, 0))
|
||||
response = self.client.get('/generic-lastmod/index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>http://example.com/simple/sitemap-generic.xml</loc><lastmod>2013-03-13T10:00:00</lastmod></sitemap>
|
||||
</sitemapindex>"""
|
||||
self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
|
||||
|
|
|
@ -24,9 +24,9 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/simple/index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""" % self.base_url
|
||||
""" % (self.base_url, date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
def test_sitemap_not_callable(self):
|
||||
|
@ -34,9 +34,9 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/simple-not-callable/index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""" % self.base_url
|
||||
""" % (self.base_url, date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
def test_paged_sitemap(self):
|
||||
|
@ -44,24 +44,24 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/simple-paged/index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>{0}/simple/sitemap-simple.xml</loc></sitemap><sitemap><loc>{0}/simple/sitemap-simple.xml?p=2</loc></sitemap>
|
||||
<sitemap><loc>{0}/simple/sitemap-simple.xml</loc><lastmod>{1}</lastmod></sitemap><sitemap><loc>{0}/simple/sitemap-simple.xml?p=2</loc><lastmod>{1}</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""".format(self.base_url)
|
||||
""".format(self.base_url, date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
@override_settings(TEMPLATES=[{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
|
||||
}])
|
||||
def test_simple_sitemap_custom_index(self):
|
||||
def test_simple_sitemap_custom_lastmod_index(self):
|
||||
"A simple sitemap index can be rendered with a custom template"
|
||||
response = self.client.get('/simple/custom-index.xml')
|
||||
response = self.client.get('/simple/custom-lastmod-index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This is a customised template -->
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""" % self.base_url
|
||||
""" % (self.base_url, date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
def test_simple_sitemap_section(self):
|
||||
|
@ -176,7 +176,30 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/lastmod-sitemaps/descending.xml')
|
||||
self.assertEqual(response.headers['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT')
|
||||
|
||||
@override_settings(USE_I18N=True)
|
||||
def test_sitemap_get_latest_lastmod_none(self):
|
||||
"""
|
||||
sitemapindex.lastmod is ommitted when Sitemap.lastmod is
|
||||
callable and Sitemap.get_latest_lastmod is not implemented
|
||||
"""
|
||||
response = self.client.get('/lastmod/get-latest-lastmod-none-sitemap.xml')
|
||||
self.assertNotContains(response, '<lastmod>')
|
||||
|
||||
def test_sitemap_get_latest_lastmod(self):
|
||||
"""
|
||||
sitemapindex.lastmod is included when Sitemap.lastmod is
|
||||
attribute and Sitemap.get_latest_lastmod is implemented
|
||||
"""
|
||||
response = self.client.get('/lastmod/get-latest-lastmod-sitemap.xml')
|
||||
self.assertContains(response, '<lastmod>2013-03-13T10:00:00</lastmod>')
|
||||
|
||||
def test_sitemap_latest_lastmod_timezone(self):
|
||||
"""
|
||||
lastmod datestamp shows timezones if Sitemap.get_latest_lastmod
|
||||
returns an aware datetime.
|
||||
"""
|
||||
response = self.client.get('/lastmod/latest-lastmod-timezone-sitemap.xml')
|
||||
self.assertContains(response, '<lastmod>2013-03-13T10:00:00-05:00</lastmod>')
|
||||
|
||||
def test_localized_priority(self):
|
||||
"""The priority value should not be localized."""
|
||||
with translation.override('fr'):
|
||||
|
@ -240,9 +263,9 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/cached/index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/cached/sitemap-simple.xml</loc></sitemap>
|
||||
<sitemap><loc>%s/cached/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""" % self.base_url
|
||||
""" % (self.base_url, date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
def test_x_robots_sitemap(self):
|
||||
|
@ -356,7 +379,7 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
def test_callable_sitemod_partial(self):
|
||||
"""
|
||||
Not all items have `lastmod`. Therefore the `Last-Modified` header
|
||||
is not set by the detail sitemap view.
|
||||
is not set by the detail or index sitemap view.
|
||||
"""
|
||||
index_response = self.client.get('/callable-lastmod-partial/index.xml')
|
||||
sitemap_response = self.client.get('/callable-lastmod-partial/sitemap.xml')
|
||||
|
@ -378,16 +401,15 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
def test_callable_sitemod_full(self):
|
||||
"""
|
||||
All items in the sitemap have `lastmod`. The `Last-Modified` header
|
||||
is set for the detail sitemap view. The index view does not (currently)
|
||||
set the `Last-Modified` header.
|
||||
is set for the detail and index sitemap view.
|
||||
"""
|
||||
index_response = self.client.get('/callable-lastmod-full/index.xml')
|
||||
sitemap_response = self.client.get('/callable-lastmod-full/sitemap.xml')
|
||||
self.assertNotIn('Last-Modified', index_response)
|
||||
self.assertEqual(index_response.headers['Last-Modified'], 'Thu, 13 Mar 2014 10:00:00 GMT')
|
||||
self.assertEqual(sitemap_response.headers['Last-Modified'], 'Thu, 13 Mar 2014 10:00:00 GMT')
|
||||
expected_content_index = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>http://example.com/simple/sitemap-callable-lastmod.xml</loc></sitemap>
|
||||
<sitemap><loc>http://example.com/simple/sitemap-callable-lastmod.xml</loc><lastmod>2014-03-13T10:00:00</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
"""
|
||||
expected_content_sitemap = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
@ -397,3 +419,31 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
"""
|
||||
self.assertXMLEqual(index_response.content.decode(), expected_content_index)
|
||||
self.assertXMLEqual(sitemap_response.content.decode(), expected_content_sitemap)
|
||||
|
||||
|
||||
# RemovedInDjango50Warning
|
||||
class DeprecatedTests(SitemapTestsBase):
|
||||
@override_settings(TEMPLATES=[{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
|
||||
}])
|
||||
def test_simple_sitemap_custom_index_warning(self):
|
||||
msg = 'Calling `__str__` on SitemapIndexItem is deprecated, use the `location` attribute instead.'
|
||||
with self.assertRaisesMessage(RemovedInDjango50Warning, msg):
|
||||
self.client.get('/simple/custom-index.xml')
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
@override_settings(TEMPLATES=[{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
|
||||
}])
|
||||
def test_simple_sitemap_custom_index(self):
|
||||
"A simple sitemap index can be rendered with a custom template"
|
||||
response = self.client.get('/simple/custom-index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This is a customised template -->
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
|
||||
</sitemapindex>
|
||||
""" % (self.base_url)
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
|
|
@ -14,9 +14,9 @@ class HTTPSSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/secure/index.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/secure/sitemap-simple.xml</loc></sitemap>
|
||||
<sitemap><loc>%s/secure/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""" % self.base_url
|
||||
""" % (self.base_url, date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
def test_secure_sitemap_section(self):
|
||||
|
@ -39,9 +39,9 @@ class HTTPSDetectionSitemapTests(SitemapTestsBase):
|
|||
response = self.client.get('/simple/index.xml', **self.extra)
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
|
||||
<sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
|
||||
</sitemapindex>
|
||||
""" % self.base_url.replace('http://', 'https://')
|
||||
""" % (self.base_url.replace('http://', 'https://'), date.today())
|
||||
self.assertXMLEqual(response.content.decode(), expected_content)
|
||||
|
||||
def test_sitemap_section_with_https_request(self):
|
||||
|
|
|
@ -14,13 +14,15 @@ class SimpleSitemap(Sitemap):
|
|||
changefreq = "never"
|
||||
priority = 0.5
|
||||
location = '/location/'
|
||||
lastmod = datetime.now()
|
||||
lastmod = date.today()
|
||||
|
||||
def items(self):
|
||||
return [object()]
|
||||
|
||||
|
||||
class SimplePagedSitemap(Sitemap):
|
||||
lastmod = date.today()
|
||||
|
||||
def items(self):
|
||||
return [object() for x in range(Sitemap.limit + 1)]
|
||||
|
||||
|
@ -110,6 +112,26 @@ class CallableLastmodFullSitemap(Sitemap):
|
|||
return obj.lastmod
|
||||
|
||||
|
||||
class GetLatestLastmodNoneSiteMap(Sitemap):
|
||||
changefreq = "never"
|
||||
priority = 0.5
|
||||
location = '/location/'
|
||||
|
||||
def items(self):
|
||||
return [object()]
|
||||
|
||||
def lastmod(self, obj):
|
||||
return datetime(2013, 3, 13, 10, 0, 0)
|
||||
|
||||
def get_latest_lastmod(self):
|
||||
return None
|
||||
|
||||
|
||||
class GetLatestLastmodSiteMap(SimpleSitemap):
|
||||
def get_latest_lastmod(self):
|
||||
return datetime(2013, 3, 13, 10, 0, 0)
|
||||
|
||||
|
||||
def testmodelview(request, id):
|
||||
return HttpResponse()
|
||||
|
||||
|
@ -180,6 +202,18 @@ generic_sitemaps = {
|
|||
'generic': GenericSitemap({'queryset': TestModel.objects.order_by('pk').all()}),
|
||||
}
|
||||
|
||||
get_latest_lastmod_none_sitemaps = {
|
||||
'get-latest-lastmod-none': GetLatestLastmodNoneSiteMap,
|
||||
}
|
||||
|
||||
get_latest_lastmod_sitemaps = {
|
||||
'get-latest-lastmod': GetLatestLastmodSiteMap,
|
||||
}
|
||||
|
||||
latest_lastmod_timezone_sitemaps = {
|
||||
'latest-lastmod-timezone': TimezoneSiteMap,
|
||||
}
|
||||
|
||||
generic_sitemaps_lastmod = {
|
||||
'generic': GenericSitemap({
|
||||
'queryset': TestModel.objects.order_by('pk').all(),
|
||||
|
@ -202,6 +236,10 @@ urlpatterns = [
|
|||
path(
|
||||
'simple/custom-index.xml', views.index,
|
||||
{'sitemaps': simple_sitemaps, 'template_name': 'custom_sitemap_index.xml'}),
|
||||
path(
|
||||
'simple/custom-lastmod-index.xml', views.index,
|
||||
{'sitemaps': simple_sitemaps, 'template_name': 'custom_sitemap_lastmod_index.xml'},
|
||||
),
|
||||
path(
|
||||
'simple/sitemap-<section>.xml', views.sitemap,
|
||||
{'sitemaps': simple_sitemaps},
|
||||
|
@ -266,6 +304,21 @@ urlpatterns = [
|
|||
'lastmod-sitemaps/descending.xml', views.sitemap,
|
||||
{'sitemaps': sitemaps_lastmod_descending},
|
||||
name='django.contrib.sitemaps.views.sitemap'),
|
||||
path(
|
||||
'lastmod/get-latest-lastmod-none-sitemap.xml', views.index,
|
||||
{'sitemaps': get_latest_lastmod_none_sitemaps},
|
||||
name='django.contrib.sitemaps.views.index',
|
||||
),
|
||||
path(
|
||||
'lastmod/get-latest-lastmod-sitemap.xml', views.index,
|
||||
{'sitemaps': get_latest_lastmod_sitemaps},
|
||||
name='django.contrib.sitemaps.views.index',
|
||||
),
|
||||
path(
|
||||
'lastmod/latest-lastmod-timezone-sitemap.xml', views.index,
|
||||
{'sitemaps': latest_lastmod_timezone_sitemaps},
|
||||
name='django.contrib.sitemaps.views.index',
|
||||
),
|
||||
path(
|
||||
'generic/sitemap.xml', views.sitemap,
|
||||
{'sitemaps': generic_sitemaps},
|
||||
|
@ -287,6 +340,11 @@ urlpatterns = [
|
|||
path('callable-lastmod-partial/sitemap.xml', views.sitemap, {'sitemaps': callable_lastmod_partial_sitemap}),
|
||||
path('callable-lastmod-full/index.xml', views.index, {'sitemaps': callable_lastmod_full_sitemap}),
|
||||
path('callable-lastmod-full/sitemap.xml', views.sitemap, {'sitemaps': callable_lastmod_full_sitemap}),
|
||||
path(
|
||||
'generic-lastmod/index.xml', views.index,
|
||||
{'sitemaps': generic_sitemaps_lastmod},
|
||||
name='django.contrib.sitemaps.views.index',
|
||||
),
|
||||
]
|
||||
|
||||
urlpatterns += i18n_patterns(
|
||||
|
|
Loading…
Reference in New Issue