I am trying to create a sitemap in Django but I am getting an error
'Post' object has no attribute 'get_absolute_url'
Here is my anotherfile/sitemap.py
from django.contrib.sitemaps import Sitemap
from somefile.models import Post
class site_map(Sitemap):
changefreq = "daily"
priority = 0.8
def items(self):
return Post.objects.all()
def lastmod(self, obj):
return obj.time_stamp
and here is my somefile/models.py
class Post(models.Model):
number=models.AutoField(primary_key=True)
slug=models.CharField(max_length=130)
time_stamp=models.DateTimeField(blank=True)
def __str__(self):
return self.number
In order to determine the paths in the sitemap, you need to implement a get_absolute_url for the model(s) for which you make a sitemap, so:
from django.urls import reverse
class Post(models.Model):
number=models.AutoField(primary_key=True)
slug=models.CharField(max_length=130)
time_stamp=models.DateTimeField(blank=True)
def get_absolute_url(self):
return reverse('name-of-some-view', kwargs={'para': 'meters'})
def __str__(self):
return self.number
With reverse(…) [Django-doc] you can calculate the URL based on the name of the view, and parameters that the corresponding path needs.
I've never used it, but it simply sounds like your Post model doesn't have a get_absolute_url method.
http://docs.djangoproject.com/en/dev/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.location
If location isn't provided, the framework will call the
get_absolute_url() method on each object as returned by items().
Model.get_absolute_url()
Define a get_absolute_url() method to tell Django how to calculate the canonical URL for an object. To callers, this method should appear to return a string that can be used to refer to the object over HTTP.
For example:
def get_absolute_url(self):
return "/post/%i/" % self.id
Related
I am trying to delete a record after displaying it with DetailView and redirecting to the list page again.
view.py
...
class DailyRecordDeleteConformationView(RequiredBasicDetailsAndContextMixin, DetailView):
model = DailyRecord
obj_not_found_redirect = reverse_lazy('business:add_daily_records')
template_name = 'business/daily_records_detail.html'
context_object_name = 'record'
def get_object(self):
detail = self.model.custom_obj.get_single(self)
return detail
class DailyRecordDeleteView(RequiredBasicDetailsMixin, RedirectView):
pattern_name = 'business:daily_records'
def get_redirect_url(self, *args, **kwargs):
# Delete the record
return super().get_redirect_url(*args, **kwargs)
...
urls.py
...
path('daily-records/', DailyRecordView.as_view(), name='daily_records'),
path('daily-records/<int:pk>/', DailyRecordDeleteConformationView.as_view(), name='daily_record_detail'),
path('daily-records/<int:pk>/delete/', DailyRecordDeleteView.as_view(), name='daily_record_delete'),
...
Here I am getting this below error when click on the delete button on detail view
NoReverseMatch at /business/daily-records/7/delete/
Reverse for 'daily_records' with keyword arguments '{'pk': 7}' not found. 1 pattern(s) tried: ['business/daily\\-records/\\Z']
I am new to class based views, and still not able to figure out how to redirect to url that does not have the pk parameter.
I tryed to find any variable for RedirectView or something that can skip the pk of current url while redirecting to another url.
Thanks.
When you invoke get_redirect_url with **kwargs, your pk is in kwargs and in RedirectView, Django tries to get your url with that kwargs.
So if you don't want to pass the pk, just don't pass kawrgs:
def get_redirect_url(self, *args, **kwargs):
return super().get_redirect_url()
You can specify url instead of pattern_name, so:
from django.urls import reverse_lazy
class DailyRecordDeleteView(RequiredBasicDetailsMixin, RedirectView):
urls = reverse_lazy('business:daily_records')
# no get_redirect_url
Your get_redirect_url is also not supposed to delete records: the HTTP protocol specifies that GET and OPTION should be "safe" methods, that means they should not have side-effects.
You thus should delete records with a POST/DELETE request. You can work with a small FormView that will tackle this in case the (empty) form is valid, so by overriding the form_valid method.
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']
)
I'm trying to do cache_page with class based views (TemplateView) and i'm not able to. I followed instructions here:
Django--URL Caching Failing for Class Based Views
as well as here:
https://github.com/msgre/hazard/blob/master/hazard/urls.py
But I get this error:
cache_page has a single mandatory positional argument: timeout
I read the code for cache_page and it has the following:
if len(args) != 1 or callable(args[0]):
raise TypeError("cache_page has a single mandatory positional argument: timeout")
cache_timeout = args[0]
which means it wont allow more than 1 argument. Is there any other way to get cache_page to work?? I have been digging into this for sometime...
It seems like the previous solutions wont work any longer
According to the caching docs, the correct way to cache a CBV in the URLs is:
from django.views.decorators.cache import cache_page
url(r'^my_url/?$', cache_page(60*60)(MyView.as_view())),
Note that the answer you linked to is out of date. The old way of using the decorator has been removed (changeset).
You can simply decorate the class itself instead of overriding the dispatch method or using a mixin.
For example
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
#method_decorator(cache_page(60 * 5), name='dispatch')
class ListView(ListView):
...
Django docs on decorating a method within a class based view.
yet another good example CacheMixin
from cyberdelia github
class CacheMixin(object):
cache_timeout = 60
def get_cache_timeout(self):
return self.cache_timeout
def dispatch(self, *args, **kwargs):
return cache_page(self.get_cache_timeout())(super(CacheMixin, self).dispatch)(*args, **kwargs)
usecase:
from django.views.generic.detail import DetailView
class ArticleView(CacheMixin, DetailView):
cache_timeout = 90
template_name = "article_detail.html"
queryset = Article.objects.articles()
context_object_name = "article"
You can add it as a class decorator and even add multiple using a list:
#method_decorator([vary_on_cookie, cache_page(900)], name='dispatch')
class SomeClass(View):
...
I created this little mixin generator to do the caching in the views file, instead of in the URL conf:
def CachedView(cache_time=60 * 60):
"""
Mixing generator for caching class-based views.
Example usage:
class MyView(CachedView(60), TemplateView):
....
:param cache_time: time to cache the page, in seconds
:return: a mixin for caching a view for a particular number of seconds
"""
class CacheMixin(object):
#classmethod
def as_view(cls, **initkwargs):
return cache_page(cache_time)(
super(CacheMixin, cls).as_view(**initkwargs)
)
return CacheMixin
Yet another answer, we found this to be simplest and is specific to template views.
class CachedTemplateView(TemplateView):
#classonlymethod
def as_view(cls, **initkwargs): ##NoSelf
return cache_page(15 * 60)(super(CachedTemplateView, cls).as_view(**initkwargs))
Would like to add this:
If you need to use multiple decorators for cache like vary_on_headers and cache_page together, here is one way I did:
class CacheHeaderMixin(object):
cache_timeout = int(config('CACHE_TIMEOUT', default=300))
# cache_timeout = 60 * 5
def get_cache_timeout(self):
return self.cache_timeout
def dispatch(self, *args, **kwargs):
return vary_on_headers('Authorization')(cache_page(self.get_cache_timeout())(super(CacheHeaderMixin, self).dispatch))(*args, **kwargs)
This way cache is stored and it varies for different Authorization header (JWT). You may use like this for a class based view.
class UserListAPIView(CacheHeaderMixin, ListAPIView):
serializer_class = UserSerializer
def get_queryset(self):
return CustomUser.objects.all()
I didn't found a good cache solution for class based views and created my own: https://gist.github.com/svetlyak40wt/11126018
It is a mixin for a class. Add it before the main base class and implement method get_cache_params like that:
def get_cache_params(self, *args, **kwargs):
return ('some-prefix-{username}'.format(
username=self.request.user.username),
3600)
Here's my variation of the CachedView() mixin - I don't want to cache the view if the user is authenticated, because their view of pages will be unique to them (e.g. include their username, log-out link, etc).
class CacheMixin(object):
"""
Add this mixin to a view to cache it.
Disables caching for logged-in users.
"""
cache_timeout = 60 * 5 # seconds
def get_cache_timeout(self):
return self.cache_timeout
def dispatch(self, *args, **kwargs):
if hasattr(self.request, 'user') and self.request.user.is_authenticated:
# Logged-in, return the page without caching.
return super().dispatch(*args, **kwargs)
else:
# Unauthenticated user; use caching.
return cache_page(self.get_cache_timeout())(super().dispatch)(*args, **kwargs)
I need to add a custom view to the Django Admin. This should be similar to a standard ChangeList view for a certain model, but with a custom result set. (I need to display all models having some date or some other date less than today, but this is not really relevant).
One way I can do this is by using the Admin queryset method, like
class CustomAdmin(admin.ModelAdmin):
...
def queryset(self, request):
qs = super(CustomAdmin, self).queryset(request)
if request.path == 'some-url':
today = date.today()
# Return a custom queryset
else:
return qs
This makes sure that ...
The problem is that I do not know how to tie some-url to a standard ChangeList view.
So you want a second URL that goes to the changelist view so you can check which of the two it was by the requested URL and then change the queryset accordingly?
Just mimick what django.contrib.admin.options does and add another URL to the ModelAdmin.
Should look something like this:
class CustomAdmin(admin.ModelAdmin):
def get_urls(self):
def wrap(view):
def wrapper(*args, **kwargs):
kwargs['admin'] = self # Optional: You may want to do this to make the model admin instance available to the view
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
# Optional: only used to construct name - see below
info = self.model._meta.app_label, self.model._meta.module_name
urlpatterns = patterns('',
url(r'^my_changelist/$', # to your liking
wrap(self.changelist_view),
name='%s_%s_my_changelist' % info)
)
urlpatterns += super(CustomAdmin, self).get_urls()
return urlpatterns
I am having some problems with sitemaps.
urls.py
from django.contrib import sitemaps
from oportunidade.views import OportunidadeSitemap
sitemaps = {'oportunidade': OportunidadeSitemap}
...
url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
views.py
...
class OportunidadeSitemap(Sitemap):
changefreq = "never"
priority = 0.5
def items(self):
return Oportunidade.objects.filter(ativo=True)
def lastmod(self, obj):
return obj.ultima_alteracao
But I get the following error when I access http://localhost:8000/sitemap.xml
'Oportunidade' object has no attribute 'get_absolute_url'
Here is my "Oportunidade" model:
class Oportunidade(models.Model):
user = models.ForeignKey(User)
titulo = models.CharField('Titulo',max_length=31)
...
def __unicode__(self):
return self.titulo
I am very confused how to set sitemap.
Please take a look at Django's sitemap class reference. Although you implement the necessary items method, you seem to be missing either the location method (or attribute) in your sitemap or the get_absolute_url method in you model class.
If location isn't provided, the framework will call the get_absolute_url() method on each object as returned by items().
Easiest way to go forward would be to implement get_absolute_url() in you Oportunidade model class.
According to the docs: https://docs.djangoproject.com/en/1.3/ref/contrib/sitemaps/#sitemap-class-reference
If you don't provide a location for the sitemap class, it will look for get_absolute_url on each object.
So, you'll either need to specify a location property on your sitemap class, or get_absolute_url on your object. That should get you going.