I have a method on my model to change an object from being published to unpublished. The redirect works alright but in my database, nothing happens. If the object is published it remains so with no changes to it when the button is clicked to unpublish the object(blog post article)
This is the model and the method
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
published = models.BooleanField(default=False, null=True, blank=True)
def unpublish(self):
self.published == False
self.save()
My view
def unpublish_post(request, slug):
post = get_object_or_404(Post, slug=slug)
post.unpublish
return redirect('dashboard')
Edit 2: My view
#require_http_methods(['POST', 'DELETE'])
def unpublish_post(request, slug):
post = get_object_or_404(Post, slug=slug)
if post.published == True:
post.unpublish()
return redirect('dashboard')
else:
return messages.warning(request, "Post already not published")
return redirect('dashboard')
My urls.py
path('unpublish-post/<slug>/', unpublish_post, name='unpublish-post'),
EDIT 1: Now I updated the view logic with:
def unpublish_post(request, slug):
post = get_object_or_404(Post, slug=slug)
if post.published == True:
post.unpublish()
return redirect('dashboard')
else:
return messages.warning(request, "Post already not published")
There are three minor mistakes:
you set a variable with a single equal sign (=), not a double equal sign (==);
you should call the .unpublish() method; and
you should only allow to access this view in a POST or DELETE request.
In your model, we thus rewrite the logic to:
class Post(models.Model):
# …
def unpublish(self):
self.published = False
self.save()
and in the view, we call the method and restrict access to a POST and/or a DELETE request:
from django.views.decorators.http import require_http_methods
#require_http_methods(['POST', 'DELETE'])
def unpublish_post(request, slug):
post = get_object_or_404(Post, slug=slug)
post.unpublish()
return redirect('dashboard')
The client thus then will need to make a POST/DELETE request, not a GET request. The template thus should look like:
<form method="POST" action="{% 'unpublish-post' post.slug %}">
<input type="submit" value="unpublish">
</form>
Related
learning python through the python crash course book. Having this issue where somehow it says that there is no attribute 'owner' for each blogpost when there seems to be one? Would appreciate any guidance, cheers all!
Added to the very bottom of settings.py
#MY SETTINGS
LOGIN_URL = 'users:login'
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class BlogPost(models.Model):
title = models.CharField(max_length=50)
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
This is the code when i run django shell to see the owner associated with each blogpost
from blogs.models import BlogPost
for a in BlogPost.objects.all():
print(a, a.owner)
My first post! aaaaaa ll_admin
Second blog post ll_admin
No season 2 in product ll_admin
ll_admin
is this the tutle ll_admin
ssd ll_admin
ssaaa ll_admin
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import BlogPost
from .forms import BlogPostForm
# Create your views here.
def index(request):
"""The home page for blogs"""
return render(request, 'blogs/index.html')
#login_required
def posts(request):
"""Show all blogposts"""
posts = BlogPost.objects.filter(owner=request.owner).order_by('date_added')
context = {'posts': posts}
return render(request, 'blogs/posts.html', context)
#login_required
def new_post(request):
"""Add a new blogpost"""
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:posts')
#Display a blank or invalid form
context = {'form': form}
return render(request, 'blogs/new_post.html', context)
#login_required
def edit_post(request, post_id):
"""Edit existing post"""
post = BlogPost.objects.get(id=post_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:posts')
#return redirect('blogs:posts', post_id=post.id)
context = {'post':post, 'form':form}
return render(request, 'blogs/edit_post.html', context)
This is all that I have edited to add in the login functions, cant seem to spot the error. Thank you for helping!
In your posts view:
#login_required
def posts(request):
"""Show all blogposts"""
posts = BlogPost.objects.filter(owner=request.owner).order_by('date_added') # here
context = {'posts': posts}
return render(request, 'blogs/posts.html', context)
The request object is storing 2 values:
The instance of the currently logged in user under the name user (changes to AnonymousUserObject instance when logged out)
auth depending on the type of authentication used
You are calling request.owner and obviously getting an error because a request object has no owner attribute, change the marked line line this:
posts = BlogPost.objects.filter(owner=request.user).order_by('date_added')
And it should work.
Once a user had logged into my site he could write a post and update it.
Then I was making progress in adding functionality which allowed people to make comments. I was at the stage where I could add comments from the back end and they would be accurately displayed on the front end.
Now when I try and update posts I get an error message.
I assume it is because there is a foreign key linking the comments class to the post class. I tried Googling the problem and looking on StackOverflow but I wasn't entirely convinced the material I was reading was remotely related to my problem. I am struggling to fix the issue because I barely even understand / know what the issue is.
models.py
def save(self, *args, **kwargs):
self.url= slugify(self.title)
super().save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', kwargs={'slug': self.slug})
class Comment(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
class Meta:
ordering = ['created_on']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
def get_absolute_url(self):
return reverse('article_detail', kwargs={'slug': self.slug})
views.py
def post_detail(request, pk):
template_name = 'post_detail.html'
comments = Comment.objects.filter(post=pk ,active=True)
post = Post.objects.get(pk=pk)
new_comment = None
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})
You have no field in your model Comment called slug (aka SlugField) so it's not going to work.
The save method should be defined in the class also - see here. You also don't need the get_absolute_url under save() either.
It was all fine just an hour ago but now when I try to add data to form and save it is running the exception part
Before the post method was called and the form used to save and redirect
But I cannot figure out why the exception part is running.
I haven't change anything
In console it is giving "POST /forum/topics/9/reply HTTP/1.1" 200 523
class NewPostView(generic.CreateView):
form_class = NewPostForm
template_name = 'forum/new_post.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if 'pk' not in context:
context['pk'] = self.kwargs['pk']
return context
def post(self, request, *args, **kwargs):
topic = get_object_or_404(Topic, pk=self.kwargs['pk'])
form = self.get_form()
try:
if form.is_valid():
post = form.save(commit=False)
post.topic = topic
post.created_by = User.objects.get(pk=request.user.pk)
post.save()
return redirect('topic-detail', pk=topic.pk)
except Exception as e:
messages.warning(request, "Failed To Create. Error: {}".format(e))
messages.warning(request, "Failed To Create Check Errors")
args = {'form': form, 'pk': self.kwargs['pk']}
#print(args)
return render(request, self.template_name, args)
Edit:Now I can see User matching query does not exist. error
Edit2 : Thank you all for the response. I found the error that I had made, I was actually using django.contrib.auth.user in the login while I was using my own User model.
When I change created_by = models.ForeignKey("auth.User"), in the model then it worked fine.
My guess is that you are no longer logged into the application, so request.user.pk is None.
The easiest way to ensure only a logged in user can access your view is to add the LoginRequiredMixin:
from django.contrib.auth.mixins import LoginRequiredMixin
class NewPostView(LoginRequiredMixin, generic.CreateView):
I have changed your views.py
class NewPostView(View):
.....add your template_name, form_class and get function....
def post(self, request, *args, **kwargs):
topic = get_object_or_404(Topic, pk=self.kwargs['pk']) # here you can directly add pk=pk and def post(self, request, pk)
form = self.form_class(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.topic = topic
post.created_by = self.request.user
post.save()
return redirect('topic-detail', pk=topic.pk)
else:
messages.error(request, "Error Message")
return render(request, self.template_name, {'form':form})
For not logged in user, you can follow Daniel's answer and/or in your template you can add,
{% if user.is_authenticated %}
html
{% else %}
html
{% endif %}
I am attempting to reuse my create form (EntryForm) for editing a model in Django. My Entry model has a unique slug that is generated on save. This works fine when creating an Entry, but shows the following error when I attempt to edit it:
Entry with this Slug already exists.
I saw several similar questions, but most were failing to set instance= when instantiating the form. I'm pretty sure I'm doing that part correctly.
I've removed other model fields from the code below for clarity.
Here is my model:
class Entry(models.Model):
title = models.CharField(max_length=128, blank=True)
slug = models.SlugField(unique=True, blank=True)
def save(self, *args, **kwargs):
if not self.title:
self.title = self.date.strftime('%B %-d, %Y')
self.slug = slugify(self.title)
super(Entry, self).save(*args, **kwargs)
My view:
def edit_entry(request, entry_slug):
entry = get_object_or_404(Entry, slug=entry_slug)
form = EntryForm(instance=entry, label_suffix='')
if request.method == 'POST':
form = EntryForm(request.POST, instance=entry, label_suffix='')
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print(form.errors)
return render(request, 'journal/entry/form.html', {'form': form})
My form:
class EntryForm(forms.ModelForm):
title = forms.CharField(required=False, max_length=128, label="Title (defaults to date)")
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
class Meta:
model = Entry
exclude = ()
Any ideas?
I did finally figure this out.
The issue was stemming from the fact that I was reusing my create template for the edit form, but forgot to set the form action dynamically depending on which action I desired. So, my 'edit' form was rendering correctly, but actually submitting via the 'create' action.
Thanks to those who commented and to what ultimately led me to debugging the problem, this handy code snippet equivalent to Ruby's binding.pry:
import code; code.interact(local=dict(globals(), **locals()))
I did also take #xyres's advice and remove the slug from my form, as it was unnecessary.
New form:
class EntryForm(forms.ModelForm):
title = forms.CharField(required=False, max_length=128, label="Title (defaults to date)")
class Meta:
model = Entry
exclude = ['slug']
New final line of edit_entry():
return render(request, 'journal/entry/form.html', {'form': form, 'entry': entry})
Form action:
{% if entry %}
<form id="entry_form" method="post" action="/journal/entry/{{ entry.slug }}/edit">
{% else %}
<form id="entry_form" method="post" action="/journal/new_entry/">
{% endif %}
I am making django project and have one problem with uploading images in forms.
So, I tried to add one object with image, in admin this is working, but in form on site - not.
My views:
def newbook(request, user_id):
form = BookAdd(request.POST or None)
if form.is_valid():
book = form.save(commit=False)
book.author = get_object_or_404(User, pk=user_id)
book.save()
return redirect('../%s' % book.id)
return render(request, 'userbook/newbook.html', {'form': form})
My model:
class Book(models.Model):
"""Book is a compilation of sections with subjects."""
author = models.ForeignKey(AUTH_USER_MODEL)
name = models.CharField(max_length=100)
description = models.CharField(blank=True, max_length=256)
cover = models.ImageField(upload_to='img/bookcovers')
def __str__(self):
return self.name
My form:
class BookAdd(ModelForm):
class Meta:
model = Book
fields = ('name', 'description', 'cover')
When I add new book, I get an error "the field is required", maybe for field of cover, but image added. This work honestly on local server, but don't work on pythonanywhere.com
You have to change code
form = BookAdd(request.POST or None)
to
form = BookAdd(request.POST,request.FILES)
and your form should have enctype="multipart/form-data"
<form action="." method="post" enctype="multipart/form-data">