My simple Django blog don't work - if I go to http://127.0.0.1:8000/posts/create/ I see
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/posts/create/
Raised by: posts.views.post_create
Looking in the git history I understand what error in posts/views.py.
The file itself looks like this
from urllib.parse import quote
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.contrib import messages
from .forms import PostForm
from .models import Post
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
def post_create(request):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
form = PostForm(request.POST or None, request.FILES or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
messages.success(request, 'Successfully Created')
return HttpResponseRedirect(instance.get_absolute_url())
context = {
'form': form,
}
return render(request, 'posts/post_form.html', context)
def post_detail(request, id=None):
instance = get_object_or_404(Post, id=id)
share_string = quote(instance.content)
context = {
'title': instance.title,
'instance': instance,
'share_string': share_string
}
return render(request, 'posts/post_detail.html', context)
def post_list(request):
queryset_list = Post.objects.all()
paginator = Paginator(queryset_list, 5)
page = request.GET.get('page')
queryset = paginator.get_page(page)
context = {
'object_list': queryset,
'title': 'List'
}
return render(request, 'posts/post_list.html', context)
def post_update(request, id=None):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
instance = get_object_or_404(Post, id=id)
form = PostForm(request.POST or None, request.FILES or None,
instance=instance)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
messages.success(request, 'ItemSaved',
extra_tags='html_safe')
return HttpResponseRedirect(instance.get_absolute_url())
context = {
'title': instance.title,
'instance': instance,
'form': form
}
return render(request, 'posts/post_form.html', context)
def post_delete(request, id=None):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
instance = get_object_or_404(Post, id=id)
instance.delete()
messages.success(request, 'Successfully Deleted')
return redirect('posts:list')
I think error in if not request.user.is_staff or not request.user.is_superuser line.
Because after this line, if I understand correctly from history, django-blog not work.
django.VERSION - (2, 0, 1, 'final', 0)
Thank you very much.
Related
there is a small django project that has methods for creating a topic, creating and editing posts within it.
I don't understand how to write a method to delete a post in a topic
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
from django.http import Http404
#login_required()
def new_entry(request, topic_id):
topic = Topic.objects.get(id=topic_id)
check_topic_owner(topic.owner, request)
if request.method != "POST":
form = EntryForm()
else:
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('composetopic:topic', topic_id=topic_id)
context = {'topic': topic, 'form': form}
return render(request, 'composetopic/new_entry.html', context)
#login_required()
def edit_entry(request, entry_id):
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
check_topic_owner(topic.owner, request)
if request.method != "POST":
form = EntryForm(instance=entry)
else:
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return redirect('composetopic:topic', topic_id=topic.id)
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'composetopic/edit_entry.html', context)
def check_topic_owner(owner, request):
if owner != request.user:
raise Http404
i was tried to add delete_entry() function, but its not working
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
from django.http import Http404
#login_required()
def delete_entry(request, topic_id):
try:
topic = Topic.objects.get(id=topic_id)
topic.delete()
except Topic.DoesNotExist as e:
topic = None
if request.method != "POST":
form = EntryForm()
else:
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic # <Topic: Topic object (None)> topic or None
return redirect('composetopic:topic', topic_id=topic_id) # deleted id
context = {'topic': topic, 'form': form} # <Topic: Topic object (None)> topic or None
return render(request, 'composetopic/deleted_entry.html', context)
topic should return a null queryset instance if has beed deleted correctly (id exist and object is validated) if is not is just set to None
from app.models import Topic
>>> Topic.objects.all()
<QuerySet [. . .]>
#...
>>> topic = Topic.objects.get(id = id)
>>> topic.delete()
<Topic: Topic object (None)> # You can still access to his attributes but this not exist in database
if id object is not valid, raise an error self.model.DoesNotExists then a try-except statement is used when you call get method, also when you check for type of request ("POST", "GET") you need to modify how and where redirect deleted objects or something like render a page with text 'deleted topic' and some information about deleted object.
Hey so I have this list of campaigns that have a button to start and stop the process.
'''
from django.shortcuts import render
from .models import Campaign
from .forms import CampaignForm, CampaignStatusFormSet
def home_view(request):
queryset = Campaign.objects.all()
if request.method == 'POST':
form_type = request.POST.get('id')
if form_type == 'campaign_status':
formset = CampaignStatusFormSet(
request.POST, request.FILES,
queryset=queryset,
)
for form in formset.forms:
if form.is_valid():
form.save()
else:
formset = CampaignStatusFormSet(queryset=queryset)
campaigns_and_forms = list(zip(queryset, formset))
context = {
'formset': formset,
'campaigns_and_forms': campaigns_and_forms,
}
return render(request, 'campaigns_in_progress.html', context)
'''
But when I run it and click on the button to change play/pause it shows this error.
UnboundLocalError: local variable 'formset' referenced before assignment
Can anyone help me refactor this code so that it doesn't show this error?
You can remove the else block so that the formset is available even after clicking on the play/pause buttons. When you send a POST request, and campaign_status is as expected, formset variable is not available in the conext and hence the error.
from django.shortcuts import render
from .models import Campaign
from .forms import CampaignForm, CampaignStatusFormSet
def home_view(request):
queryset = Campaign.objects.all()
if request.method == 'POST':
form_type = request.POST.get('id')
if form_type == 'campaign_status':
formset = CampaignStatusFormSet(
request.POST, request.FILES,
queryset=queryset,
)
for form in formset.forms:
if form.is_valid():
form.save()
# Set form with new created campaign
queryset = Campaign.objects.all()
formset = CampaignStatusFormSet(queryset=queryset)
campaigns_and_forms = list(zip(queryset, formset))
context = {
'formset': formset,
'campaigns_and_forms': campaigns_and_forms,
}
else:
formset = CampaignStatusFormSet(queryset=queryset)
campaigns_and_forms = list(zip(queryset, formset))
context = {
'formset': formset,
'campaigns_and_forms': campaigns_and_forms,
}
return render(request, 'campaigns_in_progress.html', context)
I don't know if this is the right solution and if it won't break something else but for now it works perfectly. I just declared the formset before the if statement so it kind of declares 2 times.
If anyone could tell me if this is a good solution I would be greatful
'''
def home_view(request):
queryset = Campaign.objects.all()
formset = CampaignStatusFormSet(
request.POST, request.FILES,
queryset=queryset,
)
if request.method == 'POST':
form_type = request.POST.get('id')
if form_type == 'campaign_status':
formset = CampaignStatusFormSet(
request.POST, request.FILES,
queryset=queryset,
)
for form in formset.forms:
if form.is_valid():
form.save()
else:
formset = CampaignStatusFormSet(queryset=queryset)
campaigns_and_forms = list(zip(queryset, formset))
context = {
'formset': formset,
'campaigns_and_forms': campaigns_and_forms,
}
return render(request, 'campaigns_in_progress.html', context)
'''
Here's my problem, when a user logs in they can see the products uploaded by the other user also but i am trying to show when an user logged in they can see the products uploaded by them only**
views.py
from django.shortcuts import render, get_object_or_404, redirect
from .forms import ProductForm
from .models import Product
from django.contrib.auth.models import User
prodcut create or upload
def product_create_view(request):
form = ProductForm(request.POST or None)
if form.is_valid():
form.save()
form = ProductForm()
context = {
'form': form
}
return render(request, "products/product_create.html", context)
product update
def product_update_view(request, id=id):
obj = get_object_or_404(Product, id=id)
form = ProductForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
context = {
'form': form
}
return render(request, "products/product_create.html", context)
tried by changing in the list view but getting this error product_list_view() missing 1 required positional argument: 'user'
def product_list_view(request, user):
queryset = Product.objects.filter(user=request.user)
context = {
"object_list": queryset
}
return render(request, "products/product_list.html", context)
**product detail **
def product_detail_view(request, id):
obj = get_object_or_404(Product, id=id)
context = {
"object": obj
}
return render(request, "products/product_detail.html", context)
product delete
def product_delete_view(request, id):
obj = get_object_or_404(Product, id=id)
if request.method == "POST":
obj.delete()
return redirect('../../')
context = {
"object": obj
}
return render(request, "products/product_delete.html", context)
this is product list template page
template/product.list.html
{% extends 'accounts/base.html' %}
{% block content %}
{% for instance in object_list %}
<p>{{ instance.id }} - <a href='{{ instance.get_absolute_url }}'>{{ instance.title }}</a></p>
{% endfor %}
{% endblock %}
here's the **
**models.py
from django.db import models
from django.urls import reverse
from django.conf import settings
from django.contrib.auth.models import User
class Product(models.Model):
title = models.CharField(max_length=120)
description = models.TextField(blank=True, null=True)
price = models.DecimalField(decimal_places=2, max_digits=10000)
summary = models.TextField(blank=False, null=False)
featured = models.BooleanField(default=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
def get_absolute_url(self):
return reverse("products:product-detail", kwargs={"id": self.id}) #f"/products/{self.id}/"
urls.py
urlpatterns = [
path('', product_list_view, name='product-list'),
path('create/', product_create_view, name='product-create'),
path('<int:id>/', product_detail_view, name='product-detail'),
path('update/<int:id>/', product_update_view, name='product-update'),
path('<int:id>/delete/', product_delete_view, name='product-delete'),
]
You never set the logged in user in your Product when you create or update it:
def product_create_view(request):
if request.method == 'POST':
form = ProductForm(request.POST)
if form.is_valid():
form.instance.user = request.user
form.save()
form = ProductForm()
context = {
'form': form
}
return render(request, 'products/product_create.html', context)
def product_update_view(request, id):
obj = get_object_or_404(Product, id=id, user=request.user)
if request.method == 'POST':
form = ProductForm(request.POST, instance=obj)
if form.is_valid():
form.instance.user = request.user
form.save()
else:
form = ProductForm(instance=obj)
context = {
'form': form
}
return render(request, 'products/product_create.html', context)
In your product_detail_view and product_delete_view, you should filter on the user as well:
def product_detail_view(request, id):
obj = get_object_or_404(Product, id=id, user=request.user)
context = {
'object': obj
}
return render(request, "products/product_detail.html", context)
def product_delete_view(request, id):
obj = get_object_or_404(Product, id=id, user=request.user)
if request.method == "POST":
obj.delete()
return redirect('../../')
context = {
'object': obj
}
return render(request, 'products/product_delete.html', context)
in your list view, you should not use user as a parameter:
def product_list_view(request):
queryset = Product.objects.filter(user=request.user)
context = {
'object_list': queryset
}
return render(request, 'products/product_list.html', context)
Additional remarks:
usually in case of a successful POST request, you should make a redirect, in order to implement the Post/Redirect/Get pattern [wiki];
you might want to make the user column non-NULLable, since now it will have NULL if you do not specify a value, and you allow to construct products without a user;
please do not use request.POST or None. A POST request can be valid, even if it is empty;
please do not write urls yourself, Django's redirect(..) [Django-doc] can resolve a URL based on the name of a view, and the values for the parameters. This is more safe if you later want to rewrite the URLs; and
you might want to take a look at class-based views like CreateView [Django-doc], UpdateView [Django-doc], and DeleteView [Django-doc]. These will reduce the amount of boilerplate logic.
I'm trying to create a form where users can add a new topic within a category. Then the users will be able to comment on each topic. Everything was working up until I tried adding the form page for new_topic
The error I receive:
ValueError at /new_topic/
The view speak_space.views.new_topic didn't return an HttpResponse object. It returned None instead.
Heres the code for my view.py and urls.py files:
view.py
from django.shortcuts import render, redirect
from .models import Category
from .models import Topic
from .forms import TopicForm
def index(request):
"""The home page for speak_space"""
return render(request, 'speak_space/index.html')
def categories(request):
"""Show all categories."""
categories = Category.objects.order_by('date_added')
context = {'categories': categories}
return render(request, 'speak_space/categories.html', context)
def category(request, category_id):
"""Show a single category(tribe) and all of its topics(convos)."""
category = Category.objects.get(id=category_id)
topics = category.topic_set.order_by('-date_added')
context = {'category': category, 'topics': topics}
return render(request, 'speak_space/category.html', context)
def topics(request):
"""Show all topics within a category(tribe)."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'speak_space/topics.html', context)
def topic(request, topic_id):
"""Show a single topic(convo/post) and all of it's replies."""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'speak_space/topic.html', context)
def new_topic(request):
"""Add a new conversation topic."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('speak_space:topics')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'speak_space/new_topic.html', context)
and heres my url file:
from django.urls import path
from . import views
app_name = 'speak_space'
urlpatterns = [
# Home page
path('', views.index, name='index'),
# Page that shows all categories
path('categories/', views.categories, name='categories'),
# Profile page for a category
path('categories/<int:category_id>/', views.category, name='category'),
# Page that shows all topics.
path('topics/', views.topics, name='topics'),
# Detail page for a single topic(conversation)
# Where you go after you click on someones post
path('topics/<int:topic_id>/', views.topic, name='topic'),
# Page for adding a new conversation topic
path('new_topic/', views.new_topic, name='new_topic'),
]
and my forms page:
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
Your new_topic view doesn't return anything in case of GET request. You shoud make return statement common:
def new_topic(request):
"""Add a new conversation topic."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('speak_space:topics')
# The changes are here, now return will work for all requests types POST and GET
context = {'form': form}
return render(request, 'speak_space/new_topic.html', context)
Deindent the following lines
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'speak_space/new_topic.html', context)
inside your new_topic method.
I want to use ajax in comments and reply sections of my blog application. In function based view everything is working fine, but I want to do it class based view.
***My function based view***
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
comments = Comment.objects.filter(post=post, reply=None).order_by('-id')
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
content = request.POST.get('content')
reply_id = request.POST.get('comment_id')
comment_qs = None
if reply_id:
comment_qs = Comment.objects.get(id=reply_id)
comment = Comment.objects.create(post=post,
user=request.user,
content=content,
reply=comment_qs)
comment.save()
else:
comment_form = CommentForm()
context = {
'title': 'blog',
'post': post,
'comments': comments,
'comment_form': comment_form,
}
if request.is_ajax():
html = render_to_string('blog/comments.html', context, request=request)
return JsonResponse({'form': html})
return render(request, 'blog/post_detail.html', context)
***My class based view***
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
post = get_object_or_404(Post, id=self.object.id)
comments = Comment.objects.filter(post=post, reply=None).order_by('-id')
context['title'] = 'Blog Detail'
context['comments'] = comments
context['comment_form'] = self.get_form()
return context
def post(self, request, *args, **kwargs):
#if request.user.is_authenticated():
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
content = request.POST.get('content')
reply_id = request.POST.get('comment_id')
comment_qs = None
if reply_id:
comment_qs = Comment.objects.get(id=reply_id)
comment = Comment.objects.create(post=self.object,
user=request.user,
content=content,
reply=comment_qs)
comment.save()
#return HttpResponseRedirect(self.object.get_absolute_url())
else:
return self.form_invalid(form)
if request.is_ajax():
html = render_to_string('blog/comments.html', context, request=request)
return JsonResponse({'form': html})
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.kwargs['pk'], })
In my class based view in if request.is_ajax():
html = render_to_string('blog/comments.html', context,request=request)
return JsonResponse({'form': html})
part it shows the error that context is not defined. So how to fix this problem or how to include it in function, since I have already context.
There is no problem in my function based view code it is working as expected.
It's because. You use context variable but it not defined yet.
if request.is_ajax():
html = render_to_string('blog/comments.html', context, request=request)
return JsonResponse({'form': html})
Try this this get the context:
context = self.get_context_data(object=self.object)
You can see this by tracing the source code of DetailView, you'll see that DetailView inheriting from BaseDetailView and you will found this script to get the context
Hope this helpful!
in this method:
def post(self, request, *args, **kwargs):
...
...
if request.is_ajax():
html = render_to_string('blog/comments.html', context, request=request)
return JsonResponse({'form': html})
simple, you use context, but its not defined. Either pass it as parameter or define it somewhere as a variable