Django Improved UpdateView? - django

I have this UpDateView class and I need just author of article can edit the blog .I had the solution for the CreateView class(using def Form_valid) but it doesn't work for UpdateView class :::
class ArticleUpdateView(LoginRequiredMixin,UpdateView):
model = models.Article
template_name = 'article_edit.html'
fields = ['title','body']
login_url = 'login'
class ArticleCreateView(LoginRequiredMixin,CreateView):
model = models.Article
template_name = 'article_new.html'
fields = ['title','body',]
login_url='login'
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)

You can override the get_object method in your view class:
class ArticleUpdateView(LoginRequiredMixin,UpdateView):
model = models.Article
template_name = 'article_edit.html'
fields = ['title','body']
login_url = 'login'
def get_object(self, *args, **kwargs):
article = super().get_object(*args, **kwargs)
if article.author != self.request.user:
raise PermissionDenied('You should be the author of this article.')
return article

Related

Formset not submitting uploaded picture?

I have a view to allow courses to created by users on my app, which am currently having a challenge uploading a course cover(pic) when users create a new course, when course is created all the fields of the course detail get submitted to the data base except the picture data, why is the picture not being submitted, can some one please help me. as this has been a challenge for me for the past week!
This is my view to create a new course!.
class OwnerMixin(object):
def get_queryset(self):
qs = super(OwnerMixin, self).get_queryset()
return qs.filter(owner=self.request.user)
class OwnerEditMixin(object):
def form_valid(self, form):
form.instance.owner = self.request.user
return super(OwnerEditMixin, self).form_valid(form)
class OwnerCourseMixin(OwnerMixin):
model = Course
fields = ['subject', 'title', 'slug','overview','pic']
success_url = reverse_lazy('courses:manage_course_list')
class OwnerCourseEditMixin(OwnerCourseMixin):
fields = ['subject', 'title','slug', 'overview','pic']
success_url = reverse_lazy('courses:manage_course_list')
template_name = 'manage/module/formset.html'
class ManageCourseListView(OwnerCourseMixin,ListView):
template_name ='courses/course_list.html'
class CourseCreateView(OwnerCourseEditMixin,OwnerEditMixin,CreateView,):
pass
permission_required = 'courses.add_course'
class CourseModuleUpdateView(TemplateResponseMixin, View):
template_name = 'manage/module/formset.html'
course = None
def get_formset(self, data=None,):
return ModuleFormSet(instance=self.course,data=data,)
def get_form(self, data=None):
return RequirementFormset(instance=self.course,data=data)
def get_forms(self, data=None):
return WhatYouWillLearnFormset(instance=self.course,data=data)
def dispatch(self, request, pk):
self.course = get_object_or_404(Course,id=pk,owner=request.user)
return super(CourseModuleUpdateView, self).dispatch(request, pk)
def get(self, request, *args, **kwargs):
formset = self.get_formset()
form = self.get_form()
forms = self.get_forms()
return self.render_to_response({'course':self.course,
'formset':formset,'form':form,'forms':forms,})
def post(self, request, *args, **kwargs):
formset = self.get_formset(data=request.POST,)
form = self.get_form(data=request.POST,)
forms = self.get_forms(data=request.POST,)
if formset.is_valid():
formset.save()
if form.is_valid():
form.save()
if forms.is_valid():
forms.save()
return redirect('courses:manage_course_list')
return self.render_to_response({'course': self.course,
'formset':formset,})
Here is my model, am using formset to create course and also
class Course(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL,\
related_name='courses_created', on_delete=models.CASCADE)
subject = models.ForeignKey(Subject,related_name='courses', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
pic = models.ImageField(upload_to="course_pictures", blank=True,null=True)
slug = models.SlugField(max_length=200, unique=True,blank=True)
overview = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
students = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='courses_joined',blank=True)
class Meta:
ordering = ['-created']
def __str__(self):
return self.title
Here is my formset
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ['pic']
ModuleFormSet = inlineformset_factory(Course,
Module,
fields=['title','description',],
extra=2,
can_delete=True)
RequirementFormset = inlineformset_factory(Course,
Requirement,
fields=['requirements'],
extra=4,can_delete=True)
WhatYouWillLearnFormset = inlineformset_factory(Course,
WhatYouWillLearn,
fields=['hightlights'],
extra=4,can_delete=True)
I think the attribute enctype="multipart/form-data" is missing in the form tag in the template file.
<form method="post" enctype="multipart/form-data">
...
...
</form>

No post found matching the query

views.py
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
urls.py
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail')
When I go to 127.0.0.1:8000/post/1, it says no post found matching the query
Screenshot
In Views.py
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
lookup_field = 'id'
def get_object(self, *args, **kwargs):
kwargs = self.kwargs
kw_id = kwargs.get('id')
return Post.objects.get(id=kw_id)

django authorization in class-based view

I'm trying to restrict access of CRUD pages to the owners, but I can't find the class-based view equivalent of "if request.user != post.author raise Http404". Thx for your time.
models.py
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', args=[str(self.id)])
views.py
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'body']
template_name = 'article_edit.html'
login_url = 'login'
I tried the following (and many other combination arround those lines), but it isn't working.
def get(self, request, *args, **kwargs):
if self.request.user == self.obj.author:
raise Http404()
Youn can do something like this:-
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ['title', 'body']
template_name = 'article_edit.html'
login_url = 'login'
def get(self, request, *args, **kwargs):
self.obj = self.get_object()
if self.request.user != self.obj.author:
raise Http404()
return super(ArticleUpdateView, self).get(request, *args, **kwargs)
I think you can override the get_queryset method to achieve this. For example:
class ArticleUpdateView(...):
def get_queryset(self):
queryset = super(ArticleUpdateView, self).get_queryset()
return queryset.filter(author = self.request.user)
So, when a user tries to update an post which is not created by him, then he will not be able to get it because will not be able find the post object in Queryset provided by get_queryset method. For details, please SingleObjectMixin which is later sub-classed by UpdateView. FYI you don't need to override the get method for this implementation.

Widgets in inlineformset_factory

Hey i managed to make a inlineformset_factory but my widget in the Parent Model are not working although i have specified them in the ModelForm .
My forms.py :
class PostForm(forms.ModelForm):
post = forms.CharField(widget=CKEditorWidget())
class Meta:
model = Post
fields = ['title', 'author','picture','post','draft','publish']
class PostVocabForm(forms.ModelForm):
class Meta:
model = PostVocab
exclude = ()
PostVocabInlineFormSet = inlineformset_factory(
Post,
PostVocab,
extra=1,
exclude=(),
)
My CKEditorWidget is not working ....
My views.py:
class PostPostVocabCreate(CreateView):
model = Post
form_class = PostForm
# fields = ['title', 'author', 'picture', 'post', 'draft', 'publish']
def get_redirect_url(self, pk):
return reverse_lazy('blog:post_detail',
kwargs={'slug': pk},
)
def get_context_data(self, **kwargs):
data = super(PostPostVocabCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['postvocabs'] = PostVocabInlineFormSet(self.request.POST)
else:
data['postvocabs'] = PostVocabInlineFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
postvocabs = context['postvocabs']
with transaction.atomic():
self.object = form.save()
if postvocabs.is_valid():
postvocabs.instance = self.object
postvocabs.save()
return super(PostPostVocabCreate, self).form_valid(form)
I guess that my widget from the Parent model (Post) was overwritten while using a inlineformset_factory...
You can set widgets inside of inlineformset_factory.
PostVocabInlineFormSet = inlineformset_factory(
Post,
PostVocab,
extra=1,
exclude=(),
widgets={'post': CKEditorWidget()
)
From Django docs...inlineformset_factory uses modelformset_factory and passes most of its arguments to modelformset_factory. This means you can use the widgets parameter in much the same way as passing it to modelformset_factory.
AuthorFormSet = modelformset_factory(
... Author, fields=('name', 'title'),
... widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})})`

Django - DetailView with FormMixin and initial

I have DetaiView for my post and I want to use a form in this view so I decided to use DetailView with FormMixin. I need to set some initial to this form and I don't know how to do it. Here is my code:
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE)
name = models.CharField("Nick", max_length=80)
email = models.EmailField()
body = models.TextField("Body")
created = models.DateTimeField("created", auto_now_add=True)
updated = models.DateTimeField("Updated", auto_now=True)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = (
"name",
"email",
"body"
)
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
def get_success_url(self):
return reverse("post-detail", kwargs={"slug": self.object.slug})
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context["form"] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
return super().form_valid(form)
So I want to post in CommentForm to post of this DetailView. I hope you understand :D.
Thanks in advance for the help!
With FormMixin you can specify form's initial using initial attribute:
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
initial={'name': 'John'}
Or get_initial method:
def get_initial(self):
return {"post": self.get_object() }