Pagination in DetailView with context? [django] - django

I made comment and comments in Article(DetailView)
How can I make paginations this comments in DetailView.
I have tried many options but without succes...
VIEWS:
class ArticleDetailView(FormMixin, HitCountDetailView):
template_name = 'news/article_detail.html'
model = Article
count_hit = True
form_class = CommentForm
def get_success_url(self):
pk = self.kwargs["pk"]
slug = self.kwargs['slug']
return reverse_lazy('news:article_detail', kwargs={'pk': pk, 'slug': slug})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = ArticleComment.objects.filter(article_id=self.object.id).all()
context['comments_number'] = ArticleComment.objects.filter(article_id=self.object.id).count()
context['form'] = CommentForm()
return context
def form_valid(self, form):
form.instance.article = self.object
form.instance.author = self.request.user
form.save()
messages.success(request=self.request, message="KOMENTARZ ZOSTAƁ DODANY POPRAWNIE")
return super().form_valid(form)
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)

You can try like this.
from django.core.paginator import Paginator
qs = ArticleComment.objects.filter(article_id=self.object.id).all()
page = self.request.GET.get('page')
context['comments'] = Paginator(qs, 10).get_page(page)
return context

Related

why django inlineformset_factory data not saving in class-based updateview?

inlineformset_factory fields is rendering in my html template. Others fields data saving except inlineformset_factory fields. here is my code:
models.py
class HeaderImage(models.Model):
header_image = models.ImageField()
post = models.ForeignKey(Post, on_delete=models.CASCADE)
froms.py
BlogImageFormSet = inlineformset_factory(Post, # parent form
HeaderImage, # inline-form
fields=['header_image'] ,can_delete=False, extra=1)
#views.py
class BlogUpdateView(PermissionRequiredMixin,UpdateView):
raise_exception = True
permission_required = "blog.change_post"
model = Post
template_name = "blog_update_post.html"
form_class = BlogPost
def get_success_url(self):
self.success_url = 'http://127.0.0.1:8000/blog'
return self.success_url
def get_context_data(self, **kwargs):
context = super(BlogUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['form'] = BlogPost(self.request.POST, instance=self.object)
context['ingredient_form'] = BlogImageFormSet(self.request.POST, instance=self.object)
else:
context['form'] = BlogPost(instance=self.object)
context['ingredient_form'] =BlogImageFormSet(instance=self.object)
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = BlogImageFormSet(self.request.POST)
if (form.is_valid() and ingredient_form.is_valid()):
return self.form_valid(form, ingredient_form)
else:
return self.form_invalid(form, ingredient_form)
def form_valid(self, form, ingredient_form):
self.object = form.save()
ingredient_form.instance = self.object
ingredient_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form):
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
))
#html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.media}}
{{form.as_p}}
{{ingredient_form}}
<button class="btn btn-info">Update</button>
</form>
I am not understanding why data is not saving for inlineformset_factory field. see the picture for better understand
Can you try this?
class BlogUpdateView(PermissionRequiredMixin,UpdateView):
...
def get_context_data(self, **kwargs):
context = super(BlogUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['form'] = BlogPost(self.request.POST, instance=self.object)
# this line below changed
context['ingredient_form'] = BlogImageFormSet(self.request.POST, self.request.FILES, instance=self.object)
else:
context['form'] = BlogPost(instance=self.object)
context['ingredient_form'] =BlogImageFormSet(instance=self.object)
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
# this line below changed
ingredient_form = BlogImageFormSet(self.request.POST, self.request.FILES)
if (form.is_valid() and ingredient_form.is_valid()):
return self.form_valid(form, ingredient_form)
else:
return self.form_invalid(form, ingredient_form)
...
by data you mean the file you are uploading is not getting saved. The image field in your model needs a location to save the file
header_image = models.ImageField(upload_to='some_directory/')

How do I solve 'Model' object has no attribute 'object' for UpdateView for formset handling?

I am currently trying to get past numerous problems I've encountered with formset handling. Here is my latest code...In other issues it would seem that there might be an issue with formset and form_invalid(). I am trying to simply edit formset data that I have saved from a CreateView. It seems as if the data is presenting fine in the view after I save it from CreateView, but then I am not able to use this data or save it without changing anything. This issue seems eerily similar and I tried to use it to a degree but I couldn't get it to work either...Django passing form_valid inline formset context to get_context_data()
My View....
class UpdateTeamView(LoginRequiredMixin,UpdateView):
model = Team
form_class = UpdateTeamForm
template_name = 'update_team.html'
def get_context_data(self, **kwargs):
context = super(UpdateTeamView, self).get_context_data(**kwargs)
if self.request.POST:
context['player_form'] = PlayerFormSet(self.request.POST)
else:
context['player_form'] = PlayerFormSet(instance=self.object)
return context
def get_form_kwargs(self):
kwargs = super(UpdateTeamView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_object(self, queryset=None):
return get_object_or_404(Team, id=self.request.GET.get("dropdown"))
def get(self, request, *args, **kwargs):
dropdown=self.request.GET.get("dropdown")
if dropdown is not None:
if Team.objects.all().distinct():
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
else:
raise Http404
else:
messages.add_message(self.request, messages.INFO, 'Team is required.')
return HttpResponseRedirect(reverse('Company:update_team_by_name'))
def form_valid(self, form):
instance = form.save()
return super(UpdateTeamView, self).form_valid(form)
def form_invalid(self, form):
return self.render_to_response(
self.get_context_data(form=form,
))
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if "cancel" in request.POST:
return HttpResponseRedirect(reverse('Company:company_main_menu'))
else:
team_form = TeamForm(self.request.POST)
player_form = UpdateSavedNewCompanyContactFormSet(self.request.POST)
if player_form.is_valid():
print("works")
return HttpResponseRedirect(reverse('Team:main_menu'))
else:
player_form = UpdateSavedNewCompanyContactFormSet()
print("not working")
print(player_form.errors)
return super(UpdateTeamView, self).form_invalid(player_form)
After a lot of trial and error here was my solution....
def get_context_data(self, **kwargs):
context = super(UpdateTeamView, self).get_context_data(**kwargs)
if self.request.POST:
context['player_form'] = PlayerFormSet(self.request.POST, instance=self.object)
else:
context['player_form'] = PlayerFormSet(instance=self.object)
return context
def get_form_kwargs(self):
kwargs = super(UpdateTeamView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_object(self, queryset=None):
return get_object_or_404(UpdateTeamView, id=self.request.GET.get("dropdown"))
def form_valid(self, form):
data = self.get_context_data()
formset = data['company_contact_form']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
else:
print('formset invalid:', formset.errors)
instance = form.save()
def form_invalid(self, form, *args, **kwargs):
super().form_invalid()

Is this the correct way to use a CreateView in Django with an inlineformset_factory?

I've got one master detail relationship beteen Article and Section, and wish to allow a user to add sections on the same page before creating the Article.
It seems to work, but I'm also very new to Django, and wonder if I'm doing it right. There's a lot going on here, and I wonder if I'm overriding methods correctly, for example, and when I do, is the content correct?
A gold-standard way of doing it would be very appreciated!
class ArticleCreateView(LoginRequiredMixin,CreateView):
template_name = 'articles/article_add.html'
model = Article
form_class = ArticleForm
success_url = reverse_lazy('index')
SectionFormSet = inlineformset_factory(Article, Section, extra=2, can_delete=False, fields=('content',))
def get(self, request, *args, **kwargs):
print('get called on article create view')
self.object = None #what does this achieve?
return self.render_to_response(
self.get_context_data(form=self.get_form(),
section_form=self.SectionFormSet(),
))
def post(self, request, *args, **kwargs):
print('post called on article create view')
self.object = None
form = self.get_form()
section_form = self.SectionFormSet(self.request.POST)
if (form.is_valid() and section_form.is_valid()):
return self.form_valid(form, section_form)
else:
return self.form_invalid(form, section_form)
def form_valid(self, form, section_form):
form.instance.author = self.request.user
if section_form.is_valid():
print('section form is valid')
self.object = form.save()
section_form.instance = self.object
section_form.save()
return HttpResponseRedirect(self.success_url)
#return super().form_valid(form)
'''
return self.render_to_response( self.get_context_data(form=form,
section_form=section_form,
))
'''
def form_invalid(self, form, article_form):
return self.render_to_response(
self.get_context_data(form=form,
article_form=article_form))
I don't know if this is 100% correct, but it seems better than my previous code :
class ArticleCreateView(LoginRequiredMixin,CreateView):
template_name = 'articles/article_add.html'
form_class = ArticleForm
SectionFormSet = inlineformset_factory(Article, Section, extra=2, can_delete=False, fields=('content',))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['section_form'] = self.SectionFormSet(self.request.POST,instance=self.object)
else:
context['section_form'] = self.SectionFormSet(instance=self.object)
return context
def save_sections(self):
try:
context = self.get_context_data()
section_form = context['section_form']
if section_form.is_valid():
#section_form.instance = self.object
section_form.save()
except Exception as e:
print('failed to save section: ' + str(e))
def form_valid(self, form):
form.instance.author = self.request.user
response = super().form_valid(form) #save article form
self.save_sections()
return response
def get_success_url(self):
return reverse_lazy('index')
here's the update view (for completion), pretty much identical except it gets the instance of the article in the formset factory
class ArticleUpdateView(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
template_name = 'articles/ArticleUpdate.html'
form_class = ArticleForm
model = Article
SectionFormSet = inlineformset_factory(Article, Section, extra=0, can_delete=False, fields=('content',))
def test_func(self):
article = self.get_object()
if self.request.user == article.author or self.request.user.is_superuser :
return True
else:
return False
def get_context_data(self, **kwargs):
print('get context data called update view')
context = super().get_context_data(**kwargs)
if self.request.POST:
context['section_form'] = self.SectionFormSet(self.request.POST,instance=self.object)
else:
context['section_form'] = self.SectionFormSet(instance=self.object)
return context
def save_sections(self):
print('save sections called update view')
try:
context = self.get_context_data()
section_form = context['section_form']
if section_form.is_valid():
# section_form.instance = self.object #if im passing instance in the factory, do I need it here to?
section_form.save()
except Exception as e:
print('failed to save section: ' + str(e))
def form_valid(self, form):
print('form valid called update view')
form.instance.author = self.request.user
response = super().form_valid(form) #save article form
self.save_sections()
return response
def get_success_url(self):
return reverse_lazy('index')

how can i filter comments by user and by article with get_context_data Django?

i want to filter the comments by user and by post. would you like to tell me how can i filter the comment using get_context_data. i am getting this error with that code 'NewsDetailView' object has no attribute 'get_object_or_404' how can i solve this issue?
models.py
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
commentator = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=200)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.article.title
views.py
class NewsDetailView(LoginRequiredMixin, DetailView):
model = Article
form_class = CommentForm
template_name = 'news/news_detail.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object_or_404(User, username=self.kwargs.get('username'))
self.object = self.get_object_or_404(Article, pk=self.kwargs.get('pk'))
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = self.object
context['form'] = CommentForm()
return context
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = CommentForm(request.POST)
form.instance.article = Article.objects.get(
pk=self.kwargs.get('pk'))
form.instance.commentator = self.request.user
form.save()
return redirect('news:news-detail', pk=self.kwargs.get('pk'))
else:
return redirect('news:news-detail', pk=self.kwargs.get('pk'))
The NewsDetailView indeed has no get_object_or_404, so using self.get_object_or_404 does not make any sense. Furthermore it is not necessary at all.
You can obtain the Comments that belong to a user with the username with self.object.comment_set.filter(commentator__username=username)
You can further use the FormMixin [Django-doc] to avoid some boilerplate code to construct the form and redirect to the success url:
from django.urls import reverse
from django.views.generic.edit import FormMixin
class NewsDetailView(LoginRequiredMixin, FormMixin, DetailView):
model = Article
form_class = CommentForm
template_name = 'news/news_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = self.object.comment_set.filter(
commentator__username=self.kwargs['username']
)
return context
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.instance.article_id = self.kwargs['pk']
form.instance.commentator = self.request.user
form.save()
return super().form_valid(form)
def form_invalid(self, form):
self.object = self.get_object()
return super().form_invalid(form)
def get_success_url(self):
return reverse('news:news-detail', kwargs={'pk': self.kwargs['pk']})

Django FBVs to CBVs

I'm refactoring my code and change all FBVs to CBVs. But one of them isn't look pretty well. So I'm asking for right CBVs for my example.
Here my FBVs
#login_required
def profile(request, username):
user_main = get_object_or_404(User, username=username)
user_profile = UserProfile.objects.get_or_create(user=user_main)[0]
form = UserProfileForm({'website': user_profile.website, 'picture': user_profile.picture})
if request.method == 'POST' and request.user == user_main:
form = UserProfileForm(request.POST, request.FILES, instance=user_profile)
if form.is_valid():
form.save(commit=True)
return redirect(profile, username)
else:
print(form.errors)
context_dict = {'user_main': user_main, 'user_profile': user_profile, 'form': form}
return render(request, 'rango/profile.html', context=context_dict)
Here's what I'm got:
#method_decorator(login_required, name='dispatch')
class ProfileView(DetailView, FormMixin):
template_name = 'rango/profile.html'
context_object_name = 'user_main'
form_class = UserProfileForm
def get_object(self, queryset=None):
user_main = get_object_or_404(User, username=self.kwargs['username'])
return user_main
def get_context_data(self, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['user_profile'] = self.get_user_profile()
return context
def get_initial(self):
user_profile = self.get_user_profile()
return {'website': user_profile.website, 'picture': user_profile.picture}
def get_form_kwargs(self):
kwargs = super(ProfileView, self).get_form_kwargs()
kwargs['instance'] = self.get_user_profile()
return kwargs
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid and request.user == self.object:
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
profile = form.save(commit=True)
return redirect('profile', self.kwargs['username'])
def form_invalid(self, form):
print(form.errors)
return super(ProfileView, self).form_invalid(form)
def get_user_profile(self):
return UserProfile.objects.get_or_create(user=self.object)[0]
I guess my way to solve this task looks horrible. Please show me the right way to make this CBV looks better.