I want to display titles from 2 models in my autocomplete feature in the search bar, however, I am not sure how to write a loop in search_address_qa function that would display titles from multiple models.
For now, I can only properly display address_objects (Article) or address_objects_qa(QA) but not both. The results from search_items are fine. When I try to put for address_object in address_objects_qa or address_objects then I get some random results.
models.py
class QA(models.Model):
title=models.CharField(max_length=1000, help_text='max 1000 characters')
body = RichTextField(verbose_name='Description', blank=True)
class Article(models.Model):
title=models.CharField(max_length=1000, help_text='max 1000 characters')
body = RichTextField(verbose_name='Description', blank=True)
views.py
#login_required
def search_address_qa(request):
query = request.GET.get('title')
payload = []
if query:
lookups = Q(title__icontains=query)
address_objects = Article.objects.filter(lookups, status=1)
address_objects_qa = QA.objects.filter(lookups, status=1)
for address_object in address_objects_qa:
payload.append(address_object.title)
return JsonResponse({'status':200, 'data': payload})
#login_required
def search_items(request):
query = request.GET.get('q')
article = Article.objects.filter(title__icontains=query)
qa_list = QA.objects.filter(title__icontains=query)
if query is not None:
lookups = Q(title__icontains=query) |Q(body__icontains=query)
article = Article.objects.filter(lookups).distinct()
qa_list = QA.objects.filter(lookups).distinct()
context = {
'query_name': query,
'search_items': article,
'qa_list': qa_list,
}
return render(request, 'search/search_items.html', context)
search_items.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=yes" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/#trevoreyre/autocomplete-js/dist/style.css" />
</head>
<body>
<main class="container-fluid">
<!--HEADER-->
<header class="header" id="header">
<h3 class="col-6">Search results for: {{ query_name }}</h3>
<!--SEARCH BAR-->
<button class="col-6" onclick="openSearch()"><i class="fas fa-search fa-sm"></i></button>
<form action="{% url 'search_items' %}" method="get" id="search">
{% csrf_token %}
<div class="searchbar" id="autocomplete">
<input name="q" type="text" placeholder="Search..." class="search_input">
<button class="search_icon"><i class="fas fa-search fa-sm"></i></button>
<ul class="autocomplete-result-list"></ul>
</div>
</form>
<!--END SEARCH BAR-->
</header>
<!--END HEADER-->
{% if qa_list %}
<section class="py-3">
<h2>Q&A's</h2>
<hr>
<div class="row">
{% for qa in qa_list %}
<div class="col-10">
{{qa.title}}
</div>
{% endfor %}
</div>
</section>
{% endif %}
{% if search_items %}
<section class="py-3">
<h2>Q&A's</h2>
<hr>
<div class="row">
{% for article in search_items %}
<div class="col-10">
{{article.title}}
</div>
{% endfor %}
</div>
</section>
{% endif %}
</main>
<!--JS files-->
<script src="https://unpkg.com/#trevoreyre/autocomplete-js"></script>
As suggested in comments I used itertools.chain to iterate over 2 models. I am posting the code below. Maybe it will be useful for someone.
from itertools import chain
#login_required
def search_title(request):
query = request.GET.get('title')
payload = []
if query:
lookups = Q(title__icontains=query)
optimazon_titles = Article.objects.filter(lookups, status=1)
qa_titles = QA.objects.filter(lookups, status=1)
payload = map(lambda x: x.title, chain(optimazon_titles, qa_titles))
return JsonResponse({'status':200, 'data': list(payload)})
Related
I got a problem, I'm making a web for SQLServer Agent Jobs execution, where if my job got parameters it redirects me to another page where I can enter my parameters. My problme is submiting these parameters. I don't know how to send all the parameters from my html to my view.py. All inputs have different names and I really don't know how to make the request accept different input names. I'll add some code below. IM commenting the line where I have the problem
parameters.html
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %} Parametros {% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
</head>
<div class="container">
<div class="row">
<div class="col-md-4 offset-md-4">
<br><br>
<div class="card">
<div class="card-header">
<h3>Parámetros</h3>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
{% for i in parameters %}
<!-- Parameters input -->
<div class="form-outline mb-4">
<label class="form-label" for="form2Example1">{{ i.parameter_name }}</label>
{% if i.parameter_type == 'Int' %}
<input type="number" id="form2Example1" class="form-control" name="int" />
{% else %}
{% if i.parameter_type == 'Decimal' %}
<input type="text" id="form2Example1" class="form-control" name="decimal" />
{% else %}
{% if i.parameter_type == 'String' %}
<input type="text" id="form2Example1" class="form-control" name="string" />
{% else %}
{% if i.parameter_type == 'Date' %}
<input type="date" id="form2Example1" class="form-control" name="date" />
{% endif %}
{% endif %}
{% endif %}
{% endif %}
</div>
{% endfor %}
<!-- Submit button -->
<button type="submit" class="btn btn-primary btn-block mb-4">Ejecutar</button>
</form>
</div>
</div>
</div>
</div>
</div>
views.py
#job execution
def ejecutar(request, job_name):
cursor = connection.cursor()
job = Jobs.objects.get(job_name=job_name)
if job.flag_params == 0:
cursor.execute("EXEC msdb.dbo.sp_start_job '" + job.job_name + "'")
return HttpResponse("""<html><script>alert("Job ejecutado");window.location.replace('/home');</script></html>""")
else:
#redirects to parameters.html
redirect('parameters.html', job_name=job.job_name)
#get job parameters
parameters = Parametros.objects.filter(job_id=job.id)
if request.method == 'POST':
for parameter in parameters:
#update parameters
#Here I got the problem, in request.POST(), how can I get different input names from parameters.html (decimal, int, string, date)
cursor.execute("UPDATE Jobs_parametros SET parameter_value='" + request.POST['decimal'] + "' WHERE parameter_name='" + parameter.parameter_name + "' AND job_id='" + str(job.id) + "'")
cursor.execute("EXEC msdb.dbo.sp_start_job '" + job.job_name + "'")
return HttpResponse("""<html><script>alert("Job ejecutado");window.location.replace('/home');</script></html>""")
return render(request, 'parameters.html', {'parameters': parameters})
models.py
class Usuarios(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=100)
class Jobs(models.Model):
id = models.AutoField(primary_key=True)
job_name = models.CharField(max_length=100)
flag_activo = models.BooleanField(default=True)
flag_params = models.BooleanField(default=False)
class DetailJobUser(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(Usuarios, on_delete=models.CASCADE)
job = models.ForeignKey(Jobs, on_delete=models.CASCADE)
class Parametros(models.Model):
id = models.AutoField(primary_key=True)
job = models.ForeignKey(Jobs, on_delete=models.CASCADE)
parameter_name = models.CharField(max_length=100)
parameter_type = models.CharField(max_length=100)
parameter_value = models.CharField(max_length=100)
Thanks for your answers :D
views.py
import datetime
from .filters import MyModelFilter
from django.shortcuts import render
import pymysql
from django.http import HttpResponseRedirect
from facligoapp.models import Scrapper
from django.db.models import Q
from django.utils import timezone
import pytz
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
users = ""
def index(request):
if request.method == "POST":
from_date = request.POST.get("from_date")
f_date = datetime.datetime.strptime(from_date,'%Y-%m-%d')
print(f_date)
to_date = request.POST.get("to_date")
t_date = datetime.datetime.strptime(to_date, '%Y-%m-%d')
print(t_date)
get_records_by_date = Scrapper.objects.all().filter(Q(start_time__date=f_date)|Q(end_time__date=t_date))
print(get_records_by_date)
filtered_dates = MyModelFilter(request.GET,queryset=get_records_by_date)
page = request.GET.get('page', 1)
paginator = Paginator(filtered_dates.qs, 5)
global users
try:
users = paginator.get_page(page)
except PageNotAnInteger:
users = paginator.page(1)
except EmptyPage:
users = paginator.page(paginator.num_pages)
else:
roles = Scrapper.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(roles, 5)
try:
users = paginator.page(page)
except PageNotAnInteger:
users = paginator.page(1)
except EmptyPage:
users = paginator.page(paginator.num_pages)
return render(request, "home.html", {"users": users})
return render(request, "home.html", {"users": users})
filters.py:
import django_filters
from.models import Scrapper
class MyModelFilter(django_filters.FilterSet):
class Meta:
model = Scrapper
# Declare all your model fields by which you will filter
# your queryset here:
fields = ['start_time', 'end_time']
home.html
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<body>
<style>
h2 {text-align: center;}
</style>
<h1>Facilgo Completed Jobs</h1>
<form action="" method="post">
{% csrf_token %}
<label for="from_date">From Date:</label>
<input type="date" id="from_date" name="from_date">
<label for="to_date">To Date:</label>
<input type="date" id="to_date" name="to_date">
<input type="submit"><br>
</form>
<div class="container">
<div class="row">
<div class="col-md-12">
<h2>Summary Details</h2>
<table id="bootstrapdatatable" class="table table-striped table-bordered" width="100%">
<thead>
<tr>
<th>scrapper_id</th>
<th>scrapper_jobs_log_id</th>
<th>external_job_source_id</th>
<th>start_time</th>
<th>end_time</th>
<th>scrapper_status</th>
<th>processed_records</th>
<th>new_records</th>
<th>skipped_records</th>
<th>error_records</th>
</tr>
</thead>
<tbody>
{% for stud in users %}
{% csrf_token %}
<tr>
<td>{{stud.scrapper_id}}</td>
<td>{{stud.scrapper_jobs_log_id}}</td>
<td>{{stud.external_job_source_id}}</td>
<td>{{stud.start_time}}</td>
<td>{{stud.end_time}}</td>
<td>{{stud.scrapper_status}}</td>
<td>{{stud.processed_records}}</td>
<td>{{stud.new_records}}</td>
<td>{{stud.skipped_records}}</td>
<td>{{stud.error_records}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if users.has_other_pages %}
<ul class="pagination">
{% if users.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% if user.number|add:'-4' > 1 %}
<li>…</li>
{% endif %}
{% for i in users.paginator.page_range %}
{% if users.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% elif i > users.number|add:'-5' and i < users.number|add:'5' %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if users.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
</div>
</div>
</body>
</html>
I need to get only the datas which I have filtered et_records_by_date = Scrapper.objects.all().filter(Q(start_time__date=f_date)|Q(end_time__date=t_date)) in pagination. But when I click the next page its showing different datas. Is there any solution to get only the datas for the particular query. When I post the datas the of dates the 1st pages is showing the correct details but when I click page 2 its showing the other datas
When you click on 'next page' you're performing a GET request, and not a POST request. Which means it will go into the else block, which has no filtering but just returns all the Scrapper objects.
You're better off including the from_date and to_date in a GET request and not using a POST request.
If you're using a form you can simply set the method:
<form method="GET" action="..." />
Shared method
def paginate(request,obj,total=25):
paginator = Paginator(obj,total)
try:
page = int(request.GET.get('page', 1))
except:
page = 1
try:
obj_list = paginator.page(page)
except(EmptyPage,InvalidPage):
obj_list = paginator.page(paginator.num_pages)
return obj_list
View
users = UserInfo.objects.all()
data = {
'users': paginate(request,users,15),
}
return render(request,self.template_name,data)
HTML
{% include './pagination.html' with obj=users %}
pagination.html file
{% if obj.paginator.num_pages > 1 %}
<div class="text-center">
<ul class="pagination">
<li class="{% if not obj.has_previous %} disabled{% endif %}">
{% if obj.has_previous %}
<a data-page="{{obj.previous_page_number}}" href="?page={{obj.previous_page_number}}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}" aria-label="{% trans 'Previous' %}" tabindex="-1"> « </a>
{% else %}
<a data-page="0" class="" href="javascript:void(0);" tabindex="-1">«</a>
{% endif %}
</li>
{% for i in obj.paginator.page_range %}
{% if obj.number == i %}
<li class="active">
<a data-page="0" class="" href="javascript:void(0)">{{ i }}</a>
</li>
{% elif i > obj.number|add:'-5' and i < obj.number|add:'5' %}
<li class=""><a data-page="{{i}}" class="" href="?page={{i}}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}">{{ i }}</a>
</li>
{% endif %}
{% endfor %}
<li class="{% if not obj.has_next %} disabled{% endif %}">
{% if obj.has_next %}
<a data-page="{{ obj.next_page_number }}" class="" href="?page={{obj.next_page_number}}{% for key, value in request.GET.items %}{% if key != 'page' %}&{{ key }}={{ value }}{% endif %}{% endfor %}" tabindex="-1">»</a>
{% else %}
<a data-page="0" class="" href="javascript:void(0);" tabindex="-1">»</a>
{% endif %}
</li>
</ul>
</div>
{% endif %}
My website lets a teacher create multiple questions for an assignment. Once the assignment is created, a student can come and write an answer for each question.
My problem is that only one answer is saved for each entry. For example, there are two questions in an assignment. The answer for question 1 is "asdf", the answer for question 2 is "fdsa". The content for both answers will be saved as "asdf", when they should be unique.
I have tried printing request.form and it looks like this (excluding the csrf_tokens):
('code_content', 'asdf'), ('code_content', 'fdsa'), ('submit', 'Submit Assignment')]
So I know that fdsa is still in there somewhere, but I'm not able to access it. If this is important, there were two csrf_tokens that were the exact same when printing request.form.
To get that data, I created a 'GetQuestionContent()' form for as many questions in the assignment. Like this:
questions = []
question_content_forms = []
for question in Question.query.all():
if int(question.owner) == int(assignment_id):
questions.append(question)
question_content_forms = [GetQuestionContent() for item in range(0, len(questions))]
Then, in the HTML, I write the form like this:
<form method="POST">
{% for question in questions %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{ question.id }}">
<button class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse{{ question.id }}"
aria-expanded="false"
aria-controls="collapse{{ question.id }}">
{{ question.title }}
</button>
</h2>
<div id="collapse{{ question.id }}"
class="accordion-collapse collapse"
aria-labelledby="heading{{ question.id }}"
data-bs-parent="#questionsAccordion">
<p>
<small>
<strong>Question Description</strong>
<br>
{% if question.description != "" %}
{{ question.description|safe }}
{% else %}
<em>The assignment creator did not provide a description for this question.</em>
{% endif %}
</small>
</p>
{{ question_content_forms[loop.index - 1].hidden_tag() }}
{% if question.type == "code" %}
<div class="code-box">
{{ question_content_forms[loop.index - 1].code_content(id = "editor") }}
</div>
{% endif %}
{% if question.type == "text" %}
{{ question_content_forms[loop.index - 1].text_content|safe }}
{% endif %}
</div>
</div>
{% endfor %}
{% if current_user.account_type == "Student" %}
{{ submit_button.submit(class="btn btn-outline-success mt-3", value="Submit Assignment")}}
{% endif %}
</form>
When the user presses submit, I want to get every answer and put it into their own entry for the StudentQuestionSubmission table in my database. This is what that code looks like:
if request.method == "POST" and submit_button.data:
print(request.form)
index = 0
for question in questions:
question_to_submit = StudentQuestionSubmission(question_id = int(question.id),
student_id = int(current_user.id))
if question.type == "code":
question_to_submit.question_content = question_content_forms[index].code_content.data
elif question.type == "text":
question_to_submit.question_content = question_content_forms[index].text_content.data
print(f"\n\n{ question_content_forms[index].code_content.data } \n \
{ question_content_forms[index].text_content.data } \n\n")
index += 1
db.session.add(question_to_submit)
assignment_to_submit = StudentAssignmentSubmission(assignment_id = int(assignment_id),
student_id = int(current_user.id),
has_submitted = True,
submission_date = date.today())
db.session.add(assignment_to_submit)
db.session.commit()
flash(f"'{assignment.name}' has been succesfully submitted.")
return redirect(url_for('classroom_assignments_list', class_id = class_id, paper_id = paper_id))
You can see that I print the data of the textboxes. It will output 'asdf' on both iterations even if I wrote something entirely different for question 2.
I appreciate any help. Thank you.
EDIT: 'hackily' getting the content from multiple instances of the same form using request.form.to_dict(flat=False)['your_form_field']
Here's the new code:
if request.method == "POST" and submit_button.data:
code_content = request.form.to_dict(flat=False)['code_content']
text_content = request.form.to_dict(flat=False)['text_content']
code_content_index = 0
text_content_index = 0
for question in questions:
question_to_submit = StudentQuestionSubmission(question_id = int(question.id),
student_id = int(current_user.id))
if question.type == "code":
question_to_submit.question_content = code_content[code_content_index]
code_content_index += 1
elif question.type == "text":
question_to_submit.question_content = text_content[text_content_index]
text_content_index += 1
print(f"\n\n{ question_to_submit.question_content } \n\n")
db.session.add(question_to_submit)
assignment_to_submit = StudentAssignmentSubmission(assignment_id = int(assignment_id),
student_id = int(current_user.id),
has_submitted = True,
submission_date = date.today())
db.session.add(assignment_to_submit)
db.session.commit()
flash(f"'{assignment.name}' has been succesfully submitted.")
return redirect(url_for('classroom_assignments_list', class_id = class_id, paper_id = paper_id))
My suggestion is not to create multiple forms, but to dynamically create one form for all the necessary questions.
In this case it is possible to assign a unique id to each field, which indicates that it belongs to the question.
The following example shows you a possible implementation.
An answer field is added to the form for each question, which has the id of the question in its name. Thus, the respective question can be assigned when rendering and querying the input data.
Flask
from flask import (
Flask,
render_template,
request
)
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import TextAreaField, SubmitField
from wtforms.validators import DataRequired
import random
app = Flask(__name__)
app.secret_key = 'your secret here'
db = SQLAlchemy(app)
class Question(db.Model):
id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String)
owner = db.Column(db.Integer)
title = db.Column(db.String)
description = db.Column(db.Text)
with app.app_context():
db.drop_all()
db.create_all()
qs = [Question(
type=random.choice(['code', 'text']),
owner=1,
title=f'Question {i+1}',
description=f'Your description here.'
) for i in range(3)]
db.session.add_all(qs)
db.session.commit()
def form_factory(qs):
class F(FlaskForm):
submit = SubmitField('Submit')
for q in qs:
field = TextAreaField(
q.type.title(),
validators=[
# DataRequired()
],
)
setattr(F, f'q-{q.id}', field)
return F
#app.route('/', methods=['GET', 'POST'])
def index():
questions = Question.query.filter_by(owner=1).all()
form = form_factory(questions)(request.form)
if form.validate_on_submit():
for q in questions:
field = getattr(form, f'q-{q.id}')
print(f'Question-{q.id}\n{field.data}\n')
return render_template('index.html', **locals())
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
</head>
<body>
<div class="container my-3">
<form method="post">
{{ form.hidden_tag() }}
<div class="accordion mb-3" id="accordionExample">
{% for q in questions -%}
<div class="accordion-item">
<h2 class="accordion-header" id="heading-{{loop.index}}">
<button
class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapse-{{loop.index}}"
aria-expanded="false"
aria-controls="collapse-{{loop.index}}"
>
{{ q.title }}
</button>
</h2>
<div
id="collapse-{{loop.index}}"
class="accordion-collapse collapse"
aria-labelledby="heading-{{loop.index}}"
data-bs-parent="#accordionExample"
>
<div class="accordion-body">
<div class="mb-3">
{{ q.description|safe }}
</div>
{% set field = form|attr('q-{}'.format(q.id)) -%}
<div>
{{ field.label(class_='form-label') }}
{{ field(class_='form-control' + ('', ' editor')[q.type=='code']) }}
</div>
</div>
</div>
</div>
{% endfor -%}
</div>
<div class="d-grid gap-2">
{{ form.submit(class_='btn btn-primary') }}
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</body>
</html>
I'm trying to build a website that generates a power hour style video from a list of music videos that the user selects by searching through the youtube api. I'm having trouble figuring out how to append selected videos to a list (addedVideos) in view.py from a button (add) in my html. I need to append each added video to a list so I can display them and loop through them in an embedded youtube player. This is my first django project so I don't have a good understanding of what the potential problems could be. index.html and views.py below:
views.py
import requests
from isodate import parse_duration
from django.conf import settings
from django.shortcuts import render, redirect
def index(request):
addedVideos = []
videos = []
if request.method == 'POST':
search_url = 'https://www.googleapis.com/youtube/v3/search'
video_url = 'https://www.googleapis.com/youtube/v3/videos'
search_params = {
'part' : 'snippet',
'q' : request.POST['search'],
'key' : settings.YOUTUBE_DATA_API_KEY,
'maxResults' : 3,
'type' : 'video'
}
#print(request.POST['submit'])
r = requests.get(search_url, params=search_params)
#print(r)
results = r.json()['items']
#print(results)
video_ids = []
for result in results:
video_ids.append(result['id']['videoId'])
if request.POST['submit'] == 'lucky':
return redirect(f'https://www.youtube.com/watch?v={ video_ids[0] }')
video_params = {
'key' : settings.YOUTUBE_DATA_API_KEY,
'part' : 'snippet,contentDetails',
'id' : ','.join(video_ids),
'maxResults' : 3
}
r = requests.get(video_url, params=video_params)
results = r.json()['items']
for result in results:
video_data = {
'title' : result['snippet']['title'],
'id' : result['id'],
'url' : f'https://www.youtube.com/watch?v={ result["id"] }',
'duration' : int(parse_duration(result['contentDetails']['duration']).total_seconds() // 60),
'thumbnail' : result['snippet']['thumbnails']['high']['url']
}
videos.append(video_data)
if request.POST['add'] == 'addValue':
print("add clicked")
context = {
'videos' : videos,
'addedVideos': addedVideos
}
return render(request, 'search/index.html', context)
index.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Search YouTube</title>
<!-- Bootstrap core CSS -->
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="{% static 'search/album.css' %}" rel="stylesheet">
</head>
<body>
<main role="main">
<section class="jumbotron text-center">
<div class="container">
<h1 class="jumbotron-heading">Build Your Power Hour</h1>
<p class="lead text-muted">Select music videos to add to your power hour</p>
<form method="POST">
<div class="input-group mb-3">
{% csrf_token %}
<input type="text" name="search" class="form-control" aria-label="Username">
</div>
<p>
<button type="submit" name="submit" value="search" class="btn btn-primary my-2">YouTube Search</button>
<button type="submit" name="submit" value="lucky" class="btn btn-secondary my-2">I'm Feeling Lucky</button>
</p>
</form>
</div>
</section>
<div class="album py-5 bg-light">
<div class="container">
<div class="row">
{% for video in videos %}
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<img class="bd-placeholder-img card-img-top" width="100%" height="225" src="{{ video.thumbnail }}" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: Thumbnail"></img>
<div class="card-body">
<p class="card-text">{{ video.title }}</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<form method="POST">
{% csrf_token %}
<button type="submit" name="add" value="addValue" id='{{ video }}' class="btn btn-sm btn-outline-secondary">Add</button>
</form>
</div>
<small class="text-muted">{{ video.duration }} mins</small>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</main>
</body>
</html>
Running this as is gives a MultiValueDictKeyError at 'q' : request.POST['search'],
Any help would be appreciated, thanks.
You should put / in front of search/index.html instead of search/index.html.
And you don't have to have the https:// you can just put // instead.
so I added a new page to my website to search for 'people' items. The thing is, whenever I search for an item, the result shows in my command line but it's not printed in the website, the only thing that is are the tags but with the empty result, like this:
Gender:
Age:
Eye color:
Film:
So the result of the query is not printed out. Why is this happening?
This is my view:
from django.shortcuts import render, HttpResponse
from django.views.generic import ListView
import requests
def people(request):
people = []
if request.method == 'POST':
people_url = 'https://ghibliapi.herokuapp.com/people/'
search_params = {
'people' : 'name',
'people' : 'gender',
'people' : 'age',
'people' : 'eye_color',
'q' : request.POST['search']
}
r = requests.get(people_url, params=search_params)
results = r.json()
if len(results):
for result in results:
people_data = {
'Name' : result['name'],
'Gender': result['gender'],
'Age' : result['age'],
'Eye_Color' : result['eye_color']
}
people.append(people_data)
else:
message = print("No results found")
print(people)
context = {
'people' : people
}
return render(request,'core/people.html', context)
This is the html:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Ghibli Studio | People</title>
<link rel="stylesheet" href="{% static 'core/people.css' %}">
</head>
<body>
<div class=" header">
</div>
<div class="wrap">
<form action='/people' method="POST">
{% csrf_token %}
<div class="search">
<input type="text" name="search" class="searchTerm" placeholder="Type character name, gender, age, or eye color">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<button type="submit" class="searchButton">
<i class="fa fa-search" style="font-size:24px"></i>
</button>
</div>
</form>
</div>
{% if people %}
{% for p in people %}
<div>
<ul class="result">
<style>
#import url('https://fonts.googleapis.com/css2?family=Dosis:wght#300&display=swap');
</style>
<h4>{{people.Name}}</h4>
<h5 style="color:lightslategray;">
Gender: {{people.Gender}} <br>
Age: {{people.Age}}
<br>Eye color: {{people.Eye_color}}
<br>Film: {{people.Film}}
</h5>
</ul>
{% endfor %}
{% endif %}
</div>
</body>
</html>
The name of an individual person in your template is p, not people, so:
{% for p in people %}
<div>
<ul class="result">
<style>
#import url('https://fonts.googleapis.com/css2?family=Dosis:wght#300&display=swap');
</style>
<h4>{{ p.Name }}</h4>
<h5 style="color:lightslategray;">
Gender: {{ p.Gender }}<br>
Age: {{ p.Age }}
<br>Eye color: {{ p.Eye_color }}
<br>Film: {{ p.Film }}
</h5>
</ul>
</div>
{% endfor %}