i want to fetch a blog with a model field which is unique and but when i click on a perticular blog it throw me above mentioned error
here is my views
class Blogs(View):
def get(self, request):
blog_list = Blog.objects.order_by('-joined_date')
return render(request, 'blogs.html',{'blog_list':blog_list})
class ReadBlogs(View):
def get(self, request, title):
readblog = Blog.objects.filter(title=title)
return render(request,'blogs_read.html', {'readblog':readblog})
my model
class Blog(models.Model):
title = models.CharField(max_length=48, blank=False)
urltitle = models.SlugField(max_length=48, blank=False, unique=True)
title_image = models.ImageField(upload_to='blog',blank=True, null=True)
subone = models.CharField(max_length=80, blank=False)
subone_image = models.ImageField(upload_to='blog',blank=True,null=True)
onedes = models.TextField(blank=False)
my html for fetching right blog
<div class="blogs">
{% for blogs in blog_list %}
<a class="products" href="{% url 'blogs:readblog' title=blogs.urltitle %}">
<div class="blog col-4" style="width: 18rem; height:350px">
<img class="img" src="{{ blogs.title_image.url }}" alt="" height="250px" width="100%">
<div class="detail">
<h4 class="title text-center" style="color: #025; font-family:cursive;">{{blogs.title}}</h4>
</div>
</div>
</a>
{% endfor %}
</div>
my url.py
urlpatterns = [
path('blogs/',Blogs.as_view(),name="Blogs"),
path('<slug:title>/',ReadBlogs.as_view(),name="readblog")
]
as you can see mu urltitle is unique slug field so but when i clicked on a particular blog i got above mentioned error any idea what causing error
my template for showing realated field of that blog
<div class="col-11 card">
<div class="blogs">
{% for blog in readblog %}
<h1 class="text-center" style="color: #025; font-family:cursive; margin-top:20px;">{{read.title}}</h1>
{% endfor %}
</div>
</div>
The URL parameter is named title, not url_title:
path('<slug:title>/',ReadBlogs.as_view(),name='readblog')
therefore the .get(…) method should work with title as parameter:
class ReadBlogs(View):
# title ↓
def get(self, request, title):
blog = Blog.objects.filter(title=title)
return render(request,'blogs_read.html',{'blog':blog})
Here blog is a collection of zero, one, or more blogs. If you want to pass a single Blog object, you should fetch a single object, for example with get_object_or_404:
from django.shortcuts import get_object_or_404
class ReadBlogs(View):
def get(self, request, title):
blog = get_object_or_404(Blog, title=title)
return render(request,'blogs_read.html',{'blog':blog})
It might also make more sense to work with a DetailView [Django-doc] to automatically render the item properly:
from django.shortcuts import get_object_or_404
from django.views.generic.detail import DetailView
class ReadBlogs(DetailView):
model = Blog
template_name = 'blogs_read.html'
def get_object(self, *args, **kwargs):
return get_object_or_404(Blog, title=self.kwargs['title'])
template
{% url 'blogs:readblog' title=blogs.urltitle %}
here, you pass title=urltitle, so you actually pass urltitle!
views
# your-code | wrong
class ReadBlogs(View):
def get(self, request, title):
# ======== HERE, TITLE IS ACTUALLY urltitle! ==================
readblog = Blog.objects.filter(title=title)
return render(request,'blogs_read.html', {'readblog':readblog})
# correct
class ReadBlogs(View):
def get(self, request, title):
readblog = Blog.objects.filter(urltitle = title)
return render(request,'blogs_read.html', {'readblog':readblog})
Related
I have a simple blog app which has a blog list view and their detail pages that are generated automatically. everything seems to work just fine in local. however after I deployed the app on the server when I click to see the detail page I get a Template does not exist error.
this is my views:
class BlogMain(ListView):
model = BlogPost
template_name = 'Blog/Blog-news.html'
queryset = BlogPost.objects.all()
context_object_name = 'posts'
ordering = ['-published']
paginate_by = 3
def get_context_data(self, **kwargs):
context = super(BlogMain, self).get_context_data(**kwargs)
context['tags'] = BlogPost.tags.all()
return context
class BlogTags(ListView):
model = BlogPost
template_name = 'Blog/Blog-news.html'
context_object_name = 'posts'
def get_queryset(self):
return BlogPost.objects.filter(tags__slug=self.kwargs.get('tag_slug'))
def get_context_data(self, **kwargs):
context = super(BlogTags, self).get_context_data(**kwargs)
context['tags'] = BlogPost.tags.all()
return context
class BlogDetail(DetailView):
model = BlogPost
template_name = 'Blog/Blog-news-detail.html'
context_object_name = 'blog'
slug_url_kwarg = 'the_slug'
slug_field = 'slug'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["related_items"] = self.object.tags.similar_objects()[:3]
return context
this is my urls:
path('blog/', views.BlogMain.as_view(), name="blog-page"),
path('log/tags/<slug:tag_slug>/', views.MaghaleTags.as_view(), name="tagged"),
re_path(r'blog/(?P<the_slug>[-\w]+)/', views.BlogDetail.as_view(), name='post-detail'),
and finally my templates:
{% for post in posts %}
<div class="container-fluid blog-main mb-5">
<a href="{% url 'post-detail' post.slug %}">
<div class="row">
<div class="col-lg-5 pt-5 pb-3 my-5">
<h5>{{ post.title | persianize_digits }}</h5>
<small>
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
</small>
<br>
</div>
</div>
</a>
</div>
{% endfor %}
I am sure is this the issue with you I'll just share my experience. I named my template blog.html and in the code I wrote Blog.html just starting character capital. It was running great with my local server but when I deployed the website on pythonanywhere.com it gave me the same error template does not exist so I changed the template name to blog.html in the code and it worked. See if you are having this kind of issue.
I want my users to be able to add certain songs to Favourite Songs but although the success message 'Added to favourite songs' but when I visit the Favourite Songs page, I see no songs there. How can I fix this? Thanks in advance!
My models.py:
class Songs(models.Model):
title = models.CharField(max_length = 100)
lyrics = models.TextField()
author = models.CharField(max_length = 100)
track_image = models.CharField(max_length=2083)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('/', kwargs={'pk': self.pk})
My views.py:
def home(request):
context = {
'songs': Songs.objects.all()
}
return render(request, 'home.html', context)
#login_required
def add_to_fav_songs(request, **kwargs):
fav_song = Songs.objects.filter(id=kwargs.get('id'))
messages.success(request, f'Added to favourite songs')
return redirect('/')
class Fav_songs(ListView):
model = Songs
template_name = 'fav_songs.html'
context_object_name = 'fav_song'
paginate_by = 2
def get_queryset(self):
return Songs.objects.filter(pk=self.kwargs.get('pk'))
My favoutie_songs.html:
{% for song in fav_song %}
<article class="media content-section">
<div class="media-body">
<h2><a class="article-title" href="{% url 'song-detail' song.id %}">{{ song.title }}</a></h2>
<div class="article-metadata">
<a class="mr-2" href="{% url 'author-songs' song.author %}">{{ song.author }}</a>
</div>
<br>
<img class="card-img-bottom" height="339px" width="20px" src="{{ song.track_image }}">
</div>
</article>
{% endfor %}
Your Song is not connected to the User, so you never keep track about what user has wat song as favorite.
You should add a ManyToManyField to your Song model with:
from django.conf import settings
class Songs(models.Model):
# …
favorited_by = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='favorite_songs'
)
# …
Then in our view we can add the logged in user to the favorited_by field. Since we here alter data, this should be done with a POST request:
from django.contrib.auth.decorators import loginrequired
from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_http_methods
#login_required
#require_POST
def add_to_fav_songs(request, pk):
fav_song = get_object_or_404(Songs, id=pk)
fav_song.favorited_by.add(request.user)
messages.success(request, 'Added to favourite songs')
return redirect('/')
For the ListView, we can then filter by the logged in user:
from django.contrib.auth.mixins import LoginRequiredMixin
class Fav_songs(LoginRequiredMixin, ListView):
model = Songs
template_name = 'fav_songs.html'
context_object_name = 'fav_song'
paginate_by = 2
def get_queryset(self):
return Songs.objects.filter(favorited_by=self.request.user)
You should change the button to add this to the favorite to a miniform:
<form method="post" action="{% url 'add-to-fav-songs' song.id %}">
<button class="btn btn-danger" type="submit">Add to Favorite Songs</button>
</form>
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
im following a tutorial for the project but it does it on function views and im trying to do it on class based views
i get a ( The view blog.views.PostDetailView didn't return an HttpResponse object. It returned None instead.) error but thats not my concern now ... because the data(new comments) arent getting saved
so how can i save them with the post request and redirect to the same page of the DetailView
my urls
app_name = 'blog'
urlpatterns = [
path('', views.PostListView.as_view(), name='blog-home'),
path('blog/<slug:slug>/', views.PostDetailView.as_view() , name='post-detail'),
]
my models
class Post(models.Model):
options = (
('draft', 'Draft'),
('published', 'Published')
)
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date='publish_date')
publish_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
content = models.TextField()
status = models.CharField(max_length=10, choices=options, default='draft')
class Meta:
ordering = ('-publish_date',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'slug': self.slug})
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
publish_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-publish_date',)
def __str__(self):
return f'Comment By {self.author}/{self.post}'
my forms
class AddCommentForm(forms.ModelForm):
content = forms.CharField(label ="", widget = forms.Textarea(
attrs ={
'class':'form-control',
'placeholder':'Comment here !',
'rows':4,
'cols':50
}))
class Meta:
model = Comment
fields =['content']
my views
class PostDetailView( DetailView):
model = Post
context_object_name = 'post'
template_name='blog/post_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
comments = Comment.objects.filter(post=self.object)
context['comments'] = comments
context['form'] = AddCommentForm()
return context
def post(self, request, *args, **kwargs):
pass
def form_valid(self, form):
form.instance.author = self.post.author
user_comment.post = self.post
user_comment.save()
return super().form_valid(form)
html
<form method="POST">
<div class="col-12">
<hr>
{% with comments.count as total_comments %}
<legend class="border-bottom mb-4">{{total_comments }} comment{{total_comments|pluralize }}</legend>
{% endwith %}
{% for c in comments%}
<div class ="col-md-12 mb-1rem" >
<p class="mb-0"><strong>{{c.author}}:</strong> {{c.content}}</p>
<small class="text-muted">{{ c.publish_date|date:'f A, Y'}}</small>
</div>
<br>
{% endfor %}
</div>
<hr>
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Comment</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-dark btn-lg mt-1" type="submit">Publish</button>
</div>
</form>
The DetailView has no form_valid method.
It just shows the objects of the model.
Form processing is in the GenericEdit View class.
There are many ways, but...
In this code, you can create a GenericEdit View(CreateView or UpdateView ) url, process the form there(form_valid), and then
success_url = reverse_lazy('form:detail') # in GenericEdit View class
return the template.
To sum up,
add path in urls.py
like this... 👇
path('blog/<slug:slug>/update', views.PostUpdateView.as_view(), name='post-update'),
add update or create url in form action.
ex. ☞ action="{% url 'blog:post-update' %}"
make GenericEdit View class☞ views.PostUpdateView
ps.You can also use forms.py
"""
When you use Class Base View in django, it is recommended to refer to this site.
https://ccbv.co.uk/
and,
DetailView refer is here
https://ccbv.co.uk/projects/Django/3.0/django.views.generic.detail/DetailView/
"""
I know it's been a long time, but I think someone might need the answer in future.
I'm using django 3.2.16.
in your post method inside DetailView:
Update your post method to:
def post(self, request, *args, **kwargs):
# Get the current pk from the method dictionary
pk = kwargs.get('pk')
if request.method == 'POST':
# Get the current object
obj = self.model.objects.get(id=pk)
# Alter the field Value
some_value = request.POST.get('some_value_from_html_input')
obj.field = some_value
# Save the object
obj.save()
# Redirect to you current View after update
return redirect(current_details_view, pk=pk)
I have model named Book in models.py file.
And this model has slug field to display details of books
Books are being displayed in home.html template and product.html template is to display details of selected book.
I really don't know much about slugs, and how they work.
Models.py:
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField('Title', max_length=255)
authors = models.ManyToManyField(Author, related_name='books_written')
publisher = models.ForeignKey(Publisher, on_delete=models.DO_NOTHING, related_name='books_published')
price = models.DecimalField('Price', decimal_places=2, max_digits=10)
description = models.TextField('Description')
upload_timestamp = models.DateTimeField('Uploading DateTime', auto_now_add=True)
categories = models.ManyToManyField(Category, related_name='book_category')
cover = models.ImageField(upload_to='covers', null=True,blank=True)
copyright_proof = models.ImageField(upload_to=book_copyrights_path, null=True,blank=True)
slug = models.SlugField(max_length=100,blank=True)
def get_absolute_url(self):
return reverse("bookrepo:product", kwargs={
'slug': self.slug
})
def __str__(self):
return "Title: {} | Authors: {} | Price: {}".format(
self.title, self.get_authors(), self.price
)
urls.py
app_name = 'bookrepo'
urlpatterns = [
path('product/<slug:slug>/', ItemDetailView.as_view(), name='product'),
path('',views.home,name='home'),
path('about/',views.about,name='about'),
path('faq/',views.faq,name='faq'),
path('login/',views.user_login,name='login'),
path('shop/',views.shop,name='shop'),
path('signup/',views.registration,name='signup'),
path('logout/', views.user_logout, name='logout'),
]
views.py
class ItemDetailView(DetailView):
model = Book
template_name = "bookrepo/product.html"
def main(self, *args, **kwargs):
# kwargs key should be equal to parameter passed from url
slug_from_param = self.kwargs.get('slug')
def home(request):
bookz = Book.objects.order_by('title')
var = {'books': bookz, 'range': 10}
return render(request, 'bookrepo/home.html', context=var)
home.html
<div class="row">
{% load my_filters %}
{% for b in books|slice:":10" %}
<div class="col-lg-2 col-md-3 col-sm-4">
<div class="item">
<img src="{{ b.cover.url }}" alt="book-image">
<h6>{{ b.title }}</h6>
<h6>{{ b.get_authors }}</h6>
<h6><span class="price">{{ b.price }}</span></h6>
<a href="{% url 'bookrepo:product' b.slug %}" class="btn btn-sm my-btn detail-btn">
<span><i class="fa fa-info-circle"></i></span>Book Details
</a>
</div>
</div>
{% endfor %}
</div>
Honestly speaking, I don't know much about slugs and class-based views. I have used only function-based views. And, by searching internet, I found this "slug" way to get url of detail page.
In html template, I tried this way to: (got the same results)
<a href="{{ item.get_absolute_url }}" class="btn btn-sm my-btn detail-btn">
Simply do:
<a href="{% url 'product' b.slug %}" class="btn btn-sm my-btn detail-btn">
Your urls.py, <slug> to <slug:slug>:
path('product/<slug:slug>/', ItemDetailView.as_view(), name='product'),
In your views.py, you could get slug as:
class ItemDetailView(DetailView):
model = Book
template_name = "bookrepo/product.html"
def main(self, *args, **kwargs):
# kwargs key should be equal to parameter passed from url
slug_from_param = self.kwargs.get('slug')
# Your remaining codes
Slug is nothing but a url. As, you could not add any strings to url, as there can be spaces, you can use slug.
Have a look at Refs
I'm new in Django. There is a html page (project_details) which should show the title and the tasks of the project, but shows only the title of the project, not the tasks. The tasks exists, the problem is the filter!!!
views.py The error is here
from .models import Project,Task
from django.views.generic import ListView, DetailView
class ProjectsList(ListView):
template_name = 'projects_list.html'
queryset= Project.objects.all()
class ProjectDetail(DetailView):
model = Project
template_name = 'projects_details.html'
def get_context_data(self, **kwargs):
context = super(ProjectDetail, self).get_context_data(**kwargs)
## the context is a list of the tasks of the Project##
##THIS IS THE ERROR##
context['tasks'] = Task.object.filter(list=Project) <---->HERE ((work with Task.object.all() ))
return context
models.py
class Project(models.Model):
title = models.CharField(max_length=30)
slug = AutoSlugField(populate_from='title', editable=False, always_update=True)
class Task(models.Model):
title = models.CharField(max_length=250)
list = models.ForeignKey(Project)
slug = AutoSlugField(populate_from='title', editable=False, always_update=True)
urls.py
from django.conf.urls import url
from .models import Project
from .views import ProjectsList, ProjectDetail
urlpatterns = [
url(r'^$', ProjectsList.as_view(), name='project_list'),
url(r'(?P<slug>[\w-]+)/$',ProjectDetail.as_view() , name='project_details'),]
projects_details.html
{% extends './base.html' %}
{% block content %}
<div>
<a href={{ object.get_absolute_url }}>
<h4> {{object.title}} </h4>
</a>
<ul>
{% for task in tasks %} <----> NO OUTPUT <li>
<li> {{task}}</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
Sorry for my bad English.
Project is the model class, so doing (list=Project) doesn't make sense.
If you want to access the object in the detail view's get_context_data method, you can use self.object:
def get_context_data(self, **kwargs):
context = super(ProjectDetail, self).get_context_data(**kwargs)
context['tasks'] = Task.objects.filter(list=self.object)
return context
However, you don't actually have to override the get_context_data method at all. In your template, you can follow the relationship backwards from a project to get its tasks:
{% for task in object.task_set.all %}
<li>{{task}}</li>
{% endfor %}