How to point django form to pk? - django

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)
'''

Related

Update view in Django WITHOUT using generic classes

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)

Django form's not valid

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.

context data wont display anything in detailview

I'm trying to display this models' object where the user is as same as the detail view user. here is my views:
class ScientificInfoView(FormMixin, DetailView):
model = ScientificInfo
template_name = 'reg/scientific-info.html'
form_class = ScientificInfoForm
def get_success_url(self):
messages.success(self.request, 'Profile details updated.')
return reverse('scientific-info', kwargs={'pk': self.object.pk})
def get_context_date(self, **kwargs):
context = super(ScientificInfoView, self).get_context_data(**kwargs)
#THIS IS THE LINE THAT WONT WORK#
context['result'] = Results.objects.filter(user=self.object.user)
context['sample'] = 'sample text sample text'
#################################
context['form'] = ScientificInfoForm()
return context
def post(self, request, pk):
self.object = self.get_object()
form = ScientificInfoForm(request.POST, instance=get_object_or_404(ScientificInfo, id=pk))
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
f = form.save(commit=False)
f.user = self.object.user
f.save()
return super(ScientificInfoView, self).form_valid(form)
everything works fine except the result and sample data. it shows nothing in the template. It cant even render a simple text!
this is the templates:
{% for r in result %}
{{r}}
{% endfor %}
{{ sample }}
This is just a typo, the name is get_context_data and not get_context_datE.

Submitting a form inside a detail view creates a new object instead of replacing it

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

Django class based view and comments

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}))