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.
Related
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})
In my models.py file i made a variable called hero_name. For instance, if the hero name is Bart, I want the url to be ./heroes/Bart
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<str:hero_name>/', views.heropage, name='heropage'),
]
views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Heroes
def index(request):
return render(request, 'heroes/index.html')
def heropage(request, hero_name):
get_object_or_404(Heroes, pk=hero_name)
return render(request, 'heroes/heropage.html')
models.py
from django.db import models
class Heroes(models.Model):
hero_name = models.CharField(max_length=200)
I think that you need to replace your implementation of get_object_or_404 with one that gets the object based on hero_name and not pk, since in your model, hero_name is not your primary key.
I hope this helps.
It's not clear exactly what you are asking. But you certainly need to use the relevant field to query the object, and pass the result to the template:
hero = get_object_or_404(Heroes, hero_name=hero_name)
return render(request, 'heroes/heropage.html', {"hero": hero})
Set the field you want to primary_key.
class Heroes(models.Model):
hero_name = models.CharField(primary_key=True)
I created a very simple example code using Django, but cannot get model value to be displayed on my page:
----------------------------- home/models.py
from django.db import models
class Home(models.Model):
msg = models.CharField(max_length=100)
#classmethod
def create(cls, msg):
home = cls(msg=msg)
# do something with the book
return home
home = Home.create("Hello World!")
------------------------------------home/views.py
from django.views.generic import TemplateView
from project.models import Home
class IndexView(TemplateView):
model = Home
template_name = 'home/index.html'
------------------------------------------ templates/home/index.html
{{ home.msg }}
this is a test page. I am expecting to see this....
------------------------------------------- urls.py
from django.conf.urls.defaults import patterns, include, url
from django.contrib import admin
from django.views.generic import TemplateView
admin.autodiscover()
urlpatterns = patterns('',
# Home pagetentacl.urls
url(r'^$', TemplateView.as_view(template_name='home/index.html')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)
-------------------------------------- result page on browser:
this is a test page. I am expecting to see this....
I don't want to have DB access for my example. I want my model returns "hello world" string. home.msg on index.html doesn't return anything. What is missing here?
You're not giving the template an instance of Home. You need to create one and pass it to the template as a Context, in the form {'msg': msg}.
EDIT: Adding some code
First of all, you should create your instance of home in your view. I've never used TemplateViews, so I'm going to use a regular view method instead.
def IndexView(request):
home=Home.create("Hello World!")
return render(request, 'index.html', {'home': home},)
As #Daniel rightly points out, you're not giving your template an instance of Home to work with.
If you want to use class-based views, subclass TemplateView and override get_context_data():
class IndexView(TemplateView):
template_name = "home/index.html"
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context["home"] = Home.create("Hello World!")
return context
And make sure your urls.py is using IndexView.as_view() - your version above is just referencing the generic TemplateView.
The fact that you added a model field to your subclass of TemplateView makes me think you're confusing it with DetailView. See the documentation for the difference.
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
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)),