Django try exception in class view - python

im only student so please bear with me. I already posted another topic about this but in function view. now i want to how do i convert this try exception to a class view and also add that comment form.
here's my views.py def
def BookDetail(request, id):
most_recent = Book.objects.order_by('-timestamp')[:3]
book= get_object_or_404(Book, id=id)
form = CommentForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
form.instance.user = request.user
form.instance.post = book
form.save()
return redirect(reverse("book-detail", kwargs={
'id': book.pk
}))
if request.user.is_anonymous:
user_membership = None
else:
try:
user_membership = Customer.objects.get(user=request.user)
except Customer.DoesNotExist:
user_membership = None
context = {
'user_membership': user_membership,
'form': form,
'book': book,
'most_recent': most_recent,
}
return render(request, 'catalog/book_detail.html', context)
here is my new class view
class BookDetailView(NeverCacheMixin, generic.DetailView):
model = Book
UPDATE POST
here's my models.py..
class Book(models.Model):
slug = models.SlugField(unique=True, help_text="Enter BIC Code", null=True)
title = models.CharField(max_length=200) #more fields after this
timestamp = models.DateTimeField(default=timezone.now)
activeReference = models.ManyToManyField(Membership)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('book-detail', kwargs={'slug': self.slug})
#property
def get_comments(self):
return self.comments.all().order_by('-timestamp')
#property
def pages(self):
return self.page_set.all()
class Page(models.Model):
slug = models.SlugField(max_length=50)
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
preview = models.FileField(upload_to='book_content', validators=[pdf_file_extension], help_text="PDF File Only")
def __str__(self):
return self.slug
def get_absolute_url(self):
return reverse('page-detail',
kwargs={
'book_slug': self.book.slug,
'page_slug': self.slug
})
here's my URL pattern
urlpatterns = [
path('', views.index, name='index'),
path('books/', views.BookListView.as_view(), name='book-list'),
path('book-detail/<slug>', views.BookDetailView.as_view(), name='book-detail'),
path('book-detail/<book_slug>/<page_slug>', views.PageDetailView.as_view(), name='page-detail'),
path('search/', views.Search, name='search'),
]
and my book_detail.html
{% for content in book.pages %}
Read
{% endfor %}
{% else %}

CBV are good when you need inheritance or need to deal with different HTTP methods to have the same route. If that is not the case, a FBV is a better choice. That being said, for what you are trying to do, you should just a FormView that will handle everything about a post and forms.
Something like:
class BookViewSet(FormView):
form = CommentForm
template_name = 'catalog/book_detail.html'
def form_valid(self, form):
self.send_mail(form.cleaned_data)
return super(BookViewSet, self).form_valid(form)

If you are handling forms, you should be using CreateView or FormView not DetailView. Here is an implementation example, as you can see is a little complex for someone new to Django:
from django.views.generic import CreateView
class BookDetailView(NeverCacheMixin, CreateView):
form_class = CommentForm
template_name = 'catalog/book_detail.html'
def dispatch(self, request, *args, **kwargs):
book_slug = self.kwargs.get('slug') # url variables are stored in self.kwargs
self.book = get_object_or_404(Book, slug=book_slug) # attach book to your view
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
# context data passed to template
kwargs['book'] = self.book
kwargs['most_recent'] = Book.objects.order_by('-timestamp')[:3]
if request.user.is_anonymous:
user_membership = None
else:
try:
user_membership = Customer.objects.get(user=self.request.user)
except Customer.DoesNotExist:
user_membership = None
kwargs['user_membership'] = user_membership
return super().get_context_data(**kwargs)
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.post = self.book
instance.save()
return redirect(self.book.get_absolute_url())

Related

The view post.views.view didn't return an HttpResponse object. It returned None instead

I want to create a new post using PostCreateView and go to the details page of the new post in the next step, but I get this error:
(The view post.views.view didn't return an HttpResponse object. It returned None instead.)
views
class PostDetailView(View):
"""see detail post"""
def get(self, request, post_id, post_slug):
post = Post.objects.get(pk=post_id, slug=post_slug)
return render(request, "post/detail.html", {"post": post})
class PostCreateView(LoginRequiredMixin, View):
form_class = PostCreateUpdateForm
def get(self, request, *args, **kwargs):
form = self.form_class
return render(request, "post/create.html", {"form": form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.slug = slugify(form.cleaned_data["body"][:20])
new_post.user = request.user
new_post.save()
messages.success(request, "you created a new post", "success")
return redirect("post:post-detail", new_post.id, new_post.slug)
models
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
slug = models.SlugField()
img = models.ImageField(upload_to="%Y/%m/%d/")
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
urls
app_name = 'post'
urlpatterns = [
path('', views.BlogView.as_view(), name="home"),
path('detail/<int:post_id>/<slug:post_slug>/', views.PostDetailView.as_view(), name="post-detail"),
path('delete/<int:post_id>/', views.PostDeleteView.as_view(), name="post-delete"),
path('update/<int:post_id>/', views.PostUpdateView.as_view(), name="post-update"),
path('create/', views.PostCreateView.as_view(), name="post-create"),
]
In case the form is not valid, you should rerender the template with the form, so:
class PostCreateView(LoginRequiredMixin, View):
form_class = PostCreateUpdateForm
def get(self, request, *args, **kwargs):
form = self.form_class
return render(request, "post/create.html", {"form": form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.slug = slugify(form.cleaned_data['body'][:20])
new_post.user = request.user
new_post.save()
messages.success(request, 'you created a new post', 'success')
return redirect('post:post-detail', new_post.id, new_post.slug)
return render(request, 'post/create.html', {'form': form})
But you are implementing a lot of boilerplate code here. What you here do is implementing a CreateView [Django-doc]:
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic import CreateView
class PostCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
form_class = PostCreateUpdateForm
template_name = 'post/create.html'
success_message = 'you created a new post'
def form_valid(self, form):
form.instance.slug = slugify(form.cleaned_data['body'][:20])
form.instance.user = request.user
return super().form_valid()
def get_success_url(self):
return reverse('post:post-detail', args=(new_post.id, new_post.slug))
Your "post" method in PostCreateView only returns a response if the form is valid. If it isn't valid, it will return None, causing an error.
Modify that method so it looks like this:
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.slug = slugify(form.cleaned_data["body"][:20])
new_post.user = request.user
new_post.save()
messages.success(request, "you created a new post", "success")
return redirect("post:post-detail", new_post.id, new_post.slug)
return render(request, "post/create.html", {"form": form})

How would I pass request.user into my form?

I'm trying to create a posts form that lets the user create posts on my site. I've been stuck on how to pass request.user into the fields "author" and "participants". Could anybody help?
Here is my view:
def home(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
form.save()
return redirect('')
My model:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
body = models.TextField()
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
participants = models.ManyToManyField(User, related_name="participants", blank=True)
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created"]
def __str__(self):
return self.body
And my form:
from django.forms import ModelForm
from .models import Post
class PostForm(ModelForm):
class Meta:
model = Post
fields = '__all__'
For the author you can use this.
def home(request):
if request.method == "POST":
form = PostForm(request.POST)
form.author = request.user
if form.is_valid():
form.save()
return redirect('')
For the participants you can wait until the new Post is created an then add the User
if form.is_valid():
new_post = form.save()
new_post.participants.add(user)
I have an example with class based views where is easy to accomplish.
class PostCreateView(CreateView):
template_name = 'Post/article_create.html'
form_class = ArticleModelForm
queryset = Article.objects.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form_journal'] = JournalModelForm
return context
def dispatch(self, request, *args, **kwargs):
if request.method == 'POST':
want_redirect = request.POST.get('want_redirect')
if not want_redirect:
self.success_url = reverse_lazy('article:article-create')
return super(ArticleCreateView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.user = self.request.user //I think this is what you are trying to do
return super().form_valid(form)

Django: WARNING - Method Not Allowed (POST)

I know what above error means. Seems I can't handle form when it is posted. I can do it in function based views, but in class based views I am tad lost.
I am creating a comment app and here is forms.py content in comment app:
class CommentForm(forms.Form):
content_type = forms.CharField(widget=forms.HiddenInput)
object_id = forms.CharField(widget=forms.HiddenInput)
body = forms.CharField(widget=forms.Textarea)
then in DetailView of the blog app, I handled it this way:
class BlogDetail(DetailView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get_context_data(self, *args, **kwargs):
obj = self.get_object()
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.filter_by_instance(obj)
""" comment form """
initial_data = {
'content_type': obj.get_content_type,
'object_id': obj.id
}
if self.request.method == 'POST':
form = CommentForm(self.request.POST, initial=initial_data)
if form.is_valid():
c_type = form.cleaned_data.get('content_type')
content_type = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
new_comment, created = Comment.objects.get_or_create(
user=self.request.user,
content_type=content_type,
object_id=object_id,
body=body
)
else:
form = CommentForm(initial=initial_data)
context['comment_form'] = form
return context
albeit I passsed form = CommentForm(self.request.POST, initial=initial_data) but there sounds something is going wrong, Can anyone help? Thank you
edited:
class BlogDetail(DetailView, FormView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
form_class = CommentForm
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get_initial(self):
obj = self.get_object()
return {
'content_type': obj.get_content_type,
'object_id': obj.id
}
def form_valid(self, form):
c_type = form.cleaned_data.get('content_type')
content_type = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
Comment.objects.create(
user=self.request.user,
content_type=content_type,
object_id=object_id,
body=body
)
edit 2:
Can anyone spot the error with this approach:
class BlogDetail(DetailView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
form_class = CommentForm
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get(self, request, *args, **kwargs):
obj = self.get_object()
initial_data = {
'content_type': obj.get_content_type,
'object_id': obj.id
}
print("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", obj.get_content_type)
form = self.form_class(initial=initial_data)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
c_type = form.cleaned_data.get('content_type')
content_type_2 = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
Comment.objects.create(
user=request.user,
content_type=content_type_2,
object_id=object_id,
body=body,
)
return render(request, self.template_name, {'form': form})
Posts are handled by the post method of the class-based view:
class BlogDetail(DetailView):
# ...
def post(self, request, *args, **kwargs):
# all your form processing
Django ships with several views, that already provide various hooks into the form handling process, e.g. FormView, that you could leverage instead:
class BlogDetail(DetailView, FormView):
form_class = CommentForm
def form_valid(self, form):
c_type = form.cleaned_data.get('content_type')
# ...
def get_initial(self):
obj = self.get_object()
return {
'content_type': obj.get_content_type,
'object_id': obj.id
}
# ....
By default, the form is passed as "form" into the context.
To allow post requests to your view, write a function def post(self, request, *args, **kwargs) which will receive the post request. If you want to handle this as you would handle get, redirect it to the get function
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
You don't need the DetailView. You can simply use CreateView. I think you have everything overridden correctly to be able to ditch DetailView, except maybe get_form_kwargs().
However...
I usually approach this differently, cause it's confusing and very hacky. Instead, you add the form via get_context_data() to the DetailView and then in the template post to /blog/{id}/comment/create, where you have the CreateView. That makes things a lot simpler.

Django-Taggit saving tags from a model field

I am building a post editor for my website. One of the fields I can edit is for tags. However when I save the post everything updates correctly and no errors are thrown but any changes I made to the tags field are not saved.
# Views.py
class EditPostView(UpdateView):
form_class = EditPostForm
model = Post
template_name = 'myapp/editpost.html'
def get(self, request, pk):
if (not request.user.is_superuser):
return HttpResponseForbidden()
post = Post.objects.get(id=pk)
if (post is None):
return HttpResponseNotFound()
form = self.form_class(instance=post)
return render(request, self.template_name, {'form': form, 'post': post})
def post(self, request, pk):
if (not request.user.is_superuser):
return HttpResponseForbidden()
post = Post.objects.get(id=pk)
if (post is None):
return HttpResponseNotFound()
form = self.form_class(request.POST, instance=post)
if (form.is_valid()):
post.title = form.cleaned_data['title']
post.content_type = form.cleaned_data['content_type']
post.screenshot = form.cleaned_data['screenshot']
post.tags = form.cleaned_data['tags']
post.body = form.cleaned_data['body']
post.nsfw = form.cleaned_data['nsfw']
post.allow_comments = form.cleaned_data['allow_comments']
post.display_edited = form.cleaned_data['display_edited']
post.files = form.cleaned_data['files']
post.date_edited = datetime.now()
post.save()
return redirect('/posts/' + str(post.id))
else:
return HttpResponseNotFound()
return HttpResponseNotFound()
#Forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content_type', 'screenshot', 'body', 'tags', 'nsfw', 'allow_comments', 'files']
widgets = {
'screenshot': forms.TextInput(attrs={'placeholder': 'URL...'}),
'tags': TagWidget(),
}
labels = {
'files': 'Attachments',
}
# Models.py
class Post(models.Model):
title = models.CharField(max_length=256)
disclaimer = models.CharField(max_length=256, blank=True)
BLOGS = 'blogs'
APPLICATIONS = 'applications'
GAMES = 'games'
WEBSITES = 'websites'
GALLERY = 'gallery'
PRIMARY_CHOICES = (
(BLOGS, 'Blogs'),
(APPLICATIONS, 'Applications'),
(GAMES, 'Games'),
(WEBSITES, 'Websites'),
)
content_type = models.CharField(max_length=256, choices=PRIMARY_CHOICES, default=BLOGS)
screenshot = models.CharField(max_length=256, blank=True)
tags = TaggableManager()
body = RichTextField()
date_posted = models.DateTimeField(default=datetime.now)
date_edited = models.DateTimeField(blank=True, null=True)
visible = models.BooleanField(default=True)
nsfw = models.BooleanField()
display_edited = models.BooleanField(default=False)
allow_comments = models.BooleanField(default=True)
files = models.ManyToManyField(File, blank=True)
def __str__(self):
if (self.visible == False):
return '(Hidden) ' + self.title + ' in ' + self.content_type
return self.title + ' in ' + self.content_type
You're doing a lot of unnecessary work here. You're not leveraging the power of the UpdateView. This is all you should need. It will call form.save() and everything for you
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404
from django.views.generic.edit import UpdateView
class EditPostView(UpdateView):
form_class = EditPostForm
model = Post
template_name = 'myapp/editpost.html'
def dispatch(self, *args, **kwargs):
if not self.request.user.is_superuser:
return HttpResponseForbidden()
return super(EditPostView, self).dispatch(*args, **kwargs)
def get_object(self, queryset=None):
return get_object_or_404(Post, id=self.kwargs['id'])
def get_success_url(self):
return '/posts/{0}'.format(self.object.id)
Edit: bonus points if you get rid of get_success_url and add a get_absolute_url method to the Post model.
class Post(models.Model):
...
def save(self, *args, **kwargs):
self.date_edited = datetime.now()
super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
return '/posts/{0}'.format(self.id)

cant pass multiple models to my DetailsView using generic views

I have tried passing Album objects and song objects to my DetailView trying to get the DetailView working with both Album models and Song model. But anytime I try deleting a song my code cant seem to get the Song.objects.all(). I get a cannot find a song object in query or something like that
here are my url patterns:
urlpatterns =
[
/music/
url(r'^$', views.IndexView.as_view(), name='index'),
# /music/album_id/
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^register/$', views.UserFormView.as_view(), name='register'),
url(r'album/add/$', views.AlbumCreate.as_view(), name='album-add'),
#/music/album/2/
url(r'album/(?P<pk>[0-9]+)/$', views.AlbumUpdate.as_view(), name='album-update'),
#/music/album/add/delete/
url(r'^album/(?P<pk>[0-9]+)/delete/$', views.AlbumDelete.as_view(), name='album-delete'),
#/music/album/album_id/add-song/
url(r'^album/(?P<pk>[0-9]+)/add-song/$', views.SongView.as_view(), name='song-add'),
# /music/album_id/song/delete/
url(r'^album/song/delete/(?P<song_id>[0-9]+)/$', views.SongDelete.as_view(), name='song-delete'),
views.py:
class IndexView(generic.ListView):
template_name = 'music/index.html'
context_object_name = 'all_albums'
def get_queryset(self):
return Album.objects.all()
class DetailView(generic.DetailView):
template_name = 'music/detail.html'
model = [Album, Song]
# context_object_name = 'all_songs'
def get_object(self, queryset=Song.objects.all()):
return queryset
def get_queryset(self):
return Album.objects.all()
class AlbumCreate(CreateView):
model = Album
fields = ['artist','album_title', 'genre', 'album_logo']
class AlbumUpdate(UpdateView):
model = Album
fields = ['artist','album_title', 'genre', 'album_logo']
class AlbumDelete(DeleteView):
model = Album
success_url = reverse_lazy('music:index')
class UserFormView(generic.View):
form_class = UserForm
template_name='music/registration_form.html'
#displays a blank form
def get(self, request):
form = self.form_class(request.GET)
return render(request, self.template_name, {'form': form})
#process form data
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user=form.save(commit=False)
#cleand (Normalized) data
username= form.cleaned_data['username']
password=form.cleaned_data['password']
user.set_password(password)
user.save()
#Returns user object if cresentials are correct
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('music:index')
return render(request, self.template_name, {'form': form})
class SongView(CreateView):
# template_name = 'music/song_form.html'
model = Song
fields= ['title', 'album', 'audio_file', 'favorite']
class SongDelete(DeleteView):
model = Song
success_url = reverse_lazy('music:detail')
models.py:
from django.db import models
from django.core.urlresolvers import reverse
class Album(models.Model):
artist=models.CharField(max_length=250)
album_title=models.CharField(max_length=500)
genre=models.CharField(max_length=100)
album_logo=models.FileField()
def get_absolute_url(self):
return reverse('music:detail', kwargs={'pk': self.pk})
def __str__(self):
return self.album_title + '_' + self.artist
class Song(models.Model):
title=models.CharField(max_length=250)
album=models.ForeignKey(Album, on_delete=models.CASCADE)
audio_file= models.FileField()
favorite=models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('music:detail' , kwargs={'pk': self.pk})
def __str__(self):
return self.title
please need help, been o this for a while now **wheww
If you need all songs in your DetailView you should pass them in the context; they are completely separate from the queryset of Albums you need to use to find the current object.
class DetailView(generic.DetailView):
template_name = 'music/detail.html'
model = Album
def get_context_data(self, *args, **kwargs):
context = super(DetailView, self).get_context_data(*args, **kwargs)
context['all_songs'] = Song.objects.all()
return context
(In future, please also post only relevant code and ensure that your indentation is correct.)

Categories

Resources