Django create instance from views.py not working - python

I have a Django app that is basically a list app for tracking jobs that the user is applying for. Users have applications and applications have questions that the user wants to ask to the interviewers should they get an interview. So I have created a many to many relationship between the models 'Application' and 'Question' called 'Ask'. Models.py:
class Application(models.Model):
status_choices = (
('NS','Not Started'),
('IP','In Progress'),
('OH','On Hold'),
('CO','Complete'),
)
app_status = models.CharField(max_length=2,choices=status_choices)
position = models.CharField(max_length=255)
company = models.ForeignKey(Company)
applied_date = models.DateField(null=True)
isDeleted = models.BooleanField(default=False, blank=True)
user = models.ForeignKey(User)
def __str__(self):
return self.position
class Questions(models.Model):
question = models.CharField(max_length=255)
category = models.CharField(max_length=25)
isDeleted = models.BooleanField(default=False, blank=True)
class Ask(models.Model): #joining table
question = models.ForeignKey(Questions)
app = models.ForeignKey(Application)
isDeleted = models.BooleanField(default=False, blank=True)
So the idea is that the user creates a list of questions and then when they get an interview, they prepare for it by looking at their list of questions and assigning them to the application through a template. Questions.html:
{% extends 'base.html' %}
{% block body %}
<h1> Questions </h1>
<div class="table-responsive" align="center">
<table class="table-hover" width="75%">
<tr>
<th> Category </th>
<th> Question </th>
<th> Create Question</th>
<form method="POST">
{% csrf_token %}
<input type="text" name="app" value="{{ request.META.HTTP_REFERER }}">
<th><input type='submit' value='Add Selected'></th>
</tr>
{% for item in query %}
<tr>
<td> {{ item.category }} </td>
<td> {{ item.question }} </td>
<td> <input type="checkbox" name="selected_q" value="{{ item.id }}"></td>
<td> Delete </td>
<td> Edit </td>
</tr>
</form>
{% endfor %}
</table>
</div>
{% endblock %}
Which is called from views.py:
class QuestionView(TemplateView):
template_name = 'question.html'
def get(self, request):
query = Questions.objects.all().order_by('category')
args = {'query': query}
return render(request, self.template_name, args)
def post(self, request):
id_list = request.POST.getlist('selected_q')
return_url = request.POST.get('app')
app = int(return_url.rsplit('/')[-1])
myApp = Application.objects.get(pk=app)
if id_list != None:
for item in id_list:
myQuestion = Questions.objects.get(pk=int(item))
Ask.objects.create(app=myApp,question=myQuestion)
return HttpResponseRedirect(return_url)
else:
query = Questions.objects.all().order_by('category')
args = {'query': query}
return render(request, self.template_name, args)
The user can only get to the questions page by going through the Application view and when that happens Questions.html captures the link that the user came from. Users then select one or more questions through a checkbox on questions.html which is captured in the post as id_list - So this should give me the two keys I need to create an instance of Ask.
However, when trying to do this it looks like everything works properly, but no instance of Ask is created and I don't get any error messages. I have tried creating a record from the python shell and it works just fine. I have also tried other methods such as using a form and instantiating Ask to call .save() afterwards, but it does not insert a record. For testing, I took out the query in views.py to check to make sure valid values are getting passed to myApp and myQuestion, so I believe that the data is all there and accurate, but it isn't creating a record for some reason.
I appreciate any help you might offer, or maybe just different ideas on how to debug this problem so I can get some error messages.

Blurb was right - wrapping the table in the form tag resolved the issue. Thanks!

Related

context must be a dict rather than module

When I am filtering for my search bar I am getting this error. I am not sure what I am doing wrong here
Watching this tutorial: https://www.youtube.com/watch?v=llbtoQTt4qw&t=3399s
views.py
class pplList(LoginRequiredMixin,ListView):
model = People
context_object_name = 'people'
def get_context_data(self, **kwargs):
search_input = self.get.GET.get('search-area') or ''
if search_input:
context['people'] = context['people'].filter(name__icontains=search_input)
return context
people_list.html
{%if request.user.is_authenticated %}
<p>{{request.user}}</p>
Logout
{% else %}
Login
{% endif %}
<hr>
<h1>Interviewee Dashboard {{color}}</h1>
Add Candidates
<form method="get">
<input type = 'text' name = 'search-are'>
<input type = 'submit' value = 'Search'>
</form>
<table>
<tr>
<th> Item</th>
<th> </th>
</tr>
{% for people in people %}
<tr>
<td>{{people.name}}</td>
<td>View</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% empty %}
<h3>No items in list</h3>
{% endfor %}
</table>
There are some minor mistakes such as it should be self.request.GET.get('search-area'), also you haven't called the super() method, so try this view:
class pplList(LoginRequiredMixin,ListView):
model = People
context_object_name = 'people_list'
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
search_input = self.request.GET.get('search-area', False)
if search_input:
context['people']= People.objects.filter(name__icontains=search_input)
return context
In the Html, the field name search-are and you are accessing in the view as self.request.GET.get('serach-area', False) kindly change its name to search-area` in the input tag of Html.
Also, class based views are generally written in PascalCase as they are classes of Python and requires their name to be written as model name as prefix and actual view name as suffix, so it should be PeopleListView.

Database contents not showing up after render

So, I try to display a list of items from database, but after calling return render(...) it behaves as if there were no objects in the database. I am new to django and after a second day of trial and error I have no idea what to do with it
Affected view:
class DropDownList(ListView):
context_object_name = 'drop_down'
template_name="browse.html"
model=ServiceList
asd = ''
def post(self, request):
name = request.POST.get('selected')
obj = ServiceList.objects.get(sourceFile=name)
print(obj)
print(request.POST) #some prints for debugging
context = {'asd': obj}
return render(request, self.template_name, context)
Models:
class ServiceList(models.Model):
version = models.IntegerField()
location = models.CharField(max_length=500)
services = models.ManyToManyField(Service)
drms = models.ManyToManyField(Drm)
sourceFile = models.CharField(max_length=500)
class Service(models.Model):
uid = models.CharField(max_length=255, unique=True)
locations = models.ManyToManyField(Location)
names = models.ManyToManyField(Name)
category = models.CharField(max_length=500)
drm = models.CharField(max_length=500)
description = models.CharField(max_length=500)
html fragment:
<p>
<br/>
<form action="/browse/" id="tableform" method="POST">{% csrf_token %}
<select class="form-select" aria-label="Default select example" form="tableform" name="selected">
<option selected>-----------------</option>
<option >One</option>
<option >Two</option>
<option >Three</option>
{% for obj in drop_down %}
<option name={{obj.sourceFile}}>{{obj.sourceFile}}</option>
{% endfor %}
</select>
</form>
<button id="add-list" type="submit" class="btn btn-success pull-left" class="button1" form="tableform">+</button>
</p>
<table class="table table-bordered table-dark">
<tr>
<th>UID</th>
<th>Category</th>
</tr>
{% for obj in drop_down %}
adasdasd
{% if obj.sourceFile == asd %}
<br/>fghfghfgh
{% for service in obj.services.all %}
<tr>
<td>{{service.uid}}</td>
<td>{{service.category}}</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</table>
There are a few things wrong in your code, I'm going to list them, so it is easier to understand.
1 - context_object_name = 'drop_down' is not being used, 'drop_down' is not being declared in your class.
2 - You don't need to assign 'asd' as an empty variable, since you are only using as a namespace for the obj variable in your context set.
3 - Your return function is wrong, you should only be returning the context there, since you are using a class based view.
4 - You are using a ListView to get only one object
I don't know if this is going to work(I don't have a lot of experience in class based views) but try to make your code look like this:
class DropDownList(DetailView):
template_name="browse.html"
model=ServiceList
def post(self):
name = request.POST.get('selected')
obj = ServiceList.objects.get(sourceFile=name)
context = {'asd': obj}
return context
you can delete the context_object_name line as I mentioned, so if you want to render your ServiceList object version field in and HTML page for example, you should use {{ asd.version }}
Also, try to look for function based views, they are better to use, im assuming that you are using this view to get an object from your database, if you were using a function based view here, your code would be much simpler, it would look like this
def get_object(request, object_id):
template = 'browse.html'
obj = ServiceList.objects.get(pk=object_id)
context = { 'youcandecideanamehere': obj }
return render(request, template, context)
but you would need to pass the object_id parameter in your url(I'm going to create some here, since you didn't give yours)
urlpatterns = [
...
path('get_object/<object_id>/', get_objects, name='get_objects')
...
]
And the a tag for that page, should be like this:
Text
Hope I've helped, but if you still have some problems, feel free to contact me.

How to compare user input to corresponding model field django

I'm trying to make learning app for myself and part of my plan is to make a quiz module.
My problem is, that I don't know how to compare user answer with the correct answer that's stored in the model.
Now, the only thing I've tried (except reading docs and stack overflow) was to inject the related model question inside of my HTML to later use in views.py, but from the beginning I felt like that's not the way it should work, so I guess I have to reorganize my models/forms or inside of views.py there is some way to query database for that specific model instance that I don't know.
Here is my code
Models:
class Question(models.Model):
question = models.CharField(max_length=100, unique=True)
answer = models.CharField(max_length=100, unique=False)
def __str__(self):
return self.question
Forms:
class Answer(forms.Form):
answer = forms.CharField()
Views:
def quiz(request):
questions = Question.objects.order_by('question')
form = Answer()
context_dict = {'form':form,'questions':questions}
if request.method == 'POST':
form = Answer(request.POST)
if form.is_valid():
#Here I want to make the comparison
pass
return render(request,"quiz_app/quiz.html",context_dict)
HTML:
<table>
{% for q in questions %}
<tr>
<td>{{ q.question }}</td>
<form method="POST">
<td>{{ form.answer }}</td>
{% csrf_token %}
<td>
<input type="submit" value="submit">
</td>
</form>
</tr>
{% endfor %}
</table>
You can pass the question_id with the post request and then get the question instance and compare the results.
HTML:
<form method="POST">
<td>{{ form.answer }}</td>
{% csrf_token %}
<input type="hidden" name="q_id" value="{{ q.id }}" />
<td>
<input type="submit" value="submit">
</td>
</form>
views:
def quiz(request):
questions = Question.objects.order_by('question')
form = Answer()
context_dict = {'form':form,'questions':questions}
if request.method == 'POST':
instance = Question.objects.get(id=request.POST['q_id'])
form = Answer(request.POST, instance=instance)
if form.is_valid():
#Here I want to make the comparison
if request.POST.get("answer").strip() == instance.answer:
# used strip() to remove whitespace before and after the text.
# other logic.
return render(request,"quiz_app/quiz.html",context_dict)

What should be the correct approach to pass in primary key into URL?

Right now I am using Class-based delete view, and my URL contains two arguments which are the primary keys of my 2 models: Post and Lesson. However, I am encountering an Attribute Error: Generic detail view LessonDeleteView must be called with either an object pk or a slug in the URLconf.
These are my two models Lesson and Post:
class Post(models.Model):
title = models.CharField(max_length=100)
image = models.ImageField(default = 'default0.jpg', upload_to='course_image/')
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=6)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(default = 0)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk' : self.pk})
class Lesson(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to="lesson/pdf")
date_posted = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(Post, on_delete=models.CASCADE, null=False, blank=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson_upload', kwargs={'pk': self.pk})
This is my URLs.py:
path('post/<int:post_id>/lesson_uploaded/<int:lesson_id>', LessonDeleteView.as_view(), name='lesson_delete'),
Right now this is how I am trying to insert the parameters in my html template:
{% block content %}
<div id="main">
<table class="table mb-0">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Download</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{% for l in Lesson %}
<tr>
<td>
{% if l.file %}
{{ l.title }}
{% else %}
<h6>Not available</h6>
{% endif %}
</td>
<td>{{ l.post.author }}</td>
<td>{% if l.file %}
Download
{% else %}
<h6>Not available</h6>
{% endif %}
</td>
<td> <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'lesson_delete' post.id l.id %}">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
This is my class-based view:
class LessonDeleteView(DeleteView):
model = Lesson
success_url = '../'
template_name = 'lesson_confirm_delete.html'
As you are deleting Lesson, you don't need to provide Post ID. You can simply use Lesson ID here. So try like this:
# url
path('post/lesson_uploaded/<int:pk>/', LessonDeleteView.as_view(), name='lesson_delete'), # using pk instead of lession_id, it will resolve the error you are facing
# template
<td> <a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'lesson_delete' l.id %}">Delete</a>
</td>
Update
from comment section, you can override the delete method like this:
class LessonDeleteView(DeleteView):
model = Lesson
success_url = '../'
template_name = 'lesson_confirm_delete.html'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.file.delete() # <-- added file delete code
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)
Why are you passing the lesson id.
Since Post gets deleted by default lesson would get deleted as well. Check this video to better understand how to pass the id and details on Delete View.
DeleteView(Class Based Views)

reload page automatically without needing to RE-enter URL again Django

My webpage consists of 2 parts, upper part is a section that let user to enter data while bottom part displays all the data in database in a table form. When user selects "Add" button in the upper part, the data will be saved into database and being outputted in a table in bottom part of the webpage. Is there anyway to show the table once i select the "Add" button ? Right now what the code is doing is when "Add" button is being selected, it will load a new form but then the whole table will disappear. I have to manually type this address again "http://127.0.0.1:8000/hrfinance/lscholarship/" then only the table will appear. Even with refreshing the page will not work. Below is my code in
views.py:
def scholarship(request, id=None):
query_results = []
if request.method == "POST":
form = ScholarshipForm(request.POST)
if form.is_valid():
scholarship = form.save(commit=False)
scholarship.save()
else:
form = ScholarshipForm()
id = request.GET.get('scholarship')
query_results = Scholarship.objects.all()
data = {
'query_results':query_results,
'form': form
}
return render(request, 'hrfinance/add_remove_scholarship.html', data)
models.py
class Application(models.Model):
studentID = models.CharField("Student ID", max_length=8, validators=[MinLengthValidator(8)], primary_key=True, default="")
studentName = models.CharField("Student Name", max_length=500, default="")
scholarship = models.TextField("Scholarship")
add_remove_scholarship.html
<div align="center" >
<form method="POST" onsubmit="return validation()" action="">
{% csrf_token %}
{{ form.errors }}
<p>Upload File: {{ form.doc }}</p>
<p>Faculty: {{ form.faculty }} </p>
<p>Opening date: <input id="odate" type="date" name="openDate"> </p>
<p>Closing date: {{ form.closeDate }} </p>
<input type="submit" name="AddScholarship" value="Add Scholarship" >
</form>
</div>
<br></br>
<button id="button" type="button">Delete Selected Scholarship</button>
<br></br>
{{query_results}}
<form method="POST" action="">
{% csrf_token %}
<table id="example" class="display" cellspacing="0" width="100%" border="1.5px">
<tr align="center">
<th> Scholarship </th>
<th> Faculty </th>
<th> Open Date </th>
<th> Close Date </th>
</tr>
{% for item in query_results %}
<tr align="center">
<td>{{item.doc}}</td>
<td>{{item.faculty}}</td>
<td>{{item.openDate}}</td>
<td>{{item.closeDate}}</td>
</tr>
{% endfor %}
</table>
</form>
Best practice is to use the Post/Redirect/Get pattern. This will solve your problems as well: the GET request will clear the form and show the new query results.
If the form is successfully saved, you simply return a redirect to the current page. The browser will then do a GET request. This prevents accidental duplicate form submissions when e.g. the user reloads the current page when it is still a POST request:
from django.shortcuts import redirect
def scholarship(request, id=None):
query_results = []
if request.method == "POST":
form = ScholarshipForm(request.POST)
if form.is_valid():
scholarship = form.save(commit=False)
scholarship.save()
# Return a redirect to the same page
return redirect('/path/to/current/page/')
...
def scholarship(request, id=None):
query_results = []
if request.method == "POST":
form = ScholarshipForm(request.POST)
if form.is_valid():
scholarship = form.save(commit=False)
scholarship.save()
else:
id = request.GET.get('scholarship')
query_results = Scholarship.objects.all()
form = ScholarshipForm()
data = {
'query_results':query_results,
'form': form
}
return render(request, 'hrfinance/add_remove_scholarship.html', data)
you need to move the query out side the else condition.
Although, this method would take a lot of time as the number of rows in the database increases. A better method to do this would be to use jquery Ajax method to update the data in the database and show it dynamically using javascript/Jquery once the database is updated.

Categories

Resources