I'm having trouble with adding comments to class based views,
forms.py:
class RequestForm(ModelForm):
class Meta:
model = Request
exclude = ('slug',)
class RequestCommentForm(ModelForm):
class Meta:
model = RequestComment
fields = ['body' ]
models.py:
class Request(models.Model):
title = models.CharField(max_length=250 )
date = models.DateTimeField('Request date', default=timezone.now, editable=False )
department = models.CharField(max_length=2, choices=DEPARTMENT)
support_request = models.TextField('Request', max_length=2500, blank=True)
owner = models.ForeignKey(User,)
slug = models.SlugField(blank=True, editable=False)
def __unicode__(self):
return self.title
views.py:
class RequestDetailView(ModelFormMixin, DetailView):
model = Request
form_class = RequestCommentForm
def get_success_url(self):
return reverse('request-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(RequestDetailView, 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):
form.instance.author = self.request.user
form.instance.request = Request.objects.get(pk=self.object.pk)
form.instance.created = timezone.now
form.save()
Also tried this part with:
self.object = form.save(commit=False)
self.object.author = self.request.user
self.object.request = Request.objects.get(pk=self.object.pk)
self.object.created = timezone.now
self.object.save()
return super(RequestDetailView, self).form_valid(form)
template: request_detail.html comments section .....
{% load bootstrap %}
<form action="{% url 'request-detail' object.id %}" method="post"> {% csrf_token %}
<ul class="form-group">
{{ form|bootstrap }}
</ul>
<input class="btn btn-primary" type="submit" value="Submit" />
</form>
.....
Page renders correctly, but when I submit, no-go with saving comment.
Debug toolbar shows that sql queries are updating the request model, instead of request comment.
Can't figure out how to add simple comment form from different model to detail page.
Any help would be appreciated.
Also, if there is more elegant way off adding comments form to class based view, would love to see it. My google-fu didn't help me to find anything.
I know I'm answering this too late, but maybe someone with the same question, in the future, may benefit.
Class RequestDetailView(DetailView):
model = Request
template_name = 'detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context ['comment'] = RequestComment.objects. all()
context['form'] = RequestCommentForm()
return context
Class CommentCreateView(CreateView):
model = RequestComment
form_class = RequestCommentForm
def get_success_URL(self):
return reverse ('request: detail', kwargs = {'slug':self.object.post.slug})
def form_valid(self, form):
post = get_object_or_404(Request, slug = self.kwargs ['slug'])
Form.instance.post = Request
return super().form_valid(form)
The URL
path('<slug:slug>/add_comment/', CommentCreateView.as_view(), name = add_comment')
And finally the HTML
<form action="{% url 'request:add_comment' request.slug %}" enctype = "multiparty/form-data" method = "post">
</form>
And that's it. I answered using my phone so there's some typos etc. This is my first answer here, I searched stack overflow for answers I'll post links later. I assumed RequestComment foreignkey field is post and creating models with name 'request' is not encouraged in Django.
Wish I could find a way to do this with CBV, but nope.. function view works great...
class RequestDetailView(ModelFormMixin, DetailView):
model = Request
form_class = RequestCommentForm
def get_success_url(self):
return reverse('request-detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(RequestDetailView, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def comments(self):
return RequestComment.objects.filter(request=Request.objects.get(pk=self.object.pk))
def RequestCommentAddView(request, pk):
form = RequestCommentForm(request.POST or None)
if form.is_valid() and pk:
form.instance.author = request.user
form.instance.request = Request.objects.get(pk=pk)
form.save()
return HttpResponseRedirect(reverse('request-detail', kwargs={'pk': pk}))
return HttpResponseRedirect(reverse('request-detail', kwargs={'pk': pk}))
Related
I think I don't understand something fundamental here, but every single tutorial on the topic proposes a solution using either a function or a generic class, and both of them work for me, but I can't figure out how to deal with the issue using just View. So to illustrate where I am at, I am building a very simple blog and want to update data on a single post based on it's id. So what I have is:
models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.CharField(max_length=100)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
urls.py
urlpatterns = [
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update')
]
update_post.html
<form method="post">
{% csrf_token %}
{{ isolated_post.as_p }}
<input type="submit" value="Update">
</form>
and finally views.py
class PostUpdateView(View):
form_class = PostForm
initial = {'key': 'value'}
template_name = "blog/update_post.html"
def get(self, request, pk):
isolated_post = Post.objects.get(pk=pk)
form = self.form_class(instance=isolated_post)
return render(request, self.template_name, {'form': form})
def post(self, request, pk, form):
updated_post = self.form_class(request.POST, instance=form)
if updated_post.is_valid():
updated_post.save()
return HttpResponseRedirect("/post/" + f'{pk}/')
return render(request, self.template_name, {'updated_post': updated_post})
I've tried a lot of things, this time it says that form has not been passed to the post function. but the most I can achieve is the empty form, which throws out an error after I press "update". I think I just do not get the logic of "post" well enough, or how to populate the form with the one i use get function on.
Your view
class PostUpdateView(View):`
form_class = PostForm
template_name = "blog/update_post.html"
def setup(self, request, *args, **kwargs):
self.post_instance = get_object_or_404(Post, pk=kwargs['post_id'])
return super().setup(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
post = self.post_instance
form = self.form_class(instance=post)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
post = self.post_instance
form = self.form_class(request.POST, instance=post)
if form.is_valid():
new_post = form.save(commit=False)
new_post.save()
return redirect('your dynamic url', post.id)
This question might be asked alot in stackoverflow but i couldn't find the answer.
Take a look at code:
# models.py
class Message(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.CharField(max_length=200, null=True, blank=True)
room = models.ForeignKey(Room, on_delete=models.CASCADE, blank=True, null=True)
posted = models.TimeField(auto_now_add=True)
def __str__(self):
return self.body
views.py:
class RoomInsideView(View):
template_name = 'room/room_inside.html'
form_class = SendMessageForm
room = None
def get(self, request, room_id, room_slug):
self.room = Room.objects.get(id=room_id)
if self.room.is_private:
return redirect('room:private_room_auth', self.room.id)
form = self.form_class()
context = {
'room': self.room,
'form': form,
}
return render(request, self.template_name, context)
def post(self, request, room_id, room_slug):
form = self.form_class(request.POST)
if form.is_valid():
new_msg = Message(body=form.cleaned_data['body'])
new_msg.user = request.user in
all_messages = Message.objects.filter(room=self.room)
messages.error(request, 'form not valid', 'warning')
return render(request, self.template_name, {'form': form, 'message': all_messages})
forms.py:
class SendMessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ('body',)
widgets = {
'body': forms.TextInput(attrs={'class': 'form-control',
'placeholder': 'Send'}),
}
template:
<form method="post" action="" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.body.errors }}
{{ form.body }}
<input type="submit" value="Send" class="btn btn-primary">
</form>
as I added a messages.error if form is not valid it's returning form not valid and I can't find where am I doing wrong
You always add the warning, regardless whether the form is valid or not, this does not make much sense.
That being said, you are writing too much boilerplate code, you can use a CreateView which will eliminate most of the boilerplate code:
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
class RoomInsideView(View):
template_name = 'room/room_inside.html'
form_class = SendMessageForm
success_url = reverse_lazy('name-of-some-view')
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['room'] = get_object_or_404(Room, pk=self.kwargs['room_id'], is_private=False)
return context
def form_invalid(self, form):
messages.error(request, 'form not valid', 'warning')
return super().form_invalid(form)
def form_valid(self, form):
form.instance.room_id = self.kwargs['room_id']
form.instance.user = self.request.user
return super().form_valid(form)
The name-of-some-view should be replaced with the name of the view where the view should redirect to in case of a successful POST request, this is done to implement the Post/Redirect/Get architectural pattern [wiki].
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I’m trying to create a dashboard for the staff users to fill in and edit some information regarding their users. The form works and saves successfully, but when I submit it for a second time, it creates a new object. It won’t replace the previous:
This is my views.py file:
class ScientificInfoView(FormMixin, DetailView):
model = ScientificInfo
template_name = 'reg/scientific-info.html'
form_class = ScientificInfoForm
def get_success_url(self):
return reverse('scientific-info', kwargs={'pk': self.object.pk})
def get_context_date(self, **kwargs):
context = super(ScientificInfoView, self).get_context_data(**kwargs)
context['form'] = ScientificInfoForm()
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):
form.save()
return super(ScientificInfoView, self).form_valid(form)
And my template:
<form method="POST" enctype="multipart/form-data" action="{% url 'scientific-info' pk=object.id %}">
{% csrf_token %}
{{form}}
<button type="submit">submit</button>
</form>
File urls.py:
path('surveys/scientific/<pk>', login_required(views.ScientificInfoView.as_view()), name='scientific-info')
I’m pretty sure that the action part in my form is causing the issue, but how can I solve it?
Use this:
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse("scientific-info", kwargs={"pk": pk})
Or
class ScientificInfoView(FormMixin, DetailView):
model = ScientificInfo
template_name = 'reg/scientific-info.html'
form_class = ScientificInfoForm
def get_success_url(self):
return reverse("scientific-info", args=[pk]) # You can replace pk
I am trying to make a form that is dynamically selected by a DetailView object.
I want to click the DetailView link and be taken to a form whose primary key is the same as the primary key from my detail view. When I attempt to do this I get an error. How can I do this? Is their a prebuilt library that will assist me?
My Model:
'''
class MemberStudent(models.Model):
instructor = models.ForeignKey(Teachers, on_delete=models.CASCADE)
name = models.CharField(max_length=50, default="Doe")
age = models.IntegerField(default=99)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('student_detail', args=[str(self.id)])
class BehaviorGrade(models.Model):
SEVERITY = [
('Bad','Bad Behavior'),
('Good','Good Behavior'),
('Danger','Dangerous'),
]
LETTER_GRADE = [
('A','A'),
('B','B'),
('F','F'),
]
studentName = models.ForeignKey(MemberStudent, on_delete=models.CASCADE) #need a link to student name
eventGrade = models.CharField(max_length=1, choices=LETTER_GRADE)
eventSeverity = models.CharField(max_length=15, choices=SEVERITY)
eventTitle = models.CharField(max_length=15)
eventDescription = models.TextField()
def __str__(self):
return self.eventTitle
'''
My Views:
'''
class StudentDetailView(FormMixin,DetailView):
model = MemberStudent
template_name = 'student_detail.html'
form_class = BehaviorForm
def get_success_url(self):
return reverse('student_detail', kwargs={'pk':self.object.pk})
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
form.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
return super().form_valid(form)
'''
my forms.py:
'''
class BehaviorForm(ModelForm):
class Meta:
model = BehaviorGrade
fields = ['eventGrade',
'eventSeverity','eventTitle',
'eventDescription']
'''
my url:
'''
path('students/<int:pk>', StudentDetailView.as_view(), name='student_detail'),
'''
my htmltemplate(its a detailview with a form below on the same page):
'''
{{ object.id }}
{{ object.name }}
{{ object.age }}
{{ object.instructor }}
<form action="{% url 'student_detail' object.id %}" method="POST">
{{ form }}
{% csrf_token %}
<input type="submit" value="REVIEW">
</form>
'''
error message when form submitted:
"IntegrityError at /student/students/1
NOT NULL constraint failed: Students_behaviorgrade.studentName_id"
The form also is sent without the PK I have requested in my form code.
the django error log shows the PK is never sent
here is the log message on a test of dummy data:
params [None, 'A', 'Bad', 'asdf', 'asdf']
That's because you have to manually set the member student id on the behavior garde.
You can do it like this in the post function of your view :
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
behavior_grade = form.save(commit=False)
behavior_grade.studentName = self.object
return self.form_valid(form)
else:
return self.form_invalid(form)
You can also take a look at this method to use the form_valid function to do it :
## Include the instance object before saving
def form_valid(self, form):
form.instance.studentName = self.object
return super().form_valid(form)
The solution was to to call the primary key in the model method. Then to save to the correct model object I called .pk on my object. I'm cannot elegantly explain this solution however upon testing several different scenarios it works.
'''
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
behavior_grade = form.save(commit=False)
behavior_grade.studentName = self.object #passes unamed form field
behavior_grade.studentName.pk = form.save(commit=True) #calls key
return self.form_valid(form)
else:
return self.form_invalid(form)
'''
I am creating an image upload function with django.
However, it is not uploaded.
I don't know the code mistake, so I want to tell you.
I tried variously, but if I specify default for imagefiled,
default will be applied.
#form
class RecordCreateForm(BaseModelForm):
class Meta:
model = URC
fields = ('image','UPRC','URN',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordCreateForm,self).__init__(*args, **kwargs)
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
#view
class RecordCreate(CreateView):
model = URC
form_class = RecordCreateForm
template_name = 'records/urcform.html'
success_url = reverse_lazy('person:home')
def get_form_kwargs(self):
kwargs = super(RecordCreate, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
user = self.request.user
form.instance.user = user
form.instance.group = belong.objects.get(user=user).group
return super().form_valid(form)
#model
def get_upload_path(instance, filename):
n = datetime.now()
prefix = "records/"
ymd='/'.join([n.strftime('%Y'), n.strftime('%m'), n.strftime('%d'), ""]) + "/"
directory=str(instance.user.id) + "/"
name=str(uuid.uuid4()).replace("-", "")
extension=os.path.splitext(filename)[-1]
return ''.join([prefix, directory, ymd, name, extension])
class URC(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(group, on_delete=models.CASCADE, null=True)
URN = models.ForeignKey(UPRM, on_delete=models.CASCADE)
UPRC = models.CharField(max_length=300)
image = models.ImageField(upload_to=get_upload_path)
def __str__(self):
return self.UPRC
#urls
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
I will post any other necessary code.
Sorry for the poor English.
Postscript
The page is redirected without any error display.
But admin screen was able to upload.
class BaseModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs.setdefault('label_suffix', '')
super(BaseModelForm, self).__init__(*args, **kwargs)
#template
<form method="post" action="">
{% csrf_token %}
{{form.image.label_tag}}
{{form.image}}
{{form.UPRC.label_tag}}
{{form.UPRC}}
{{form.URN.label_tag}}
{{form.URN}}
<input class="btn btn-primary" type="submit" value="submit">
</form>
Your <form> tag misses the enctype, as explained here:
<form method="post" enctype="multipart/form-data">
You can take a look at this example.
https://www.pythonsetup.com/simple-file-uploads-django-generic-createview/
def form_valid(self, form):
self.object = Author(photo=self.get_form_kwargs().get('files')['photo'])
self.object = form.save()
return HttpResponseRedirect(self.get_success_url())