What is #permalink and get_absolute_url in Django? When and why to use it?
Please a very simple example (a real practical example). Thanks
As of 2013, the Django documentation discouraged use of the permalink decorator and encouraged use of reverse() in the body of the get_absolute_url method. By 2015, the permalink decorator seemed to have vanished without a trace from the Django documentation, and it was finally removed in Django version 2.1 in 2018.
So, for a standard DRY way to create a permanent link to a single object view, use get_absolute_url() in your model like this:
from django.db import models
from django.urls import reverse
# NOTE: pre Django 1.10+ this is "from django.core.urlresolvers import reverse"
class MyModel(models.Model):
slug = models.SlugField()
def get_absolute_url(self):
return reverse('mymodel_detail', args=(self.slug,))
and then have an entry in urls.py that points to your view:
url(r'^(?P<slug>[-\w\d\_]+)/$',
MyModelDetailView.as_view(),
name='mymodel_detail'),
#permalink is a python decorator, while get_absolute_url is a method on a django model.
Both are concerned with allowing you to reverse the URL for a particular object and should be used together. They are used anytime you need to provide a link to a particular object or want to display that object's specific URL (if it has one) to the user
You could simply write your get_absolute_url method to return a hard coded string, but this wouldn't adhere to Django's philosophy of DRY (don't repeat yourself). Instead, there is the #permalink to make things more flexible.
If you read the docs on the subject you will see how they relate to each other. the #permalink decorator hooks into django's URLconf's backend, allowing you to write much more portable code by using named url patterns. This is preferable to just using get_absolute_url on it's own: your code becomes much DRYer as you don't have to specify paths.
class BlogPost(models.Model):
name = modelsCharField()
slug = models.SlugField(...)
#permalink
def get_absolute_url(self):
return ("blog-detail", [self.slug,])
and in urls.py
...
url(r'/blog/(?P<slug>[-w]+)/$', blog.views.blog_detail, name="blog-detail")
in Django 2.1
The django.db.models.permalink() decorator is removed.
source
A better approach is to declare a name for your app in urls.py and then refer to that instead of hard coding anything:
in urls.py:
app_name = 'my_app'
urlpatterns = [
path('blogs/<int:slug>', blog.views.blog_detail, name='mymodel_detail'),
]
and in models.py:
from django.urls import reverse
class BlogPost(models.Model):
name = modelsCharField()
slug = models.SlugField(...)
def get_absolute_url(self):
return ('my_app:mymodel_detail, args=[self.slug,])
Related
In my views.py I have a class who take a (CreateView) and in the form_class take a Testform in forms.py
The problem is when in my forms.py I use the def save(...) method, it save correctly the data but the is no way to reverse after that
it say to me no matter what I try to return it says
"No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model."
ok, so I've tried everywhere to put this method but no way, it allways gives me this error
It depends on where you want to redirect the user after creating a new object.
Example setup:
CreateView is configured with url r'^mymodel/new/', name='mymodel_create' in urls.py.
DetailView is configured with url r'^mymodel/:pk/', name='mymodel_detail' in urls.py.
You can define get_absolute_url like this:
class MyModel(Model):
# field definitions...
def get_absolute_url(self):
return reverse('mymodel_detail', kwargs={'pk': self.pk})
Creating a new object will then redirect to the DetailView.
I have created a object which I am trying to save in Django admin in myapp>Hello. But the object does not get created under 'Hello' when I run the server. How can I fix it? I have also registered my models in admin.py.
models.py:
from django.db import models
from django.contrib.auth.models import User
class Foo(models.Model):
foo_id = models.CharField(max_length=10)
class Hello(models.Model):
user = models.ForeignKey(User, models.DO_NOTHING)
foo_id = models.ForeignKey('Foo', models.DO_NOTHING, db_column='foo_id')
foo_text = models.CharField(max_length=500, default="Hello!")
views.py
from django.shortcuts import render,HttpResponse,redirect
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import User,Foo, Hello
from django.contrib.auth import settings
#login_required
def home(request):
return render(request, 'index.html')
#login_required
def sendhello() :
Foos=Foo.objects.all()
for foo in foos:
#Hello(user=user, foo_text='hello there', foo_id=foo).save()
xyz, obj=Hello.objects.get_or_create(user=user, foo_text='hello there', foo_id=foo)
if xyz is True:
obj.save()
#login_required
def helloxyz(request):
user = User.objects.get(id=request.session['id'])
hellos=Hello.objects.filter(user_id=user)
print(hellos)
hellos_list=[]
for hello in hellos:
print(hello.hello_text)
hellos_list.append(hello.hello_text)
hellos_list.reverse()
print(hellos_list)
return render(request,'index.html',{'hellos': hellos_list,})
First of all, your sendhello function is not a view, so it does not get called when you view your site. I will for now just assume you have a JS login dialogue or someting you get inputs from. SO you want to generate DB entries from these inputs that are present (?) in your DB. You could do this by simply calling your generating function from within a view class or function (I recommend to put such non-view helper functions in a seperate file and import them in your view) or outside of views with e.g. django cron.
The simplest would be to just create a new file utilis.py, import them in your views with import myapp.utils and then call your function at the top of your site view.
Why a sperate file? It is just for better readability, is cleaner when someone else looks at your code and swapping out helpers is a bit easier.
If you have regularly occuring tasks that should best be executed independant from the user loading a specific page, take a look at the django_crontab module, it is really handy. You need to be on a Linux system dough.
I hope that answered your question
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.
I'm trying to add some kind of generic preview functionality to the Django admin. Opposed to Django's builtin preview-on-site functionality this preview should only be visible to logged in users with specific permissions.
All my content models have the same base class which adds a status like published and unpublished. Obviously unpublished content doesn't appear on the website, but editors should still be able to preview an unpublished site.
I read about class based views in the upcoming Django 1.3 release which might be well suited to implement it in a generic way. With Django 1.2 i can't seem to come up with a solution without touching any single view and adding specific permission checks. Has anyone done something like that before?
I believe the Django Admin already provides a "show on site" option to the admin pages of any models which provides a get_absolute_url() method. Using decorators, it should be possible to do this in a generic way across models
class MyArticleModel(Article): #extends your abstract Article model
title = .....
slug = ......
body = ......
#models.permalink
def get_absolute_url(self): # this puts a 'view on site' link in the model admin page
return ('views.article_view', [self.slug])
#------ custom article decorator -------------------
from django.http import Http404
from django.shortcuts import get_object_or_404
def article(view, model, key='slug'):
""" Decorator that takes a model class and returns an instance
based on whether the model is viewable by the current user. """
def worker_function(request, **kwargs):
selector = {key:kwargs[key]}
instance = get_object_or_404(model, **selector)
del kwargs[key] #remove id/slug from view params
if instance.published or request.user.is_staff() or instance.author is request.user:
return view(request, article=instance, **kwargs)
else:
raise Http404
return worker_function
#------- urls -----------------
url(r'^article/(?(slug)[\w\-]{10-30})$', article_view, name='article-view'),
url(r'^article/print/(?(id)\d+)$',
article(view=generic.direct_to_template,
model=MyArticleModel, key='id'),
name='article-print-view'
)
#------ views ----------------
from django.shortcuts import render_to_response
#article(MyArticleModel)
def article(request, article):
#do processing!
return render_to_response('article_template.html', {'article':instance},
xontext_instance=RequestContext(request) )
Hope this is informative (and hopefully correct ;)
How do I wrap a Django Form Wizard in a view? I need to do this so I can access request.
Does anyone have some example code for this?
I probably should be just commenting on Manoj's answer, but sounds you need code
urls.py
from django.conf.urls.defaults import *
from MyApp import views
urlpatterns = patterns(
'',
(r'^wizard/$', views.MyWizardView ),
)
views.py
#login_required
def MyWizardView (request):
cw = MyWizard([WizardName, WizardQuestions, WizardProvider, WizardGoomber])
return cw(request)
The as_view function converts a class based view into a callable view:
from django import forms
from django.contrib.formtools.wizard.views import SessionWizardView
class Form1(forms.Form):
a = forms.CharField()
class Form2(forms.Form):
b = forms.CharField()
class MyWizard(SessionWizardView):
pass
wizard_view = MyWizard.as_view([Form1, Form2])
def view(request):
# do something fancy with the request object here
return wizard_view(request)
This is basicly the same answer as in How to wrap a Django Form Wizard in a View?
This Django snippet may prove useful.
From the title: "FormWizard inside view with proper context handling and site templating support, without having to use urls.py"