I am working with two pages and I would like to click on a tag on one page, which would insert a value to a search query on another page.
So here's my views.py:
def bikes_all(request):
item_list = Bike.objects.all()
category_q = request.GET.get('cat')
if category_q:
item_list = item_list.filter(category__pk=category_q)
paginator = Paginator(item_list, 10)
page = request.GET.get('page')
try:
items = paginator.page(page)
except PageNotAnInteger:
items = paginator.page(1)
except EmptyPage:
items = paginator.page(paginator.num_pages)
context = {
'items': items,
}
return render(request, "bikes_all.html", context)
and my template:
<form method="GET" action="{% url 'core:bikes_all' %}">
<div class="form-row ">
<div class="form-group col-5">
<label for="category">Category</label>
<select id="cat" class="form-control" name="cat">
<option value="" {% if not request.GET.cat %} selected {% endif %}>Choose...</option>
{% for cat in category_list %}
<option value="{{ cat.pk }}" {% if request.GET.cat == cat.pk|slugify %} selected {% endif %}>
{{ cat }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-row">
<button type="submit" class="btn btn-outline-primary btn-md">Search</button>
</div>
</form>
and here's the a tag from another page:
<div class="col-md-4 overlay zoom">
<a href="{% url 'core:bikes_all' %}">
<div style="position:relative;">
<img src="{% static '/img/category_choice/bike33.png' %}" class="img-fluid">
<div class="card-img-overlay">
<h2 class="card-title"
style="text-align: center; color: aliceblue; position: absolute; bottom:5px;">
Road Bikes
</h2>
</div>
</div>
</a>
</div>
So I have {% url 'core:bikes_all' %} in my href, which takes to bikes_all.html , but I would like to pass a search query within that href as well. I was trying to do {% url 'core:bikes_all' request.GET.cat=2 %} or {% url 'core:bikes_all' category_q=2 %}, but it didnt work.
The search query looks like that, when I filter the results by category http://localhost:8000/bikes/all?cat=1
So my aim is to redirect user to http://localhost:8000/bikes/all?cat=2 , when he clicks on that a tag on first page.
You are getting GET parameters in your request so you need to pass that GET parameter in your url like this:
https://url?parameter=2
so set the cat=i in your bikes_all url:
{% url 'core:bikes_all' %}?cat=i
Related
I have a store page that gets entries from a Products table.
This shows products in order in the same format infinitely for how many are in the table.
`
{% for product in products %}
<div class="container2">
<div href="item" class= 'product-item'>
<div class= 'image-cont'>
<img class='product-image'src = '{{product.product_picture.url}}' alt="" >
</div>
{% if product.offer != 0 %}
<div class= 'offer-banner' >
Special Offer
</div>
{% endif %}
</div>
<div href="item" class="product-content">
<div href="item" class="product-title">
<a href="item" >{{product.name}}</a>
</div>
<div class="product-price">
<a href="item" >${{product.price}}</a>
</div>
<br>
<div class="product-desc">
<a href="item" >{{product.desc}}</a>
</div>
<br>
<div class="product-userpfp">
<a href="#" ><img src='{{product.userpfp.url}}'></a>
</div>
<br>
<div class="product-poster-name">
<a href="#" >{{product.username}}</a>
</div>
<br>
</div>
</div>
</div>
</div>
{% endfor %}
`
I want to be able to click on any product from products and get a page with the specific item I clicked on. This is my Item page.
`
{`% extends 'base.html' %}
{% load static %}
{% block css %}
<link rel="stylesheet" href= "{% static 'css\item.css' %}" >
{% endblock %}
{%block content%}
{% load static %}
<h1>Item</h1>
<h3>{{item.name}}</h3>
{% endblock %}`
`
The problem should be inside the view.py file
`
def item(request):
item = Product.objects.select_related()
return render(request, "item.html", {"item": item })
def store(request):
products = Product.objects.all()
return render(request, 'store.html', {'products': products}) ;
`
The store function works. But the item function is not working. My guess is that the 'select_related' may not be the correct tool to use.
I tried changing the select_related tool to a few different ones but was worried I would ruin my table somehow so I am seeking help, Thank You.
Assuming that you are passing product_id to the item page when a product is clicked, you need to pass that product_id to the item function and rewrite the code as,
def item(request):
item = Product.objects.select_related('item').get(id=product_id)
return render(request, "item.html", {"item": item })
I have this paginated view which caters for voting similar to Django official beginner's tutorial, but with pagination.
I observed that when I submit and redirect to the next page, i.e the next question, when I submit at this page (which is eq to 2) it redirects back to page 1 instead of page 3
def vote(request):
questions = Question.objects.all()
paginator = Paginator(questions, 1)
try:
page_number = request.GET.get('page', 1)
print('current_page: ', page_number)
page_obj = paginator.get_page(page_number)
question = page_obj.object_list.get() # object list contains the objects in the page
selected_choice = question.choice_set.get(pk=request.POST['choice'])
# increment selected choice and save
selected_choice.vote += 1
selected_choice.save()
url = f"{reverse('vote')}?page={page_obj.next_page_number()}"
return HttpResponseRedirect(url)
except (KeyError, Choice.DoesNotExist):
return render(request, 'vote.html', {
'page_obj': page_obj,
'error_message': "You didn't select a choice.",
})
except PageNotAnInteger:
page_obj = paginator.get_page(1)
except EmptyPage:
page_obj = paginator.get_page(paginator.num_pages)
context = {
'questions': questions,
"page_obj": page_obj
}
return render(request, 'vote.html', context)
URL: localhost:8000/vote/ at first instant
localhost:8000/vote/?page=2 next
Then instead of localhost:8000/vote/?page=3 it goes back to localhost:8000/vote
The navigation in the template for the view works as expected but this isn't. Thanks in advance
Update: Below is the template
<div class="container">
<!-- <h3 class="text-center mt-4">VOTE</h3> -->
{% for q in page_obj %}
<h3 class="text-center">Vote for {{q.question_post}} </h3>
<form method="post" action="{% url 'vote' %}">
{% csrf_token %}
{% for choice in q.choice_set.all %}
<label for="{{choice.id}}">
<img src="{{choice.img.url}}" class="img-fluid" width="10%" alt=""/>
<input type="radio" name="choice" id="{{choice.id}}" class="user_choice" value="{{choice.id}}"> {{choice.choice_field}}
</label> <hr>
{% endfor %}
<button class="btn btn-primary btn-md" type="submit" id="submit" >Submit</button>
</form>
</div>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
last »
{% endif %}
</span>
</div>
This is a django related question:
I am doing an assignment where we are playing around making a search bar then afterwards letting each individual search result linked to its own page with more details. In this context we are doing a job search engine and I need assistance in when you click each jobs posting, it takes you to a separate page with more info about the job. We already made templates for all the pages. I understand that we have to make a request to the api again after doing it for the view function in the search bar and also use templating got fill up out the detailed search results.Im just not sure how would I apply these concepts to the html file that w ehave.
here's the code
View function code
import requests
from django.shortcuts import render
def home(request):
context = {
'example_context_variable': 'Change me.',
}
return render(request, 'pages/home.html', context)
def search_results(request):
search_query = request.GET['searchterm']
context = {
'result_count': 0,
'search_term': search_query,
}
context['results_count'] = 0
url = 'https://jobs.github.com/positions.json?location=bay+area&description='
url += search_query
response = requests.get(url)
results_data = response.json()
job_list =[]
for result in results_data:
job_list.append(result)
context['job_results'] = job_list
return render(request, 'pages/search_results.html', context)
Search Results Page
{% extends "base.html" %}
{% block title %}
Search Results
{% endblock title %}
{% block additional_styles %}
<style>
body {
background-color: white;
}
</style>
{% endblock %}
{% block content %}
<div id="home-content" class="container">
<div class="row">
<div class="col-lg-3"></div> <!-- Column for spacing -->
<div class="col-lg-6">
<div class="mt-5 mb-3 text-center">
<h1>Search Results</h1>
</div>
<form method="GET" action="/search-results/">
<div class="input-group mb-2">
<input name="searchterm" type="text" class="form-control form-control-lg" placeholder="Let's find a job..." />
<div class="input-group-append">
<button class="btn btn-primary btn-lg">Search</button></a>
</div>
</div>
</form>
<!-- 2start -->
<p>You Searched for: {{search_term}}</p>
{% for job in job_results %}
<div class="list-group">
<a href="/detailed-search-results/" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{job.title}}</h5>
</div>
<div>
<small class="text-muted">{{job.location}}</small>
</div>
</a>
</div>
<br>
{% endfor %}
<!-- End -->
{% endblock content %}
**detailed Search Results Html file **
So far when you click on a posting it takes you to the detailed search result page without anything on it.
That is because you have hyperlinked each job posting to /detailed-search-results/.
Looking at the API response, you need to change it to job.url
Replace your for loop with this:
{% for job in job_results %}
<div class="list-group">
<a href="{{ job.url }}" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{job.title}}</h5>
</div>
<div>
<small class="text-muted">{{job.location}}</small>
</div>
</a>
</div>
<br>
{% endfor %}
I have a Django form that takes input values from users. The values are then used in making query to a table ResourceBase, which finally returns a list of filtered results.
Since the results might be a long list, I added a pagination function with "Prev" and "Next" buttons. My problem is that when I click "Prev" or "Next" button, the form gets restored into default values. And all returned results are all gone. How do I prevent this from happening?
I think the form gets reset because of "form1 = QueryForm()" when a request is not "POST". However I just have difficulty coming up with a neat solution since I'm new to Django and web dev.
In views.py:
def search(request):
if request.method == "POST":
form1 = QueryForm(data=request.POST)
layer_dict = []
if form1.is_valid():
inp_ct = form1.cleaned_data['country']
q1 = ResourceBase.objects.filter(country_name__iexact=inp_ct)
for layer in q1:
down_url = 'xxxxxxx'.format(layer.title)
view_url = 'xxxxxxx'.format(layer.title)
layer_dict.append((layer.title, down_url, view_url))
layer_dict = sorted(layer_dict, key = lambda x:x[0])
paginator = Paginator(layer_dict, 10)
page = request.GET.get('page', 1)
try:
layers = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
layers = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
layers = paginator.page(paginator.num_pages)
context = {'form1': form1, 'layers': layers}
else:
form1 = QueryForm()
context = {'form1': form1}
return render(request, 'my_app/search.html', context)
In search.html:
<br />
<h3>Pagination Test</h3>
<br /><br/>
<div class="row">
<div class="col-md-4">
<form method="POST">
{% csrf_token %}
<div class="form-controls">
{{ form1|as_bootstrap }}
</div>
<button class="btn btn-primary" type="submit" style="float: right;" title = "Click to search" ><i class="fa fa-search"></i></button>
</form>
<form method="GET">
<button class="btn btn-primary" type="submit" value="Reset" name="Reset" title="Reset all choices">Reset</button>
</form>
</div>
</div>
{% if layers %}
<div class="row">
<div class="col-md-8">
<div id = "search_results" >
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Select</th>
<th scope="col">Layer Name</th>
<th scope="col">Download</th>
<th scope="col">View Layer</th>
</tr>
</thead>
<tbody>
{% for layer in layers %}
<tr>
<td><input class= messageCheckbox type="checkbox" name="checks" value="{{layer.1}}"/></td>
<td>{{layer.0}}</td>
<td> Download Layer </td>
<td><input class="btn btn-primary" onclick="window.open('{{layer.2}}')" id="view" type="button" name="view" value="View"></td>
</tr>
{% endfor %}
<tr>
<td><input type="checkbox" onClick="toggle(this, 'checks')"/> Select All</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<button class="btn btn-primary" type="button" name="download" style="float: left;" onClick= "open_all_links();">Download Selected</button>
</div>
<div class="a_pagination" align="right">
<span class="step-links">
{% if layers.has_previous %}
<a class="btn btn-primary btn-sm" name="prev_page" href="?page={{ layers.previous_page_number }}" role="button">Prev.</a>
{% endif %}
<span class="current" style ="color:#2C689C;font-size:16px;padding:8px;">
page {{ layers.number }} of {{ layers.paginator.num_pages }}
</span>
{% if layers.has_next %}
<a class= "btn btn-primary btn-sm" href="?page={{ layers.next_page_number }}" role="button">Next</a>
{% endif %}
</span>
</div>
</div>
</div>
{% endif %}
<script type="text/javascript" >
.......
</script>
You don't need to use POST Method to pass your arguments to your views.py .
Follow the below example and rewrite your view and your html form.
here a simple form for user to enter the word for search:
<form method="get" action="">
<input type="text" name="search4" class="search_input" placeholder="Search" required="required">
<input type="submit" value="Search">
</form>
The next step is that you should check the input in your views.py, we named the input tage name="search4" so we check if there is any input in our form using this code in our views.py:
from django.db.models import Q
from django.core.paginator import Paginator
def search(request):
query = request.GET.get("search4")
if query:
queryset = ResourceBase.objects.objects.all() # this will get all of your object of your model
results = queryset.filter(Q(country_name__iexact=query)).all()
number_of_objects = results.count() # get the exact number of object to show in your html file
paginator = Paginator(results, 12) # Show 12 contacts per page
page_var = 'page' # this will use for pagination in your html file
page = request.GET.get(page_var) # this will use for pagination in your html file
contacts = paginator.get_page(page) # send only 12 object to your html file to show to user
context = {
"items": contacts,
"key": str(query),
'page': page_var,
"number_of_objects": number_of_objects,
}
return render(request=request, template_name='search.html', context=context, content_type=None, status=None,
using=None)
else:
... # if user didn't enter anything to search
After getting and searching the user input in your data base, You should show it to user in your search.html file like this:
{% for item in items %}
<div>
<div>
<div class="product_title">{{ item.title }}</div> # show the part that you want the users to see
... # rest of your item parts to show
</div>
</div>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if items.has_previous %} # check the pagination that if there is perivious pages
« first
previous
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }} # example of result : Page 1 of 13
</span>
{% if items.has_next %}
<a href="?{{ page }}={{ items.next_page_number }}"</a> # check the pagination that if there is any next or perivious pages
last » # a link to last page
{% endif %}
</span>
{{ pagination }}
this is a basic search page with Paginator, if you need any further help or question, I will be happy to help.
The code <a class= "btn btn-primary btn-sm" href="?page={{ layers.next_page_number }}" role="button">Next</a> will indeed GET the page and the form1 = QueryForm() code will result in empty form. You are on a right track here.
You have two options:
1) Change the next/prev buttons so that they are inside the form1 form and they POST stuff. It might be challenging to move them inside the same form tag.
If you target modern browsers you can use HTML5 form tag in submit (https://www.w3schools.com/tags/att_button_form.asp).
<form method="POST" id="form1">
{{ form1|as_bootstrap }}
</form>
... outside the form tag, then
<button class="btn btn-primary btn-sm" form="form1" name="next" value="{{ layers.next_page_number }}" role="button">Next</button>
You should have in request.POST the next value.
2) Initialize the QueryForm from GET params.
form1 = QueryForm(data=request.GET)
and include the form parameters into the url. For this you would need some Javascript (for example How to use an input field as query parameter to a destination?) as Django doesn't know about the values in the form on rendering time before user inserts them.
You can change the pagination links into buttons to submit the form. More detailed answer in here:
How to define which input fields the form has by pressing different buttons?
You can read my comment on the answer. It explains how the answer can be helpful in pagination.
I modified the pagination code I found in https://simpleisbetterthancomplex.com/series/2017/10/09/a-complete-beginners-guide-to-django-part-6.html
The modified code for pagination is also as below:
<nav aria-label="Topics pagination" class="mb-4">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<button form="my_form" name="page" value="{{page_obj.number|add:'-1'}}" role="button" class="btn btn-link">Previous</button>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Previous</span>
</li>
{% endif %}
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<span class="page-link">
{{ i }}
<span class="sr-only">(current)</span>
</span>
</li>
{% else %}
<li class="page-item">
<button form="my_form" name="page" value="{{i}}" role="button" class="btn btn-link">{{ i }}</button>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<button form="my_form" name="page" value="{{page_obj.number|add:1}}" role="button" class="btn btn-link">Next</button>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Next</span>
</li>
{% endif %}
</ul>
</nav>>
The voting proceess is working fine with this code. The problem is only when redirecting after voting the options.
Exception Type:DoesNotExist
Exception Value:
Category matching query does not exist.
category = Category.objects.get(slug=slug)
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/vote/', views.vote, name='vote'),
views.py
def home(request):
categories = Category.objects.filter(active=True)
return render(request,'rank/base.html',{'categories': categories,'title':'TheRanker'})
def options(request,slug):
category = Category.objects.get(slug=slug)
options = Option.objects.filter(category=category)
return render(request,'rank/options.html',{'options':options,'title':'options'})
def vote(request,slug):
option = Option.objects.get(slug=slug)
if Vote.objects.filter(slug=slug,voter_id=request.user.id).exists():
messages.error(request,'You Already Voted!')
return redirect('rank:options',slug)
else:
option.votes += 1
option.save()
voter = Vote(voter=request.user,option=option)
voter.save()
messages.success(request,'Voted!')
return redirect('rank:options',slug)
options.html
{% extends "rank/base.html" %}
<title>{% block title %}{{title}}{% endblock title%}</title>
{% load bootstrap4 %}
{% block content %}
<center><br>
<center>{% bootstrap_messages %}</center>
<ol type="1">
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="200" height="100">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% endfor %}
</ol>
</center>
{% endblock content%}
You're confusing categories and options. The form sends the slug of the option, but then you redirect to the categories view using the same slug. But those are two different models.