Django sitemap change base url - django

I'm using https://docs.djangoproject.com/en/dev/ref/contrib/sitemaps/?from=olddocs .
I have a sitemap generated from api.mydomain.me for the domain: mydomain.com.
Can I, with django, specify a base url ?
Now with location() method return:
api.mydomain.me/page/3123
instead of
mydomain.com/page/3123
Is this possible?
Thanks.

Solved, I redefined my own get_urls.
It works:
class MySitemap(Sitemap):
changefreq = "never"
priority = 0.5
location = ""
def get_urls(self, site=None, **kwargs):
site = Site(domain='mydomain.com', name='mydomain.com')
return super(MySitemap, self).get_urls(site=site, **kwargs)
def items(self):
return MyObj.objects.all().order_by('pk')[:1000]
def lastmod(self, obj):
return obj.timestamp

I didn't know how to use Site in my code by using previous answers so I used below code:
class Site:
domain = 'my_site.com'
class MySitemap(Sitemap):
def get_urls(self, site=None, **kwargs):
site = Site()
return super(MySitemap, self).get_urls(site=site, **kwargs)

You can try somethings like that:
from django.contrib.sites.models import Site, SiteManager
def get_fake_site(self):
return Site(domain='mydomain.com', name='mydomain.com')
SiteManager.add_to_class('get_current', get_fake_site)
You should do it before constructing yous sitemap, and revert back to default after it.

And If you have multiples Sitemaps classes you can use a mixin approach.
An example for Django 1.5.1.
from django.contrib.sitemaps import Sitemap
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from yourapp.models import MyObj
class SiteMapDomainMixin(Sitemap):
def get_urls(self, page=1, site=None, protocol=None):
# give a check in https://github.com/django/django/blob/1.5.1/django/contrib/sitemaps/__init__.py
# There's also a "protocol" argument.
fake_site = Site(domain='mydomain.com', name='mydomain.com')
return super(SiteMapDomainMixin, self).get_urls(page, fake_site, protocol=None)
class MySitemap(SiteMapDomainMixin):
changefreq = "never"
priority = 0.5
def items(self):
return MyObj.objects.all().order_by('pk')[:1000]
def location(self, item):
return reverse('url_for_access_myobj', args=(item.slug,))
def lastmod(self, obj):
return obj.updated_at
class AnotherSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return ['url_1', 'url_2', 'url_3',]
def location(self, item):
return reverse(item)
The urls.py would be something like...
from sitemaps import MySitemap
from sitemaps import AnotherSitemap
from yourapp.views import SomeDetailMyObjView
admin.autodiscover()
sitemaps = {
'mysitemap': MySitemap,
'anothersitemap': AnotherSitemap,
}
urlpatterns = patterns('',
# other urls...
url(r'^accessing-myobj/(?P<myobj_slug>[-\w]+)$', SomeDetailMyObjView, name='url_for_access_myobj'),
(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
)

I use a patch by creating a custom template tag and use that function to replace the url:
https://www.fraydit.com/blogs/wagtail-sitemap-in-docker/

Pretty much all answers require you to use the Site framework which requires migrations to work and it be added to INSTALLED_APPS.
In my instance I only needed to show the correct URL because I use Docker and the base URL based on my docker config is app.
With that all you need to do is define a custom Site() class like I have done below and you are good to go.
class UserGuideSitemap(sitemaps.Sitemap):
priority = 1
changefreq = 'daily'
def get_urls(self, site=None, protocol=None, **kwargs):
# we need the url to be site.com
# SiteToShow() is just a custom class that gives the data needed
return super().get_urls(site=SiteToShow(), protocol='https', **kwargs)
def items(self):
# must import here due to url circular import with urls.py
from apps.user_guide.urls import urlpatterns as urls
# get names of urls from base urls
final_list = list()
for pattern in urls:
url_name = pattern.pattern.name
# there are some urls we do not want
if url_name not in ['']:
final_list.append(url_name)
return final_list
def location(self, item):
# return the URL for the sitemap
return reverse(item)
And here is very simple the SiteToShow class:
class SiteToShow:
domain = 'site.com'
name = 'site.com'
def __str__(self):
return self.domain

Related

How to create xml sitemap of all the Django project detail page urls

I'm trying to get all the urls for specific Django project detail pages for instance:
path('/mysite/speical/<pk>',views.DetalView.as_view(),name='detail')
/mysite/special/1390u20
/mysite/special/930nfdn
/mysite/special/29093nn
How do I print all these urls into an xml file in the shell? I'm not sure where to even start.
In your urls.py:
from django.contrib.sitemaps.views import sitemap
from .sitemap import SpeicalSitemap
sitemaps = {
yoursitemap: YourSitemap
}
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='sitemap'),
Create a file named sitemap.py inside your app:
from django.contrib.sitemaps import Sitemap
from yourapp.models import Yourmodel
class YourSiteMap(Sitemap):
changefreq = 'monthly'
priority = 0.9
def items(self):
return yourmodel.objects.all()
def lastmod(self, obj):
return obj.last_mod_date "Here your put a last mod date defined in your model if you have it."
To create the right route to your objects, you need to put a method in your model called "get_absolute_url", let's create:
from django.urls import reverse
class YourModel(models.Model):
def get_absolute_url(self):
return reverse('name_of_your_route', kwargs={'pk': self.pk})

Is it possible to add 2nd slug to URL path in Django?

I'm using Django version 2.1.
I want to create this type of URL Path in my Project:
www.example.com/bachelor/germany/university-of-frankfurt/corporate-finance
Is it possible to do it in Django?
Yes, say for example that you have a slug for an Author, and one for a Book, you can define it as:
# app/urls.py
from django.urls import path
from app.views import book_details
urlpatterns = [
path('book/<slug:author_slug>/<slug:book_slug>/', book_details),
]
Then the view looks like:
# app/views.py
from django.http import HttpResponse
def book_details(request, author_slug, book_slug):
# ...
return HttpResponse()
The view thus takes two extra parameters author_slug (the slug for the author), and book_slug (the slug for the book).
If you thus query for /book/shakespeare/romeo-and-juliet, then author_slug will contains 'shakespeare', and book_slug will contain 'romeo-and-juliet'.
We can for example look up that specific book with:
def book_details(request, author_slug, book_slug):
my_book = Book.objects.get(author__slug=author_slug, slug=book_slug)
return HttpResponse()
Or in a DetailView, by overriding the get_object(..) method [Django-doc]:
class BookDetailView(DetailView):
model = Book
def get_object(self, queryset=None):
super(BookDetailView, self).get_object(queryset=queryset)
return qs.get(
author__slug=self.kwargs['author_slug'],
slug=self.kwargs['book_slug']
)
or for all views (including the DetailView), by overriding the get_queryset method:
class BookDetailView(DetailView):
model = Book
def get_queryset(self):
qs = super(BookDetailView, self).get_queryset()
return qs.filter(
author__slug=self.kwargs['author_slug'],
slug=self.kwargs['book_slug']
)

Django : how to include a view.py file inside a view from another app?

I've made a "paginator" app, that add such SEO optimisation for all my pages.
So I need to pass all visible page url through paginator.view
But, I want to keep my apps as structured as possible.
For an example here is a view for my gallery app:
gallery.views
from django.shortcuts import render
from gallery.models import GalleryItem
def home(request):
img_to_display = GalleryItem.objects.filter(published=True
).order_by('-date')
return render(request, 'gallery/all.html', locals())
...
Now I'm doing like that form my view in paginator :
My current paginator.views
from django.shortcuts import render, get_object_or_404, redirect
from gallery.models import GalleryItem
from paginator.models import Page
import gallery
def custom_page(request, url):
current_page = url
# just for the gallery page :
if url == 'gallery':
img_to_display = GalleryItem.objects.filter(published=True
).order_by('-date')
# for all my page
page_to_load = get_object_or_404(Page, name=url)
template_to_load = "paginator/" + page_to_load.template_name
return render(request, template_to_load, locals())
So I copy/paste my view and all dependencies, but that is really ugly, and not at all DRY, worth it's not maintainable. I try something like that but it doesn't work :
paginator.views : option1
from django.shortcuts import render
import gallery
def custom_page(request, url):
if url == 'gallery':
gallery.views.home(request)
if url == 'anotherpage':
anotherapp.views.home(request)
...
Or something like that :
paginator.views : option 2
from django.shortcuts import render
def custom_page(request, url):
if url == 'gallery':
include("gallery.views.py")
if url == 'anotherpage':
include("anotherapp.views.py")
...
Note: I prefer the last style option because it minimize the import at the start of the paginator.views file.
Thanks a lot for helping ! :)
If you need a mechanism which is executed before the request is dispatched to a view, I would recommend using a middleware class. You can read more about it in the Django docs.
Another option is to use class based views to create a SEOView which can be inherited by every custom page view of yours. Some example of how it could look like:
from django.views.generic.base import View
class MySeoView(View):
def dispatch(self, request, *args, **kwargs):
# some logic for SEO
return super().dispatch(request, *args, **kwargs)
class CustomView1(MySeoView):
def get(self, request, *args, **kwargs):
# do normal stuff for this page
return HttpResponse(...)
def post(self, request, *args, **kwargs):
# maybe some other logic for posts
return HttpResponse(...)
To come back to your own options:
If you want to make #1 work, I guess you have to return the result of the view:
...
if url == 'someUrl':
return gallery.views.home(request)
...
Firstly I want to thanks Gocht to have sended me on a good way. So that's what I've done :
paginator.views
def custom_page(request, url):
"""
Vue d'une page statique.
"""
if url == 'gallery':
import gallery
gal = gallery.views.render_gallery(request)
gal.render()
gallery_html = gal.rendered_content
if url == 'xxx':
...
page_to_load = get_object_or_404(Page, url=url)
template_to_load = "paginator/" + page_to_load.template_name
return render(request, template_to_load, locals())
gallery.views
from django.template.response import TemplateResponse
from gallery.models import GalleryItem
def render_gallery(request):
img_to_display = GalleryItem.objects.filter(published=True
).order_by('-date')
return TemplateResponse(request, 'gallery/gallery.html', locals())
But yes, now, I understand that something like simP will be more clean. Thanks !

Setting up Mimetype when using TemplateView in Django

Does anyboy know how do I set the desired mimetype when using TemplateView, as in:
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html')),
In this case, I want to set the mimtype as "text/plain"
For Django >= 1.5
TemplateView accepts a content_type argument.
Coping example from #Meilo
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html', content_type='text/plain')),
For Django < 1.5
I think that just calling TemplateView.as_view() is not posible but maybe i missed it (from the source),
but you can do your own class
class TextTemplateView(TemplateView):
def render_to_response(self, context, **response_kwargs):
response_kwargs['content_type'] = 'text/plain'
return super(TemplateView, self).render_to_response(context, **response_kwargs)
You can take a look to:
django.template.response => TemplateResponse
django.views.generic.base => TemplateView
And if you need something more dynamic:
from django.utils.decorators import classonlymethod
class ContentTypeTemplateView(TemplateView):
#classonlymethod
def as_view(cls, content_type='text/plain', **initargs):
setattr(cls, 'content_type', content_type)
return super(ContentTypeTemplateView, cls).as_view(**initargs)
def render_to_response(self, context, **response_kwargs):
response_kwargs['content_type'] = self.content_type
return super(ContentTypeTemplateView, self).render_to_response(context, **response_kwargs)
urlpatterns = patterns('',
url(r'^$', ContentTypeTemplateView.as_view(content_type='text/plain',
template_name='staticpages/test.html'),
name='index'),
)
Using a Mixin
from django.core.exceptions import ImproperlyConfigured
class ContentTypeMixin(object):
content_type = None
def render_to_response(self, context, **response_kwargs):
if not self.content_type:
raise ImproperlyConfigured(
"MimeTypeMixin rquires a definition of content_type")
response_kwargs['content_type'] = self.content_type
return super(ContentTypeMixin, self).render_to_response(context,
**response_kwargs)
class MyTxtView(ContentTypeMixin, TemplateView):
content_type = 'text/plain'
....
In Django 1.5 the content_type argument in the TemplateView adds the same functionality that was in the function-based view before. That makes it easier to set the desired mimetype:
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html', content_type='text/plain')),
I know that you ask for setting a content type with TemplateView, but I will give you different answer which I think that will be more clean and can be used in Django versions lower than 1.5.
url(r'^robots\.txt$', 'django.shortcuts.render', kwargs={
'template_name': 'robots.txt',
'content_type': 'text/plain',
})
With this approach you don't need to import anything or to subclass TemplateView and make ugly overwrites of some methods. You can simply use the old technique with function based views.
If you don't want to extend the TemplateView, you can extend the TemplateResponse to set the mimetype:
from django.template.response import TemplateResponse
class TextResponse(TemplateResponse):
def __init__(self, *args, **kwargs):
kwargs['mimetype'] = 'text/plain'
return super(TextResponse, self).__init__(*args, **kwargs)
Then pass it as the template_class to the TemplateView
urlpatterns = patterns('django.views.generic.simple',
(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', response_class=TextResponse)),
)
The best way to do it is to subclass TemplateView and override the render_to_response() method:
class StaticPagesTest(TemplateView):
template_name = 'staticpages/test.html'
def render_to_response(self, context, **kwargs):
return super(StaticPagesTest, self).render_to_response(context,
mimetype='text/plain', **kwargs)
I know this is solved for 1.5, but the application I am working in is 1.4.
I had an issue with two url patterns in a row using sacabuche's answer:
url(r'^playlist1\.m3u$', ContentTypeTemplateView.as_view(template_name='playlist1.m3u', content_type='audio/x-mpegurl')),
url(r'^playlist2\.pls$', ContentTypeTemplateView.as_view(template_name='playlist2.pls', content_type='audio/x-scpls'))
I found playlist1 would return the correct template, but with playlist2's content type! Playlist2 was ok. Adding a 3rd url pattern with a content-type of 'foo' would cause all playlist views to return with content-type 'foo'.
I ended up using the render method instead with good results:
urls:
url(r'^playlist1\.m3u$', 'content_type_to_template', {'template_name': 'playlist1.m3u', 'content_type': 'audio/x-mpegurl'}),
url(r'^playlist2\.pls$', 'content_type_to_template', {'template_name': 'playlist2.pls', 'content_type':'audio/x-scpls'})
views:
from django.shortcuts import render
def content_type_to_template(request, template_name='', content_type='text/plain'):
return render(request, template_name, content_type=content_type)
A simple example of how change content type of a TemplateView:
#views.py
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "home/index.html"
content_type = 'text/html'
# urls.py
url(r'^home/$', HomeView.as_view(), name='home_page'),
url(r'^test/(?P<template>.*)', lambda request, template: TemplateView.as_view(template_name=template)(request)),

Django Google News Sitemap.xml

I'm trying to build out a Google News Sitemap in addition to a normal sitemap.xml to avoid having to put additional numerical characters in my URLs.
I've build out the sitemap.xml using Django's contrib system and it works great, but I'm having trouble passing the context to the (unverified) patch updating the framework to gererate a news_sitemap.xml.
This is the patch I've integrated: http://code.djangoproject.com/ticket/10907, but the context isn't passing. I think the issue is with the format I'm using to build the object in my views.py.
The code I have running:
views.py
from django.template import RequestContext
from django.shortcuts import render_to_response
from basic.blog.models import Post
from pages.models import Page
from datetime import date, datetime
from django.contrib.sitemaps import Sitemap, NewsSitemap
'''Builds the sitemap.xml section for all news posts.'''
class PostSitemap(Sitemap):
changefreq = "daily"
priority = 0.3
def items(self):
return Post.objects.published()
def lastmod(self, obj):
return obj.modified
'''Builds the sitemap.xml section for all main pages.'''
class PageSitemap(Sitemap):
changefreq = "daily"
priority = 0.8
def items(self):
return Page.objects.filter(status=1)
def lastmod(self, obj):
return obj.last_modification_date
'''Builds the news_sitemap.xml from blog module.'''
class SiteappNews(Sitemap):
def items(self):
return Post.objects.published()
def publication_date(self, obj):
return obj.publish
urls.py
from django.conf.urls.defaults import *
from django.contrib.sitemaps import Sitemap, FlatPageSitemap, NewsSitemap
from siteapp.views import homepage, news_homepage, qc_contact, PostSitemap, PageSitemap, SiteappNews
from basic.blog.feeds import *
from basic.blog.models import Post
from pages.models import Page
''' Enables Django Admin.'''
from django.contrib import admin
admin.autodiscover()
'''Add Feeds functionality.'''
feeds = {
'latest': BlogPostsFeed,
}
'''http://docs.djangoproject.com/en/1.0/ref/contrib/sitemaps/'''
sitemaps = {
'pagesitemap': PageSitemap,
'postsitemap': PostSitemap,
'flatpages': FlatPageSitemap,
}
news_sitemaps = {
'newssitemap': NewsSitemap,
}
urlpatterns = patterns('',
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
(r'^admin/(.*)', admin.site.root),
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
(r'^news_sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': news_sitemaps, 'template': 'news_sitemap.xml'}),
The template outputs just wrapper. I'm missing something obvious i think, though there might be an issue with the patch applied. Here's the relevant code for that:
Within sitemaps contrib init.py
class NewsSitemap(Sitemap):
# This limit is defined by Google. See the index documentation at
# http://www.google.com/support/webmasters/bin/answer.py?hl=en&answer=74288
limit = 1000
def get_url_info(self, item, current_site):
url_info = super(NewsSitemap, self).get_url_info(item, current_site)
url_info.update({
'publication_date': self._get('publication_date', item, None),
'keywords': self._get('keywords', item, None),
})
return url_info
Sorted this one out myself after a little digging.
Changed the urls.py lines to:
news_sitemaps = {
'newssitemap': SiteappNews,
}
And changed the code in views.py to build out the relevant Google News fields from the custom module.
Yours will vary future reader (hello!), but it will be something like:
class SiteappNews(Sitemap):
def items(self):
return Post.objects.published()
def publication_date(self, obj):
return obj.publish
def keywords(self, obj):
return obj.tags
check your SQL fields for your models to sub in the correct data for 'publish', 'tags', etc.