update formset with class based view - django

i've created web blog with django 2.2 each post has multiple images , but when i try to update the post
images wont updated
i use class based view
class Post(models.Model):
user= models.ForeignKey(Account,on_delete=models.CASCADE)
title= models.CharField(max_length=100)
#others
class PostImage(models.Model):
post= models.ForeignKey(Post,on_delete=models.CASCADE,related_name='images')
media_files = models.FileField(upload_to=random_url)
and this my forms.py
class PostImageForm(forms.ModelForm):
class Meta:
model = PostImage
fields = [
'media_files'
]
class PostUpdateForm(forms.ModelForm):
class Meta:
model = Post
fields = [
'title','description',#and others
]
my views.py
PostImageFormSet = inlineformset_factory(
Post,PostImage,form=PostImageForm,extra=1,can_delete=True,can_order=False
)
class PostUpdateView(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
model = Post
form_class = PostUpdateForm
template_name = 'posts/update_post.html'
def get_context_data(self,**kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['images'] = PostImageFormSet(self.request.POST or None,self.request.FILES,instance=self.object)
else:
data['images'] = PostImageFormSet(instance=self.object)
return data
def form_valid(self,form):
context = self.get_context_data()
images = context['images']
with transaction.atomic():
if form.is_valid() and images.is_valid():
self.object = form.save()
images.instance = self.object
images.save()
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user.username == post.user.username:
return True
return False
def get_success_url(self):
return reverse_lazy('post:post-detail',kwargs={'slug':self.object.slug})
my templates
<form enctype="multipart/form-data" method="post" action="">
{% csrf_token %}
{{images.management_form }}
{{ form|crispy }}
{% for img in images %}
<label>{{img.media_files.label}}</label>
{{img.media_files}}
{% endfor %}
<button type="submit" class="btn btn-block btn-primary">update</button>
</form>
i'm wondering why didnt update the posts image !?
thanks for replay ..

Related

Django, Foreign key not getting filled with session data

im trying to fill my foreignkey (employer) with the user that is logged in, but i have seen alot of way but they havent worked for me, does anyone know what im doing wrong? and how i can fix it?
View:
class JobCreate(CreateView):
model = Job
form = JobCreateForm()
form_class = JobCreateForm
context = {}
success_url = reverse_lazy('jobsview')
def POST(self,request):
if request.method == 'POST':
form = JobCreateForm(request.POST)
if form.is_valid():
job = form.save(commit=False)
job.employer = request.user
job.save()
context = {}
return render(request, 'jobs/jobs.html',context)
else:
context = {}
return render(request, 'jobs/job_form.html',context)
Model:
class Job(models.Model):
employer = models.ForeignKey(User, related_name='employer', on_delete=CASCADE,blank=True)
employees = models.ManyToManyField(User, related_name='employees2user',null=True,blank=True)
title = models.CharField(max_length=200,)
description = models.CharField(max_length=200,null=True,blank=True)
category_id = models.ManyToManyField(Category,blank=True)
skill_id = models.ManyToManyField(Skill,blank=True)
approved = models.BooleanField(default=False)
# img = models.ImageField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): # Default value
return self.title
HTML:
{% extends "jobs/layout.html" %}
{% block content %}
<h3> Job</h3>
<div class="container">
<div class="jobform">
<form action="" method="POST">
{%csrf_token%}
{% for field in form %}
<label for="{{field.id_for_label}}">{{field.html_name}}</label>
{{field}}
{% endfor %}
<p>Ctrl in houden om meerder te selecteren</p>
<button type="submit" class="btn btn-dark btn-space">Submit</button>
</form>
</div>
</div>
{%endblock%}
In your views method, try replacing
job = form.save(commit=False)
job.employer = request.user
job.save()
with
self.object = form.save(commit=False)
self.object.employer = self.request.user
self.object.save()
Also, what is the purpose of context {} ?
Can you just put this inside a form_valid method directly? That's much cleaner.
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.employer = self.request.user
self.object.save()
return super(JobCreate, self).form_valid(form)

How do I properly implement Django formsets with a CreateView ( Class Based View )?

And thanks in advance for any suggestions. I have been playing around with how to properly implement formsets with a CreateView for a couple of days and I'm stuck. Here is my code.
My Models:
class Team(models.Model):
team_name = models.CharField(max_length=264,null=False,blank=False)
class Player(models.Model):
player_name = models.CharField(max_length=264,null=False,blank=False)
team = models.ForeignKey(Team,null=True,on_delete=models.CASCADE)
My View:
class CreateTeamView(LoginRequiredMixin,CreateView):
model = Team
form_class = CreateTeamForm
template_name = 'create_team.html'
def get_context_data(self, **kwargs):
context = super(CreateTeamView, self).get_context_data(**kwargs)
if self.request.POST:
context['new_player'] = NewPlayerFormSet(self.request.POST)
else:
context['nwe_player'] = NewPlayerFormSet()
return context
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(CreateTeamView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
context = self.get_context_data()
new_player_form = context['new_player']
if new_player_form.is_valid():
self.object = form.save()
new_player_form.instance = self.object
new_player_form.save()
instance = form.save()
else:
return self.render_to_response(self.get_context_data(form=form))
My Forms:
class CreateTeamForm(forms.ModelForm):
class Meta:
model = Team
exclude = []
NewPlayerFormSet = inlineformset_factory(Team, Player, extra=1, fields=['player_name',])
My HTML:
<div="players>
<div="add_players">
{{ new_player.management_form }}
{% for form in new_player %}
{{ form.id }}
{{ form.player_name }}
</div>
</div>
My form is saving one player, but when I try to update the code to save more than one player with the initial CreateView, it only recognizes the first contact. I have overridden the BaseInlineFormset to do validation as shown below....
class NewPlayerFormSet(NewPlayerFormSet,BaseInlineFormSet):
player_name = forms.CharField(required=True,widget=forms.TextInput)
def add_fields(self, form, index):
super(NewPlayerFormSet,self).add_fields(form,index)
form.fields['player_name'].required = False
def clean(self):
super(NewPlayerFormSet, self).clean()
for form in self.forms:
if form.cleaned_data.get('player_name'):
pass
else:
form.add_error('player_name','Player Name is required.')
pass
I'm trying to get the code to save a second contact. I have used tried various JQuery attempts....but am unclear if my problem is with JQuery or mayby my HTML templates? That's where I'm stuck.
I tried to do something like...
$(document).ready(function() {
// Watch for the 'add player' click
$('#add_player').click(function(e) {
e.preventDefault();
$('div.add_player:last').clone().each(function(i) {
$(this).find('input,select').each(function(i) {
// Remove any existing values
$(this).val('');
}).appendTo('div#players');
});
});
And while this works to duplicate the form, the players beyond number 1 are not being saved. Not sure what I'm doing incorrectly.
It would appear there is a JQuery plugin for this, but I'm trying to avoid using it for a number of reasons. Thanks again for any help to point me in the right direction.
This was not easy. I spent about a week trying to piece this all together. Here are all of the parts that I used to finally make it work. I ultimately did wind up using jquery.formset.js from GitHub in my solution. Hope I save someone a week.
class Team(models.Model):
team_name = models.CharField(max_length=264,null=False,blank=False)
class Player(models.Model):
player_name = models.CharField(max_length=264,null=False,blank=False)
team = models.ForeignKey(Team,null=True,on_delete=models.CASCADE)
My Views.py
class CreateTeamView(LoginRequiredMixin,CreateView):
model = Team
form_class = TeamForm
template_name = 'create_team.html'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
player_form = CreatePlayerFormSet()
return self.render_to_response(
self.get_context_data(form=form,
player_form=player_form,
))
def form_valid(self, form, player_form):
self.object = form.save()
player_form.instance = self.object
player_form.save()
instance = form.save()
def form_invalid(self, form, player_form):
return self.render_to_response(
self.get_context_data(form=form,
player_form=player_form,
))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
player_form = CreatePlayerFormSet(self.request.POST)
if (form.is_valid() and player_form.is_valid()):
return self.form_valid(form, player_form)
else:
return self.form_invalid(form, player_form)
My Forms.py
class CreateTeamForm(forms.ModelForm):
class Meta:
model = Team
exclude = [ ]
CreatePlayerFormSet = inlineformset_factory(Team, Player, extra=1, fields=(['player_name'])
My HTML Template: ( Using jquery/jquery.formset.js )
<script src="{% static 'jquery/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$(".inline.{{ player_form.prefix }}").formset({
prefix: "{{ player_form.prefix }}",
})
})
</script>
<form method="POST" enctype="multipart/form-data" id="forms">
{% csrf_token %}
{{ player_form.management_form }}
{{ player_form.non_form_errors }}
{% for form in player_form %}
{{ form.id }}
<div class="inline {{ player_form.prefix }}">
<div class="leftwidth22">
<div class="width52">
<h2 class="floatright23">Player Name - </h2>
</div>
</div>
<div class="rightwidth53">
<h2 class="width70">
{{ form.player_name }}
</h2>
</div>
{% endfor %}

Using formsets for my fileupload does not work when doing an update class based view only on create in django

I have used as many examples online as I could cobble together in an attempt to get my two simple models to have the ability to do an inline formset allowing me to add many files to a technial drawing.
This is not working, I can only add one file for the Create and the update only updates the Technical_Entry model, not a file...which in of itself acts funny. The UI ona create shows one spot to add a file, then save the entire record and its child record. That works.
The update, the UI shows the file that was saved earlier..(great!) but then has two more 'choose file' slots (random?) and adding a file to those does nothing when the save is clicked. It doesn't remove the file previously added in the create, but it also
does not save the new file added to the now three slots (one used, two free). So update does not work for the files for some reason.
class Technical_Entry(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
ema = models.ForeignKey(EMA, on_delete=models.CASCADE)
system = models.ForeignKey('System', on_delete=models.CASCADE) # are SYSTEMS RELATED TO SUBSYSTEMS OR JUST TWO GROUPS?
sub_system = models.ForeignKey(SubSystem, on_delete=models.CASCADE)
drawing_number = models.CharField(max_length=200)
drawing_title = models.CharField(max_length=255)
engineer = models.CharField(max_length=200)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
date_drawn = models.DateField()
ab = models.BooleanField()
class Technical_Entry_Files(models.Model):
tech_entry = models.ForeignKey(Technical_Entry, on_delete=models.CASCADE)
file = models.FileField(upload_to='techdb/files/')
def __str__(self):
return self.tech_entry.drawing_number
To upload using a formset. While the page 'displays' mostly correctly, it flat out does not create the record in the Technical_Entry_Files model.
Relevant forms.py:
class FileUploadForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FileUploadForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
class Meta:
model = Technical_Entry_Files
fields = ('file',)
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=1, max_num=15)
class Technical_EntryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Technical_EntryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = Technical_Entry
fields = ('category', 'ema', 'system', 'sub_system', 'drawing_number', 'drawing_title', 'engineer', 'vendor', 'date_drawn', 'ab')
widgets = {
'date_drawn':DateInput(attrs={
'class':'datepicker form-control',
'id' : 'datetimepicker2',
'tabindex' : '1',
'placeholder' : 'MM/DD/YYYY hh:mm',
'autocomplete':'off',
}, format='%m/%d/%Y'),
'system' : Select(attrs={'tabindex':'2'}),
}
Relevant views.py:
class TechEntryCreateView(LoginRequiredMixin, CreateView):
print ("we are here")
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
print(template_name)
success_url = '/techentry_add'
def get_context_data(self, **kwargs):
data = super(TechEntryCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['file_upload'] = TechFileFormSet(self.request.POST, self.request.FILES)
else:
data['file_upload'] = TechFileFormSet()
return data
def form_valid(self, form):
context =self.get_context_data()
file_upload = context['file_upload']
with transaction.atomic():
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryCreateView, self).form_valid(form)
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryUpdateView, self).form_valid(form)
and the tech_entry_form.html:
{% extends 'base.html' %}
{% load static %}
{% block page-js %}
<script>
$('.link-formset').formset({
addText: 'add file',
deleteText: 'remove',
});
</script>
{% endblock %}
{% block content %}
<main role="main" class="container">
<div class="starter-template">
<h1>New Tech Entry</h1>
</div>
<h2> Details of Technical Entry </h2>
<div class="row">
<div class="col-sm">
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<h2> Files </h2>
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
<input type="submit" value="Save"/>back to the list
</form>
</div>
</div>
</div>
</main><!-- /.container -->
{% endblock %}
And what the UI looks like on edit...
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
#log_entry_class = Technical_EntryForm(Technical_Entry) #removed
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
#self.object = self.get_object() #removed
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
#return super().form_valid(form)
return super(TechEntryUpdateView, self).form_valid(form) #replaced old one
UPDATE
1. in order to be able to add mulitple files when creating,
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=4, max_num=4)
# note I changed to extra=4, if you always want to have only 4 files, then also change to max_num=4
2. When update, the reason why the update is not working even after modifying the views was because you were not passing hidden fields. You were not passing the ids of the files, therefore, your formsets were not passing .is_valid(), hence, no update. Add for loop about hidden fields like below.
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
{% for hidden in upload_form.hidden_fields %}
{{hidden}}
{% endfor %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
#Note the for loop I add about hidden fields.

Inlineformset_factory saving parent without child and not displaying validation errors if child is none

I am having 2 issues, one if you submit and click back and then submit again it duplicates the instance in the database - in this case Household. In addition it is saving the parent 'Household' without the child 'Applicants' despite me setting min_num=1
can someone point me in the right direction to resolve this issue.
Many thanks in advance
class Application(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
application_no = models.CharField(max_length=100, unique=True, default=create_application_no)
created_date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
class HouseHold(models.Model):
name = models.CharField(max_length=100)
application = models.ForeignKey(Application, on_delete=models.CASCADE)
no_of_dependents = models.PositiveIntegerField(default=0)
class Applicant(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
household = models.ForeignKey("HouseHold", on_delete=models.CASCADE)
forms.py
class ApplicationForm(ModelForm):
class Meta:
model = Application
fields = (
"name",
)
class ApplicantForm(ModelForm):
class Meta:
model = Applicant
fields = [
"household",
"first_name",
"last_name"
]
class HouseHoldForm(ModelForm):
class Meta:
model = HouseHold
fields = [
'name',
'application',
'no_of_dependents'
]
def __init__(self, application_id=None, *args, **kwargs):
super(HouseHoldForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'House Hold Name'
if application_id:
self.fields['application'].initial = application_id
self.fields['application'].widget = HiddenInput()
ApplicantFormset = inlineformset_factory(
HouseHold, Applicant, fields=('household', 'first_name', 'last_name'), can_delete=False, extra=1, validate_min=True, min_num=1)
views.py
class HouseHoldCreateView(LoginRequiredMixin, generic.CreateView):
model = models.HouseHold
template_name = "households/household_create.html"
form_class = HouseHoldForm
def get_parent_model(self):
application = self.kwargs.get('application_pk')
return application
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['application'] = models.HouseHold.objects.filter(application_id=self.kwargs['application_pk']).last()
context['house_hold_formset'] = ApplicantFormset(self.request.POST, instance=self.object)
else:
context['application'] = models.Application.objects.get(id=self.kwargs['application_pk'])
context['house_hold_formset'] = ApplicantFormset()
return context
def get_form_kwargs(self):
kwargs = super(HouseHoldCreateView, self).get_form_kwargs()
print(kwargs)
kwargs['application_id'] = self.kwargs.get('application_pk')
return kwargs
def form_valid(self, form):
context = self.get_context_data()
applicants = context['house_hold_formset']
with transaction.atomic():
self.object = form.save()
if applicants.is_valid():
applicants.instance = self.object
applicants.save()
return super(HouseHoldCreateView, self).form_valid(form)
def get_success_url(self):
if 'addMoreApplicants' in self.request.POST:
return reverse('service:household-create', kwargs={'application_pk': self.object.application.id})
return reverse('service:household-list', kwargs={'application_pk': self.object.application.id})
I had a similar problem, I solved it by adding the post() method to the view. The example is an UpdateView but the usage is the same.
(the indentation is not correct but that's what stackoverflow's editor let me do, imagine all methods are 4 spaces to the right)
class LearnerUpdateView(LearnerProfileMixin, UpdateView):
model = User
form_class = UserForm
formset_class = LearnerFormSet
template_name = "formset_edit_learner.html"
success_url = reverse_lazy('home')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
learner = User.objects.get(learner=self.request.user.learner)
formset = LearnerFormSet(instance=learner)
context["learner_formset"] = formset
return context
def get_object(self, queryset=None):
user = self.request.user
return user
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
user = User.objects.get(learner=self.get_object().learner)
formsets = LearnerFormSet(self.request.POST, request.FILES, instance=user)
if form.is_valid():
for fs in formsets:
if fs.is_valid():
# Messages test start
messages.success(request, "Profile updated successfully!")
# Messages test end
fs.save()
else:
messages.error(request, "It didn't save!")
return self.form_valid(form)
return self.form_invalid(form)
Keep in mind that to save the formset correctly you have to do some heavy lifting in the template as well. I'm referring to the hidden fields which can mess up the validation process. Here's the template corresponding to the view posted above:
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
{{ learner_formset.management_form}}
{% for form in learner_formset %}
{% if forloop.first %}
{% comment %} This makes it so that it doesnt show the annoying DELETE checkbox {% endcomment %}
{% for field in form.visible_fields %}
{% if field.name != 'DELETE' %}
<label for="{{ field.name }}">{{ field.label|capfirst }}</label>
<div id="{{ field.name }}" class="form-group">
{{ field }}
{{ field.errors.as_ul }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% for field in form.visible_fields %}
{% if field.name == 'DELETE' %}
{{ field.as_hidden }}
{% else %}
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
<input class="btn btn-success" type="submit" value="Update"/>
Additional reading :
https://medium.com/#adandan01/django-inline-formsets-example-mybook-420cc4b6225d
Save formset in an UpdateView
Inspired by Beikini
I have solved it using the create View
class HouseHoldCreateView(LoginRequiredMixin, generic.CreateView):
model = HouseHold
template_name = "households/household_create3.html"
form_class = HouseHoldForm
def get_parent_model(self):
application = self.kwargs.get('application_pk')
return application
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['application'] = HouseHold.objects.filter(
application_id=self.kwargs['application_pk']).last()
context['house_hold_formset'] = ApplicantFormset(self.request.POST)
else:
context['application'] = Application.objects.get(id=self.kwargs['application_pk'])
context['house_hold_formset'] = ApplicantFormset()
return context
def get_form_kwargs(self):
kwargs = super(HouseHoldCreateView, self).get_form_kwargs()
kwargs['application_id'] = self.kwargs.get('application_pk')
return kwargs
def form_valid(self, form):
context = self.get_context_data()
applicants = context['house_hold_formset']
application_id = self.kwargs['application_pk']
household_form = self.get_form()
if form.is_valid() and applicants.is_valid():
with transaction.atomic():
self.object = form.save()
applicants.instance = self.object
applicants.save()
messages.success(self.request, 'Applicant saved successfully')
return super(HouseHoldCreateView, self).form_valid(form)
else:
messages.error(self.request, 'please add an applicant to the household')
return self.form_invalid(form)
def get_success_url(self):
return reverse('service:household-list', kwargs={'application_pk': self.object.application.id})

Showing user's comments on posts in Django

I am adding a comment section for my posts in a blog, I reached to the part where I can add new comments and get it saved in the db and I can view them in the admin, but I am stuck to showing the username and the comment in the comment section, I have to go the admin and choose the name of the user and the blog name to appear in the page, how do i link them together
Here is the Models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
content = models.TextField(max_length=160)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.content
Here is the views.py:
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
post = get_object_or_404(Post, slug=self.kwargs['slug'])
comments = Comment.objects.filter(post=post).order_by('-id')
total_likes = post.total_likes()
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
if self.request.method == 'POST':
comment_form = CommentForm(self.request.POST or None)
if comment_form.is_valid():
content = self.request.POST.get('content')
comment = Comment.objects.create(
post=post, user=request.user, content=content)
comment.save()
return HttpResponseRedirect("post_detail.html")
else:
comment_form = CommentForm()
context["total_likes"] = total_likes
context["liked"] = liked
context["comments"] = comments
context["comment_form"] = comment_form
return context
class PostCommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
fields = ['content', ]
success_url = reverse_lazy('score:post-detail')
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
print(args, kwargs, request.POST)
return redirect('score:post-detail', slug=kwargs['slug'])
here is the template
<form action={% url 'score:post-comment' post.slug %} method="post" class="comment-form" action=".">
{% comment %} <form method="post" class="comment-form" action="."> {% endcomment %}
{% csrf_token %}
{{ comment_form.as_p }}
{% if request.user.is_authenticated %}
<input type="submit" value="Submit" class="btn btn-outline-success">
{% else %}
<input type="submit" value="Submit" class="btn btn-outline-success" disabled> You must be Logged in to Comment
{% endif %}
</form>
here is the form
class CommentForm(forms.ModelForm):
content = forms.CharField(label="", widget=forms.Textarea(
attrs={'class': 'form-control', 'placeholder': 'Text goes here!!!', 'rows': '4', 'cols': '50'}))
class Meta:
model = Comment
fields = ('content',)
A CreateView is defined to remove most of the boilplate code, so you should not reimplement that in the post method, but let the CreateView do its work.
What you here should do is override the .form_valid(…) method [Django-doc] and the .form_invalid(…) method [Django-doc], and the .get_success_url(…) method [Django-doc] to redirect to the proper view:
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse
class PostCommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
form_class = CommentForm
def form_valid(self, form):
post = get_object_or_404(Post, slug=self.kwargs['slug'])
form.instance.user = self.request.user
form.instance.post = post
return super().form_valid(form)
def form_invalid(self, form):
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('score:post-detail', kwargs=dict(slug=self.kwargs['slug']))