Add image field to XML sitemap in Django
Asked Answered
J

3

5

Google recognizes an <image> tag for XML sitemaps (http://support.google.com/webmasters/bin/answer.py?hl=en&answer=178636), and I would like to include an image attribute into my sitemaps.

So, something like this is needed to get the cover_image and then loaded into the xml file:

for article in articles:
        print article.cover_image

I'd also need article.title loaded too for the <image:title> tag.

I've Googled and searched Stack Overflow for an example, but I surprisingly couldn't find any, so help appreciated.

My files so far:

## sitemaps.py ##
from django.contrib.sitemaps import Sitemap
from myproject.article.models import Article

class ArticleSitemap(Sitemap):
    priority = 1.0

    def items(self):
        return  Article.objects.order_by('-id').order_by('-pub_date')

    def lastmod(self, obj):
        return obj.pub_date

## urls.py ##
from myproject.sitemaps import ArticleSitemap

sitemaps = {
    "article": ArticleSitemap
}
urlpatterns += patterns ('',
    (r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
Jewel answered 28/4, 2012 at 13:1 Comment(0)
G
9

It can be done by

  1. Redefining get_urls method in your inherited class (add image information)
  2. Change default template to the one which required to render image info

here is the code:

  1. Add these methods in your class - these methods are almost same as defined in django's sitemap framework, but differ by the way it prepares data that need to be render in the template

    class MySItemapClass(Sitemap):
      def item():
         .........        
    
      def __get(self, name, obj, default=None):
        try:
          attr = getattr(self, name)
        except AttributeError:
          return default
        if callable(attr):
          return attr(obj)
        return attr
    
      def get_urls(self, page=1, site=None, protocol=None):
        # Determine protocol
        if self.protocol is not None:
          protocol = self.protocol
        if protocol is None:
          protocol = 'http'
    
        # Determine domain
        if site is None:
          if Site._meta.installed:
              try:
                  site = Site.objects.get_current()
              except Site.DoesNotExist:
                  pass
          if site is None:
              raise ImproperlyConfigured("To use sitemaps, either enable the sites framework or pass a Site/RequestSite object in your view.")
        domain = site.domain
    
        urls = []
        for item in self.paginator.page(page).object_list:
          loc = "%s://%s%s" % (protocol, domain, self.__get('location', item))
          priority = self.__get('priority', item, None)
          url_info = {
              'item':       item,
              'location':   loc,
              'lastmod':    self.__get('lastmod', item, None),
              'changefreq': self.__get('changefreq', item, None),
              'priority':   str(priority is not None and priority or ''),
              'images'   :   get_image(protocol, domain,item), # changed here
          }
          urls.append(url_info)
        return urls
    

define get_image method as you please

  1. define your custom template. mine look like this - note change in defining namespaces("urlset")

    <?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
    {% spaceless %}
    {% for url in urlset %}
      <url>
        <loc>{{ url.location }}</loc>
        {% if url.images %}
            {% for image in url.images %}
                <image:image>
                    <image:loc>{{image}}</image:loc>
                </image:image>
            {% endfor %}
        {% endif %}
        {% 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 %}
    {% endspaceless %}
    </urlset>
    
  2. Override to use new template rather than the default template

    url(r'^sitemap-(?P<section>.+)\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps,'template_name': 'seo/sitemap.xml'}),
    
Golfer answered 20/11, 2012 at 11:33 Comment(1)
Please see my answer below for a simpler versionEthelethelbert
T
0

You can add some images before rendering the template and display them in template. I think, it's better to do separate application for it (inherited from django.contrib.sitemaps).

Thigmotaxis answered 29/4, 2012 at 5:45 Comment(0)
E
0

If you use your own template then it has access to your model instance at url.item when iterating urls, so you can just define anything you want on it: https://docs.djangoproject.com/en/3.2/ref/contrib/sitemaps/#id1

In the example below, the sitemap contains two different models, one which defines sitemap_image and one which doesn't.

main urls.py

from django.contrib.sitemaps import GenericSitemap, views

sitemaps = {
    'artists': GenericSitemap(
        {'queryset': Artist.objects.filter(display=True)}),
    'paintings': GenericSitemap(
        {'queryset': Painting.objects.filter(display=True).prefetch_related("images")},),
}

urlpatterns = [
    # your other urls...
    path("sitemap.xml", views.sitemap, {"sitemaps": sitemaps, "template_name": "image_sitemap.xml", }),
]

templates/image_sitemap.xml

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
    {% spaceless %}
        {% for url in urlset %}
            <url>
                <loc>{{ url.location }}</loc>
                {% if url.item.sitemap_image %}
                    <image:image>
                        <image:loc>{{ request.scheme }}://{{ request.get_host }}{{ url.item.sitemap_image }}</image:loc>
                    </image:image>
                {% endif %}
                {% 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 %}
    {% endspaceless %}
</urlset>

models.py


class Painting(models.Model):
    # your fields
    
    @property
    def sitemap_image(self):
        return self.images.first().image.url if self.images.first() else None
Ethelethelbert answered 18/1, 2022 at 10:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.