Ideal page is not shown.
I wrote in views.py
def top(request):
content = POST.objects.order_by('-created_at')[:5]
return render(request, 'top.html',{'content':content})
def detail(request,pk):
content = POST.objects.order_by('-created_at')[:5]
return render(request, 'detail.html',{'content':content})
in top.html
<div>
{% for item in content %}
<h2>{{ item.title }}</h2>
<p>SHOW DETAIL</p>
{% endfor %}
</div>
in detail.html
<div>
<h2>{{ content.title }}</h2>
<p>{{ content.text }}</p>
</div>
in urls.py
urlpatterns = [
path('top/', views.top, name='top'),
path('detail/<int:pk>/',views.detail , name='detail'),
]
When I access top method, top.html is shown.And when I click SHOW DETAIL url link, detail.html is shown.But in this detail.html,always content is same.I wanna make a system when I click this link, detail's content is changed each content.pk.But now my system is not my ideal one.Why does my system always return same content in detail.html?How should I fix this?
Well, you're not pulling the object being requested - it's just the same query to pull the list. You need to use Model.objects.get() in order to retrieve the detail for the object.
def detail(request,pk):
# Get the object with the matching pk
content = POST.objects.get(id=pk)
return render(request, 'detail.html',{'content':content})
You should also look into Class Based Views (CBVs) as what you're doing could be simplified with a DetailView and ListView.
In
def detail(request,pk):
content = POST.objects.order_by('-created_at')[:5]
return render(request, 'detail.html',{'content':content})
You're not doing anything with the primary key (pk). You go to the database and fetch the 5 most recent posts. Don't you want it to be:
def detail(request,pk):
content = POST.objects.get(pk=pk)
return render(request, 'detail.html',{'content':content})
?
Since you're doing this for views, I recommend you also take a look to get_object_or_404
I think you've used the content variable instead of item in top.html, it's always taking pk of first element from list. Check below code.
<div>
{% for item in content %}
<h2>{{ item.title }}</h2>
<p>SHOW DETAIL</p>
{% endfor %}
</div>
Related
I am trying to create a way to edit individual blog posts from their individual html. Here are the relevant files and trace back. I am somewhat understanding that the issue lies in blog_post.id being due to the fact that blog_post has not carried over from the for loop on blog_posts.html. I have read up on others having this issue and they all structured their pages to have the edit button being inside the original for loop, which makes sense in hindsight. BUT now that I have run into this issue, I'm determined to understand how I can solve it without going back and restructuring my pages to align with the others I saw.
urls.py
from django.urls import path
from . import views
app_name = 'blogs'
urlpatterns = [
# Home page
path('', views.index, name='index'),
path('blog_posts/', views.blog_posts, name='blog_posts'),
path('blog_posts/<int:blog_post_id>/', views.blog_post, name='blog_post'),
path('new_blog_post/', views.new_blog_post, name='new_blog_post'),
path('edit_blog_post/<int:blog_post_id>/', views.edit_blog_post, name='edit_blog_post'),
]
views.py
from .models import BlogPost
from .forms import BlogPostForm
def index(request):
"""Home page for Blog."""
return render(request, 'blogs/index.html')
def blog_posts(request):
"""Show all Blog Posts."""
blog_posts = BlogPost.objects.order_by('date_added')
context = {'blog_posts': blog_posts}
return render(request, 'blogs/blog_posts.html', context)
def blog_post(request, blog_post_id):
"""Show details of an individual blog post."""
blog_post = BlogPost.objects.get(id=blog_post_id)
title = blog_post.title
id = blog_post_id
date = blog_post.date_added
text = blog_post.text
context = {'title': title, 'text': text, 'date': date}
return render(request, 'blogs/blog_post.html', context)
def new_blog_post(request):
"""Add a new blog post"""
if request.method != 'POST':
# No data submitted, create a blank form.
form = BlogPostForm()
else:
# POST data submitted, process data.
form = BlogPostForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:blog_posts')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'blogs/new_blog_post.html', context)
def edit_blog_post(request, blog_post_id):
"""Edit an existing blog post's title or text."""
blog_post = BlogPost.objects.get(id=blog_post_id)
if request.method != 'POST':
# Initial request, prefill with the current data.
form = BlogPostForm(instance=blog_post)
else:
# POST data submitted; process new data.
form = BlogPostForm(instance=blog_post, data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:blog_post', blog_post_id=blog_post.id)
context = {'blog_post': blog_post, 'form': form}
return render(request, 'blogs/edit_blog_post.html', context)
models.py
from django.db import models
class BlogPost(models.Model):
"""A post the user is posting on their blog."""
title = models.CharField(max_length=200)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model"""
return f"{self.title.title()}"
blog_posts.html
{% extends 'blogs/base.html' %}
{% block content %}
<p>Blog Posts</p>
<ul>
{% for blog_post in blog_posts %}
<li>
{{ blog_post }}
</li>
{% empty %}
<li>No posts have been made yet.</li>
{% endfor %}
</ul>
Add a new blog post
{% endblock content %}
blog_post.html
{% extends 'blogs/base.html' %}
{% block content %}
<p>Blog Post: {{ title }}</p>
<p>Entry:</p>
<p>{{ text }}</p>
<p>{{ date }}</p>
<p>
Edit Blog Post
</p>
{% endblock content %}
edit_blog_post.html
{% extends "blogs/base.html" %}
{% block content %}
<p>
{{ blog_post }}
</p>
<p>Edit Blog Post</p>
<form action="{% url 'blogs:edit_blog_post' blog_post.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save Changes</button>
</form>
{% endblock content %}
Reverse for 'edit_blog_post' with arguments '('',)' not found. 1 pattern(s) tried: ['edit_blog_post/(?P<blog_post_id>[0-9]+)/\Z']
3 {% block content %}
4
5 <p>Blog Post: {{ title }}</p>
6
7 <p>Entry:</p>
8
9 <p>{{ text }}</p>
10 <p>{{ date }}</p>
11
12 <p>
13 Edit Blog Post
14 </p>
15
16 {% endblock content %}
If I've read the question correctly, You're getting the error becuase you are not providing the necessary ID to the URL construction part of your template.
You're separating out the elements (date, content etc) to send to the template, but not passing the ID at the same time. You could send the ID in as a separate context variable, but that's extra typing for no real reward.
It's easiest to pass in the post itself via context and refer to its attributes in the template - I think it makes it easier to read also. That way the ID is there when you need to contruct the edit link, and if you change the model to possess extra fields, you don't need to convert and add to the context as the whole post is already there.
views.py
def blog_post(request, blog_post_id):
"""Show details of an individual blog post."""
blog_post = BlogPost.objects.get(id=blog_post_id) #this is all we need
context = {"blog_post_context": blog_post}
return render(request, 'blogs/blog_post.html', context)
blog_post.html
{% extends 'blogs/base.html' %}
{% block content %}
<p>Blog Post: {{ blog_post_context.title }}</p>
<p>Entry:</p>
<p>{{ blog_post_context.text }}</p>
<p>{{ blog_post_context.date }}</p>
<p>
Edit Blog Post
</p>
{% endblock content %}
If that all works, look into using get_object_or_404 rather than Post.objects.get for some additional robustness.
I assume you got the error when you try visiting the blog_post.html page. If I'm correct, then here's an approach you could take...
In your views.py
def blog_post(request, blog_post_id):
"""Show details of an individual blog post."""
# blog_post = BlogPost.objects.get(id=blog_post_id)
blog_post = get_object_or_404(id=blog_post_id) # Recommended
# Commented lines below are somewhat not necessary...
# title = blog_post.title
# id = blog_post_id
# date = blog_post.date_added
# text = blog_post.text
context = {'blog_post': blog_post}
return render(request, 'blogs/blog_post.html', context)
edit_blog_post.html is expecting an object called blog_post to be able to access the blog_post.id for {% url 'blogs:edit_blog_post' blog_post.id %}.
Now within the edit_blog_post.html file.
{% block content %}
<p>Blog Post: {{ blog_post.title }}</p>
<p>Entry:</p>
<p>{{ blog_post.text }}</p>
<p>{{ blog_post.date_added }}</p>
<p>
Edit Blog Post
</p>
{% endblock content %}
I am setting up a simple blog site with Django, and run into this error when trying to link to a page that allows users to edit existing blog posts.
Reverse for 'edit_post' with arguments '('',)' not found. 1 pattern(s) tried: ['edit_post/(?P<title_id>[0-9]+)/$']
If I understand this error correctly, it means that Django can't find a urlpattern that matches the url being requested. To my eyes I have the urlpattern set up correctly, but I still get the error.
The link in question appears on the text.html template, which is the template that displays the text of a particular blog post.
Here is the relevant code:
urls.py
"""Defines URL patterns for blogs."""
from django.urls import path
from . import views
app_name = 'blogs'
urlpatterns = [
# Home page, shows all posts in chronological order.
path('', views.index, name='index'),
# A page to show the text of a specific post.
path('text/<int:title_id>/', views.text, name='text'),
# Page for adding a new post.
path('new_post/', views.new_post, name='new_post'),
# Page for editing a post.
path('edit_post/<int:title_id>/', views.edit_post, name='edit_post'),
]
views.py
from django.shortcuts import render, redirect
from .models import BlogPost
from .forms import BlogPostForm
def index(request):
"""The home page for blogs, shows all posts."""
titles = BlogPost.objects.order_by('date_added')
context = {'titles': titles}
return render(request, 'blogs/index.html', context)
def text(request, title_id):
"""Show a single post title and its text."""
title = BlogPost.objects.get(id=title_id)
text = title.text
context = {'title': title, 'text': text}
return render(request, 'blogs/text.html', context)
def new_post(request):
"""Add a new post."""
if request.method != 'POST':
# No data submitted; create a new form.
form = BlogPostForm()
else:
# POST data submitted; process data.
form = BlogPostForm(data=request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.save()
return redirect('blogs:index')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'blogs/new_post.html', context)
def edit_post(request, title_id):
"""Edit an existing post."""
post = BlogPost.objects.get(id=title_id)
if request.method != 'POST':
# Initial request: pre-fill form with the current post.
form = BlogPostForm(instance=post)
else:
# Post data submitted; process data.
form = BlogPostForm(instance=post, data=request.POST)
if form.is_valid():
form.save()
return redirect('blogs:index')
context = {'post': post, 'form': form}
return render(request, 'blogs/edit_post.html', context)
index.html (this is the homepage for the blog)
{% extends "blogs/base.html" %}
{% block content %}
<p>Blog is a generic site for a blogger to post content for their audience.</p>
<p>Posts</p>
<ul>
{% for title in titles %}
<li>
{{ title }}
</li>
{% empty %}
<li>No posts have been added yet.</li>
{% endfor %}
</ul>
Create a new post
{% endblock content %}
text.html (this page displays the text content of a particular post, and also the link to edit the post)
{% extends "blogs/base.html" %}
{% block content %}
<p>Title: {{ title }}</p>
<p>{{ text }}</p>
Edit post
{% endblock content %}
edit_post.html (this page should display the existing post and allow it to be edited)
{% extends "blogs/base.html" %}
{% block content %}
<p>Edit post:</p>
<p>Title:</p>
<form action="{% url 'blogs:edit_post' title.id %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save changes</button>
</form>
{% endblock content %}
How the edit_post function in views.py should work (in theory) is to create an instance based upon the post's title id, and then allowing the user to edit it and save changes.
I'm not sure where I'm going wrong here and any suggestions are greatly appreciated.
The name of the post object you pass to the template, is not title, but post:
{% extends "blogs/base.html" %}
{% block content %}
<p>Edit post:</p>
<p>Title:</p>
<form action="{% url 'blogs:edit_post' post.pk %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save changes</button>
</form>
{% endblock content %}
If you use title.id, it will not find that variable, and thus this will be resolved to the empty string. If you use post.id, or post.pk, it will resolve to the .id field, or primary key of the post object.
I am using the "keyone" column in the database to filter entries. So far in the code i have written i am successfully able to render template with "keyone = 2" values. what should i write in a new template file, and how should i modify existing views.py so that when the template file renders it contains a list of links , each link for each value of "keyone" , when i click on the link say "keyone = 2", the selected entries should get rendered in home.html
models.py
# app/models.py
from django.db import models
from django.urls import reverse # new
class Post(models.Model):
text = models.TextField()
def __str__(self):
return self.text[:50]
keyone = models.IntegerField(default = '777')
def get_absolute_url(self): # new
return reverse('post_detail', args=[str(self.id)])
views.py
def HomePageView(request):
key2select = Post.objects.filter(keyone=2)
return render(request, 'home.html', {
'key2select': key2select,
})
home.html
<ul>
{% for post in key2select %}
<li>{{ post.keyone }}   {{ post.text }}</li>
{% endfor %}
</ul>
sample database
desired rendering
Firstly, we need to get all keyone values in the DB, to be passed to home.html.
Then in home.html, we need a navbar or somewhere else to put all these links which represent all the keyone values.
So the code would be like:
models.py
# app.models.py would remain the same
views.py
def homePageView(request, key):
key2select = Post.objects.filter(keyone=key)
keyones = Post.objects.distinct('keyone')
return render(request, 'home.html', {
'key2select': key2select,
'keyones': keyones
})
You can check the distinct() in the Django Docs to get distinct values of a column in DB
home.html
<!-- home.html -->
<nav>
<ul>
{% for key in keyones %}
<li>somthing {{key}} something</li>
{% endfor %}
</ul>
</nav>
...
<ul>
{% for post in key2select %}
<li>{{ post.keyone }}   {{ post.text }}</li>
{% endfor %}
</ul>
As we passed the key to the url in the nav link, we need to change the url pattern to catch this.
urls.py
urlpatterns =[
...
path('home/<int:key>/', views.homePageView, name='home')
...
]
May be I'm not understand you problem, if you'r looking for create a list of link code should be
<ul>
{% for post in key2select %}
<a href="{% url "post_detail" post.id %}"><li>{{ post.keyone }}   {{ post.text }}</li><a/>
{% endfor %}
</ul>
This will contain every post link with list item
thanks #sheng-zhuang for providing the solution. Here is the working code with some slight modifications for my benefit.
I created a new template select.html and here is the logic -
Views.py
class IndexView(TemplateView):
template_name = 'index.html'
def SelectView(request):
keyones = Post.objects.values_list('keyone',flat=True).distinct()
return render(request, 'select.html', {
'keyones': keyones
})
def HomePageView(request, key):
key2select = Post.objects.filter(keyone=key)
return render(request, 'home.html', {
'key2select': key2select,
})
index.html
<header>
Select<br />
Post
</header>
select.html
<nav>
<ul>
{% for key in keyones %}
<li>Keyone value = {{key}} </li>
{% endfor %}
</ul>
</nav>
home.html
<header>
Select<br />
Post
</header>
<br /><br /><br /><br />
<ul>
{% for post in key2select %}
<li>{{ post.keyone }}   {{ post.text }}</li>
{% endfor %}
</ul>
urls.py
path('', IndexView.as_view(), name='index'),
path('select/', SelectView, name='select'),
path('home/<int:key>/', HomePageView, name='home')
I have a detail view that uses a Quiz object to display data stored in that object, like title and author. I want to have a button that links to a new page that displays different data from the same object. I don't know how to pass this data/object.
I can render the view and pass it the context of a specific quiz using an id but I want the id to change to be the id of the object from the initial page.
#assessement view
def assessment(request):
context = {
'quiz':Quiz.objects.get(id=1),
}
return render(request, 'quiz_app/assessment.html', context)
#detailview template for quiz
{% extends "quiz_app/base.html" %}
{% block content %}
<article class="quiz-detail">
<h1>{{ object.title }}</h1>
<h2>{{ object.question_amount }} Questions</h2>
<a class="btn" href="{% url 'quiz-assessment' %}">Start Quiz</a>
</article>
{% endblock content %}
#assessment template
{% extends "quiz_app/base.html" %}
{% block content %}
<h2>Assessment</h2>
<h2>Title is {{ quiz.title }}</h2>
{% endblock content %}
Then you should make another view for url quiz-assessment and pass the quiz pk as you did above in your assessment view.
def quiz_assessment(request,pk):
quiz = Quiz.objects.get (pk=pk)
return render (request,'assessment_template', {'quiz':quiz}
And in your url,pass the quiz id like this:
path ('<int:pk>/quiz/assessment /',views.quiz_assessment,name='quiz_assessment')
And in your template you can give url like this:
< a class="btn" href="{% url 'quiz_assessment' object.pk %}>
As suggested in the comments by #Robin Zigmond, you can do like this.
#assessement view
def assessment(request, qid):
context = {
'quiz':Quiz.objects.get(id=qid),
}
return render(request, 'quiz_app/assessment.html', context)
In the HTML file
#detailview template for quiz
{% extends "quiz_app/base.html" %}
{% block content %}
<article class="quiz-detail">
<h1>{{ object.title }}</h1>
<h2>{{ object.question_amount }} Questions</h2>
<a class="btn" href="{% url 'quiz-assessment' qid=object.id %}">Start Quiz</a>
</article>
{% endblock content %}
and in your urls.py change as:
path('quiz_asswssment/?P<int:qid>/', views.assessment, name="quiz_assessment")
Besides, what SammyJ has suggested, You can use the django sessions library or the django cache framework. You can temporarily store the information you need for the next view and access it whenever you want to.
In what Sammy J had suggested, you will always to have make sure that the queryset is passed in the context, otherwise it will not be rendered.
def assesment(self, request, id):
q = Quiz.objects.get(pk=id)
request.session["someData"] = q.name
request.session["qAmount] = q.amount
In your template file
<p>The title is : {{request.session.title}} and the amount is {{request.session.qamount}}
Note: Django sessions do not allow you to set a queryset as a session record, for that, you can use Django Cache framework.
Example
from django.core.cache import cache
cache.set('quiz', q)
getting cache -> cache.get('quiz')
Sessions framework docs : https://docs.djangoproject.com/en/2.2/topics/http/sessions/
Cache framework docs: https://docs.djangoproject.com/en/2.2/topics/cache/
When I requested tag_list view the posts variable didn't list out the data.
And after changing its position above self_entries view, everything worked fine.
But now I cannot get self_entries view data.
What I am missing here?
After changing the position everything falls as expected but the view which lies at the bottom doesn't display the data at all.
more views at the top
#app.route('/entries')
#login_required
def list():
posts = models.Journal.select().order_by(models.Journal.created_at.desc())
return render_template("entries.html", posts=posts)
# This View Works as expected
#app.route('/entries/<username>')
#login_required
def self_entries(username):
posts = (models.Journal
.select(models.User, models.Journal)
.join(models.User)
.where(models.User.username**username)
.order_by(models.Journal.created_at.desc())
)
# for post in posts:
# print(post.title + str(post.created_at.date()) + post.tag)
return render_template("entries.html", posts=posts)
# This view doesn't... the page displays as empty
#app.route('/entries/<tag>')
#login_required
def tag_list(tag):
posts = (models.Journal
.select()
.where(models.Journal.tag == tag)
.order_by(models.Journal.created_at.desc())
)
return render_template("entries.html", posts=posts)
more views at the bottom
entries.html
{% extends "layout.html" %}
{% block content %}
<div>
<div>
<h1>{{ hi }}</h1>
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<time datetime="{{ post.created_at }}">{{ post.created_at.date() }}</time>
<h3><a href="{{ url_for('tag_list', tag=post.tag) }}" >{{ post.tag }}</a><h3>
</article>
{% endfor %}
</div>
</div>
{% endblock %}
Ok got it. Both the route below are using the similar API ie. '/entries/', so there is a conflict. so change either one.
#app.route('/entries/<username>')
#app.route('/entries/<tag>')
changed #app.route('/entries/<tag>') to #app.route('/entry/<tag>') it works.