Django get_absolute_url always throwing a 404 - django

I am building a Django project ( 1.8) and am having trouble using get_absolute_url method of one of my models. I populated my database with some posts, so I could check my application. I am able to list the database objects in one of my views, so I know that the objects are there, and my app can display them. The problem comes when I click a link, and the view always returns a 404 ( basically It can't find an object to display)
Below is most of the code from this app. I tried to include my imports in these snippets, but truncated some of it to keep the post short.
valueFact/models.py
class ValueFactPost(models.Model):
STATUS_CHOICE = (
('draft', 'Draft'),
('published', 'Published')
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish')
author = models.ForeignKey(User, related_name='valueFact_posts')
publish = models.DateTimeField(default=timezone.now)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,
choices=STATUS_CHOICE,
default='draft')
stockSymbol = models.ForeignKey('Symbol', null=True )
objects = models.Manager()
published = ValueFactManager()
def get_absolute_url(self):
return reverse('companies:valuefact_detail', args=[self.publish.year,
self.publish.strftime('%m'),
self.publish.strftime('%d'),
self.slug])
urls.py
from django.conf.urls import include, url
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^companies/', include('valueFact.urls', namespace='companies', app_name='companies')),
]
valueFact/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.valueFactListView.as_view(), name='valueFact_list'),
url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$',
views.valuefact_detail,
name='valuefact_detail'),
]
valueFact/views.py
from django.shortcuts import render, get_object_or_404, render_to_response
from valueFact.models import ValueFactPost, Symbol
class valueFactListView(ListView):
queryset = ValueFactPost.published.all()
context_object_name = 'valueFacts'
paginate_by = 2
template_name = 'companies/valueFact/list.html'
def valuefact_detail(request, year, month, day, post):
post = get_object_or_404(ValueFactPost, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
return render(request, 'companies/valuefact/detail.html', {'post':post})
The template where the problem seems to occur:
list.html
{% for post in valueFacts %}
<a href="{{ post.get_absolute_url }}">
{{ post.title}}
</a>
</h2>
<p class="date">
Published {{post.publish}} by {{post.author}}
</p>
{{post.body|truncatewords:30|linebreaks}}
{% endfor %}**
The error in the django console is : Not Found: /companies/2016/03/23/seadrill-debt/
I have spent an inordinate amount of time on this issue which would probably take someone a couple of minutes to fix. Thanks in advance.

Do you have PYTZ installed? I can't guarantee this is the answer, but it's worth a try. I don't see anything wrong with the code, as I'm using pretty much the same code in a project.

If you use different timezone than UTC, when an object is saved, publish attribute can be different than when you have UTC timezone.
If that's the case your get_absolute_url method has to return localtime version of publish.
In your models.py, consider writing like this
def get_absolute_url(self):
"""
Get single object.
year, month, day, and slug order.
"""
# convert to Asia/seoul time
localdatetime = timezone.localtime(self.publish)
return reverse(
"blog:post_detail",
args=[
localdatetime.year,
localdatetime.month,
localdatetime.day,
self.slug,
],
)
SO has other similar post regarding this.

Method 1:
Instead of using DateTimeField use DateField in your model and the problem will be solved.
# models.py
class Post(models.Model):
publish = models.DateField(default=timezone.now)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
Method 2: Override get_object

Your url pattern establishes 4 keyword arguments:
year
month
day
post
but your call to reverse is passing those values in as positional arguments (args), which is a list. Instead, pass them in as keyword arguments (kwargs), which is a dictionary:
def get_absolute_url(self):
return reverse('companies:valuefact_detail', args=[],
kwargs={
'year':self.publish.year,
'month': self.publish.strftime('%m'),
'day': self.publish.strftime('%d'),
'post': self.slug
})

Related

why django model can't save my new object when i click create button in 'create.html'

I'm beginning to use Django, and I have some problems. I want to create new post such as blog. So I use views.py with model.py and forms.py.
but when I enter the create.html, I was writing what i want to post, and then click 'create' button. but it wasn't save in django object. I check in admin site, but there is no object. I think it means save object is failed. but I don't know where is the problem. plz help me T.T
in views.py
def create(request):
if request.method =="POST":
filled_form = ObForm(request.POST)
if filled_form.is_valid():
filled_form.save()
return redirect('index')
Ob_form = ObForm()
return render(request, 'create.html', {'Ob_form':Ob_form})
in create.html
<body>
<!-- form.py 모델 생성 -->
<form method="POST" action="">
{% csrf_token %}}
{{Ob_form.as_p}}
<input type="submit" value="확인" />
</form>
</body>
in models.py
from django.db import models
class Ob(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(null=True)
content = models.TextField(null=True)
update_at = models.DateTimeField(auto_now=True)
in forms.py
from django import forms
from .models import Ob
# 모델폼을 상속받아서 모델폼이 되었음
class ObForm(forms.ModelForm):
# 어떤 모델과 대응되는지 말해줌
class Meta:
model = Ob
fields = ( "title", "image", "content")
# 모델 폼 커스텀
# init - 내장함수 - (해당 클레스에 들어오는 여러가지 인자를 받을 수 있는 파라미터)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "제목"
self.fields['image'].label = "사진"
self.fields['content'].label = "자기소개서 내용"
self.fields['title'].widget.attrs.update({
'class': 'Ob_title',
'placeholder': '제목',
})
and urls.py
from django.urls import path
from .views import index, create, detail, delete, update
urlpatterns = [
path('', index, name="index"),
path('create/', create, name="create"),
path('detail/<int:Ob_id>', detail, name="detail"),
path('delete/<int:Ob_id>', delete, name="delete"),
path('update/<int:Ob_id>', update, name="update"),
]
A bit of guesswork here, but it's probably the case that filled_form.is_valid() is returning false, which will mean save() is never reached. To test this simply, just put an else and print on the if.
if filled_form.is_valid():
filled_form.save()
return redirect('index')
else:
print("Form validation failed")
print(filled_form.errors)
It's likely that you'd also want to return these errors to the user in the future, which means you'll need to make a couple of changes.
Right now, regardless of whether ObForm validates successfully, you are creating a new instance and passing that to the user. My typical approach would be to declare the form variable at the top of the function, and if it isn't already set when it comes to render the view, create a new instance of the form then. This way, if the form was populated by the user already (i.e. the request was a POST) then the errors will be returned with their input, instead of clearing their input (which is really annoying from a user's point of view!).
As a side note, I'm going to guess that you submitted a form with empty image and content fields, and were expecting that to be stored in your database? Try changing your field declarations to:
title = models.CharField(max_length=50)
image = models.ImageField(null=True, blank=True)
content = models.TextField(null=True, blank=True)
update_at = models.DateTimeField(auto_now=True)
null=True tells the database to allow null values, but blank=True tells the form validation to allow empty values.

Want to show detailview, clicking in a link in a listview

I want to show the detail from a school, which will show all the students but when i run the code, it doesnt find the url, i've been searching for the answer all over the web but still dont get it, the point is to show a list of all the schools in a list.html file, that works okay, but when i want to click in an item from that list, it supposed to show the details from that school which will be all the students attending that school, but it returns a 404 error, saying that it didnt find the url.
####MODELS
# Create your models here.
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return self.name
class Students(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField()
school = models.ForeignKey(School,on_delete=models.CASCADE,related_name='students')
def __str__(self):
return self.name
####VIEWS
from django.views.generic import View,DetailView,ListView
from .models import *
# Create your views here.
class Index(View):
def get(self,request):
return HttpResponse('Hello World')
class SchoolList(ListView):
model = School
template_name = 'firstapp/list.html'
context_object_name = 'School'
class SchoolDetails(DetailView):
model = School
template_name = 'firstapp/detail.html'
context_object_name = 'School_detail'
####URLS
from django.urls import path
from . import views
urlpatterns = [
path('list/',views.SchoolList.as_view(),name='list'),
path('School_detail/<int:pk>',views.SchoolDetails.as_view(),name='details')
]
####LIST HTML
{%extends 'firstapp/base.html'%}
{%block body_block%}
<h1>Here is a list of the schools!</h1>
{%for school in School%}
<p>{{school.name}}</p>
{%endfor%}
{%endblock%}
####DETAIL HTML
{%extends 'firstapp/base.html'%}
{%block body_block%}
{%for student in school_detail.students.all%}
<p>{{student.name}}</p>
{%endfor%}
{%endblock%}
Your link is not defined right
<p>{{school.name}}</p>
should be
<p><a href="{% url 'details' school.id%}>{{school.name}}</a></p>
Let me know if this solves the problem
This is one of the many reasons why using the {% url … %} template tag [Django-doc] is a good idea, because it is harder to make mistakes when you construct a URL in a template.
The URL should be School_detail/{{school.pk}}, but as said, it is better not to render this that way:
<p>{{school.name}}</p>
With this Django will look for a path(..) where the name='details', and replace the (first) parameter with school.pk.

how to pass url to a model field lookup in django

Having some trouble finding how to pass a var captured in a url to my view.
say I have a model like this...
title = models.CharField()
and a title of...
title = 'this is a post'
then how would django capture something in the URL like this...
url/this_is_a_post/
and lookup the model field like this...
x = Post.objects.get(title='this is a post')
I have another URL that is capturing a name with a space in it, and when I use a URL with "_"'s in it, it looks it up correctly, but in this case, it is telling me there is no matching query. I have tried to look in the docs, but I couldn't find anything although I know it has to be there, somewhere.
Use two fileds in your models, say;
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
and while saving yor Post, you shoud save slug also;
In views;
from django.template.defaultfilters import slugify
post = Post.objects.create(title=title, slug=slugify(title))
this slug will be unique for each post.
In urls.py
url(r'^(?P<slug>[\w-]+)/$', 'myapp.views.post_detail', name='post_detail'),
In views.py
def post_detail(request, slug):
x = Post.objects.get(slug=slug)
in models.py:
title = models.SlugField(unique=True)
in urls.py:
urlpatterns = [
...
url(r'^(?P<title>\d+)/$', views.article_view, name = 'article_detail'),
...
]
in views.py:
def article_view(request, title):
...
article = get_object_or_404(Article, title=title)
...
in template.html:
<a href="{% url 'article_detail' title=article.title %}">

Django - passing a variable from a view into a url tag in template

First of all, I apologize for the noobish question. I'm very new to Django and I'm sure I'm missing something obvious. I have read many other posts here and have not been able to find whatever obvious thing I am doing wrong. Thanks so much for any help, I am on a deadline.
I am using Django 1.6 with Python 2.7. I have one app called dbquery that uses a form to take data from the user and query a REST service. I am then trying to display the results on a results page.
Obviously there is more to add, this is just a very simple start.
The problem is that I can't seem to get the autoincremented id field from my search view into the url tag in the template properly. If I put the number 1 in like this {% url 'dbquery:results' search_id=1 %}, the page loads and works well, but I can't seem to get the variable name right, and the django documentation isn't helping- maybe this is obvious to most people. I get a reverse error because the variable ends up always being empty, so it can't match the results regex in my urls.py. I tested my code for adding an object in the command line shell and it seems to work. Is there a problem with my return render() statement in my view?
urls.py
from django.conf.urls import patterns, url
from dbquery import views
urlpatterns = patterns('',
# ex: /search/
url(r'^$', views.search, name='search'),
# ex: /search/29/results/ --shows response from the search
url(r'^(?P<search_id>\d+)/results/', views.results, name ='results'),
)
models.py
from django.db import models
from django import forms
from django.forms import ModelForm
import datetime
# response data from queries for miRNA accession numbers or gene ids
class TarBase(models.Model):
#--------------miRNA response data----------
miRNA_name = models.CharField('miRNA Accession number', max_length=100)
species = models.CharField(max_length=100, null=True, blank=True)
ver_method = models.CharField('verification method', max_length=100, null=True, blank=True)
reg_type = models.CharField('regulation type', max_length=100, null=True, blank=True)
val_type = models.CharField('validation type', max_length=100, null=True, blank=True)
source = models.CharField(max_length=100, null=True, blank=True)
pub_year = models.DateTimeField('publication year', null=True, blank=True)
predict_score = models.DecimalField('prediction score', max_digits=3, decimal_places=1, null=True, blank=True)
#gene name
gene_target = models.CharField('gene target name',max_length=100, null=True, blank=True)
#ENSEMBL id
gene_id = models.CharField('gene id', max_length=100, null=True, blank=True)
citation = models.CharField(max_length=500, null=True, blank=True)
def __unicode__(self):
return unicode(str(self.id) + ": " + self.miRNA_name) or 'no objects found!'
views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.core.urlresolvers import reverse
from dbquery.models import TarBase, SearchMainForm
from tarbase_request import TarBaseRequest
#main user /search/ form view
def search(request):
if request.method == 'POST': #the form has been submitted
form = SearchMainForm(request.POST) #bound form
if form.is_valid(): #validations have passed
miRNA = form.cleaned_data['miRNA_name']
u = TarBase.objects.create(miRNA_name=miRNA)
#REST query will go here.
#commit to database
u.save()
return render(request,'dbquery/results.html', {'id':u.id})
else: #create an unbound instance of the form
form = SearchMainForm(initial={'miRNA_name':'hsa-let-7a-5p'})
#render the form according to the template, context = form
return render(request, 'dbquery/search.html', {'form':form})
#display results page: /search/<search_id>/results/ from requested search
def results(request, search_id):
query = get_object_or_404(TarBase, pk=search_id)
return render(request, 'dbquery/results.html', {'query':query} )
templates:
search.html
<html>
<head><h1>Enter a TarBase Accession Number</h1>
</head>
<body>
<!--form action specifies the next page to load-->
<form action="{% url 'dbquery:results' search_id=1 %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Search" />
</form>
</body>
results.html
<html>
<head><h1>Here are your results</h1>
</head>
<body>
{{query}}
</body
The search results aren't created and don't have an ID until after you submit your form. The usual way to do this would be to have your form use its own URL as the action, then have the view redirect to the results view after successfully saving:
from django.shortcuts import redirect
def search(request):
if request.method == 'POST': #the form has been submitted
form = SearchMainForm(request.POST) #bound form
if form.is_valid(): #validations have passed
miRNA = form.cleaned_data['miRNA_name']
u = TarBase.objects.create(miRNA_name=miRNA)
#REST query will go here.
#commit to database
u.save()
return redirect('results', search_id=u.id)
else: #create an unbound instance of the form
form = SearchMainForm(initial={'miRNA_name':'hsa-let-7a-5p'})
#render the form according to the template, context = form
return render(request, 'dbquery/search.html', {'form':form})
Then in your template:
<form action="" method="post">
That causes your form to submit its data to the search view for validation. If the form is valid, the view saves the results, then redirects to the appropriate results page based on the ID as determined after saving.
In this case, you're likely better off passing your search parameter as a parameter, such as http://host/results?search_id=<your search value>.
This will allow you to specify your URL as url(r'results/', views.results, name ='results') and reference in your template as {% url dbquery:results %}.
Then in your view, you would change it to:
def results(request):
search_id = request.POST.get('search_id')
query = get_object_or_404(TarBase, pk=search_id)
return render(request, 'dbquery/results.html', {'query':query} )
Or if you want the query to actually show in the URL, change the form to be method="get" and the request.POST.get('search_id') to request.GET.get('search_id')

Displaying both slug and ID in URL, but route by ID only in Django

What I'm trying to achieve is: my News app should display a slug, but only query the article by ID in the form of /news/24/this-is-the-slug
Unfortunately I'm getting a NoReverseMatch: Reverse for 'news_detail' with arguments '('',)' and keyword arguments '{}' not found. when trying to browse an article. The URL generated in the template looks correct as stated above (I can confirm this by doing a search via Haystack, which delivers the correct URL).
models.py
class News(models.Model):
id = models.IntegerField(primary_key=True, editable=False)
category = models.CharField(max_length=50L)
title = models.CharField(max_length=200L)
rss_summary = models.TextField(max_length=2000L)
body_text = models.TextField(max_length=5000L)
post_date = models.DateTimeField()
prettyurl = models.SlugField(max_length=100L)
class Meta:
db_table = 'news'
def __unicode__(self):
return self.title
def get_absolute_url(self):
return urlresolvers.reverse('news_detail', kwargs={'pk': self.id, 'slug': self.prettyurl })
urls.py
urlpatterns = patterns(
'',
url(
r'^$',
view=views.NewsListView.as_view(),
name='news_index'),
url(
r'^(?P<pk>\d+)/',
view=views.NewsDetailView.as_view(),
name='news_detail'),
url(
r'^(?P<pk>\d+)/(?P<slug>[-\w]+)/$',
view=views.NewsDetailView.as_view(),
name='news_detail'),
url(
r'^archive/$',
view=views.NewsArchiveIndexView.as_view(),
name="archive_month"),
[... more unrelated urls ...]
views.py
class NewsDetailView(DetailView):
#name='news_detail'),
model = News
context_object_name = 'news'
#slug_url_kwarg = 'prettyurl'
#slug_field = 'prettyurl'
template_name = 'news/detail.html'
Template
`<p>Permalink for this article.`
Thanks #Daniel Roseman and #yuvi. With your help I managed to solve my problem by defining the URL pattern to this:
r'^(?P<pk>\d+)(?:/(?P<slug>[\w\d-]+))?/$',
Which allows all my wanted forms of
news/nn
news/nn/
news/nn/a-slug
news/nn/a-slug/
In the template, I use
{% url 'news_detail' news.id news.prettyurl %}
Which shows the fourth version in the listing above.
Thanks again!
I'm not quite sure why you're bothering to capture the slug at all. Rather than having a named group in the URL pattern, you could just have one that ignores everything after the PK:
r'^(?P<pk>\d+)/.*',
which would work just as well whether or not you passed the slug, so you could then get rid of your duplicated patterns.
There are two basic problems with what you have, though. Firstly, even though you state you want to actually match only on PK, you don't even pass the PK to the URL, just the slug. Secondly, even the slug appears to be blank, as the error message states (the args variable is just '').
You should instead pass the actual PK:
{% url 'news_detail' news.pk %}