Django filtering using multiple queries - python

On my homepage I have a search bar and when you search something it redirects you to a page with the results(titles and document types). On the left side of the page I want to implement a filter by document type.
After the search my url looks like this: http://127.0.0.1:8000/search/?q=something
After applying the filter: http://127.0.0.1:8000/search/?document_type=Tehnical+report
I don't know how to implement the filters to search just in the objects list filtered by the query (q) on the search page. Also, I'm not sure if the url should look like this : http://127.0.0.1:8000/search/?q=something&document_type=Tehnical+report or like this http://127.0.0.1:8000/search/?document_type=Tehnical+report after applying the filter.
models.py
DOCUMENT_TYPES = [
('Tehnical report','Tehnical report'),
('Bachelor thesis','Bachelor thesis'),
...
]
class Form_Data(models.Model):
title = models.CharField(unique=True, max_length=100, blank=False)
author = models.CharField(max_length=100)
document_type = models.CharField(choices=DOCUMENT_TYPES, max_length=255, blank=False, default=None)
views.py
def search_list(request):
object_list = Form_Data.objects.none()
document_types = DOCUMENT_TYPES
query = request.GET.get('q')
query_list = re.split("\s|(?<!\d)[,.](?!\d)", query)
document_type_query = request.GET.get('document_type')
for item in query_list:
object_list |= Form_Data.objects.filter( Q(title__icontains=item) | Q(author__icontains=item))
return render(request, "Home_Page/search_results.html")
home_page.html
<div class="Search">
<form action="{% url 'home_page:search_results' %}" method="get">
<input id="Search_Bar" type="text" name="q">
<button id="Button_Search" type="submit"></button>
</form>
</div>
search_results.html
{% for form_data in object_list %}
<h5>{{ form_data.title }}</h5>
<h5>{{ form_data.document_type }}</h5>
{% endfor %}
<form method="GET" action=".">
<select class="form-control" name="document_type">
{% for tag, label in document_types %}
<option value="{{ tag }}">{{ tag }}</option>
{% endfor %}
</select>
</form>

In my opinion you are doing it the wrong way... I mean I didn't understand why you are looping your query for filtering. As far as I know it was looping every letters of your query.
I was doing it I would do it like this (using my own example):
<form action='{% url 'products:search' %}' method='get'>
<input type='text' name='q' id='search' value='' >
<select name='category' id='category'>
<option value='' selected ></option>
<option value='packet'>Packet</option>
<option value='food'>Food</option>
<option value='vegetable'>Vegetable</option>
</select>
<input type='button' value='submit' >
</form>
views.py:
def search(request):
products = None
query = request.GET.get('q')
category = request.GET.get('category')
if query:
products = Product.objects.filter(
Q(name__icontains=query)|
Q(brand__icontains=query)
)
if category:
# since it is a choice field in the model
products |= Products.objects.filter(category=category)
context = {
'products': products,
}
return render(request, 'products/search_products.html', context)
in this case if I press the submit button I would get a url like:
http://localhost:8000/products/search/?q=something&category=food
with this data I can filter products by name or any other fields I want.
I don't see any instance where someone would enter their query and search result will have all the products that has any of the letters entered in the input field.

This would be the model filtering:
query = request.GET.get('q')
document_type_query = request.GET.get('document_type')
object_list = FormData.objects.none()
for item in query.split():
item_qs = FormData.objects.filter(Q(title__icontains=item) | Q(author__icontains=item))
if document_type_query:
item_qs = item_qs.filter(document_type=document_type_query)
object_list |= item_qs
return render(request, "Home_Page/search_results.html", {"object_list": object_list})
And this is the URL:
http://127.0.0.1:8000/search/?q=something%20with%20spaces&document_type=Tehnical+report

Related

How can I display a tuple field from a model to a template in Django?

I'm pretty new to Django here guys so go easy on me please...
Let me elaborate on what the title question says. Basically I have this model....
class Meta:
verbose_name_plural = 'Digital Media'
CATEGORY_CHOICES = (
('icon_sets', 'Icon Sets'),
('brand_logos', 'Brand Logos'),
('web_banners', 'Web Banners')
)
name = models.CharField(max_length=20, choices=CATEGORY_CHOICES)
SIZE_CHOICES = (
('1616', '16 x 16 pixels'),
('3232', '32 x 32 pixels'),
('6464', '64 x 64 pixels'),
('128128', '128 x 128 pixels'),
('256256', '256 x 256 pixels')
)
sizes = models.CharField(max_length=20, choices=SIZE_CHOICES)
def __str__(self):
return self.name
and this view ...
def product_detail(request, product_id):
""" A view to show individual product details """
print_media = Print_Media.objects.all()
digital_media = Digital_Media.objects.all()
product = get_object_or_404(Product, pk=product_id)
print(product, print_media, digital_media)
context = {
'product': product,
'print_media': print_media,
'digital_media': digital_media,
}
return render(request, 'products/product_detail.html', context)
So "IF" all is ok with the above code, can someone help me to get the field "sizes" from the model onto a template as I'm having trouble doing this on my own - here is what I have tried so far...
{% with product.is_print_media as p %}
{% if p %}
<div class="col-12">
{{ p.sizes }}
<p><strong>Size:</strong></p>
<select class="form-control rounded-0 w-50" name="product_size" id="id_product_size">
<option value="{{ p.sizes }}"></option>
<option value=""></option>
<option value="" selected></option>
<option value=""></option>
<option value=""></option>
</select>
</div>
{% endif %}
{% endwith %}
Any help with this is much appreciated :)
Again - go easy on the newbie.....
In django templates , choices will be showing like this. That will return the readable value for the field,
{{ p.get_sizes_display }}.
Vote up, if this one helps you !!!
If SIZE_CHOICES is attribute of model Print_Media you can access it us p.SIZE_CHOICES in template.
Otherwise, provide it as context in your view Digital_Media.SIZE_CHOICES:
def product_detail(request, product_id):
""" A view to show individual product details """
print_media = Print_Media.objects.all()
digital_media = Digital_Media.objects.all()
product = get_object_or_404(Product, pk=product_id)
print(product, print_media, digital_media)
context = {
'product': product,
'print_media': print_media,
'digital_media': digital_media,
'category_choices': Digital_Media.CATEGORY_CHOICES
'size_choices': Digital_Media.SIZE_CHOICES
}
return render(request, 'products/product_detail.html', context)
In template size_choices or p.SIZE_CHOICES.
Assuming that you used size_choices:
{% with product.is_print_media as p %}
{% if p %}
<div class="col-12">
{{ p.sizes }}
<p><strong>Size:</strong></p>
<section class="mt-20">
<select class="form-control rounded-0 w-50" name="product_size" id="id_product_size_{{ p.pk }}">
{% for size in size_choices %}
<option value="{{ item.0 }}" {% if size.0 == p.sizes %}selected{% endif %}>{{ item.1 }}</option>
{% endfor %}
</select>
</div>
{% endif %}
{% endwith %}
For every field that has choices set, the object will have a get_FOO_display() method, where FOO is the name of the field. This method returns the “human-readable” value of the field.
so try:
{{p.get_sizes_display}}
for another example:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
we can access Small, Medium and Large in templates with (p as a Person):
{{p.get_shirt_size_display}}

Keeping the value salected in the drop-down list after the search

I have got a filter on my page, that filters my items by the category. You can choose the category from a drop-down list and then press search and the filtered content is displayed. The only problem is that, the drop-down list resets and doesn't show the category, that the current items are filtered by. Anyone knows how to solve this?
views.py
def HomeView(request):
item_list = Item.objects.all()
item_list = item_list.annotate(
current_price=Coalesce('discount_price', 'price'))
category_list = Category.objects.all()
query = request.GET.get('q')
if query:
item_list = item_list.filter(title__icontains=query)
cat = request.GET.get('cat')
if cat:
item_list = item_list.filter(category__pk=cat)
price_from = request.GET.get('price_from')
price_to = request.GET.get('price_to')
if price_from:
item_list = item_list.filter(current_price__gte=price_from)
if price_to:
item_list = item_list.filter(current_price__lte=price_to)
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,
'category': category_list
}
return render(request, "home.html", context)
html:
<form method="GET" action=".">
<div class="form-group col-md-4">
<label for="category">Category</label>
<select id="cat" class="form-control" name="cat">
<option value="" selected>Choose...</option>
<option value="" href="/home">All</option>
{% for cat in category %}
<option value="{{ cat.pk }}">
{{ cat }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
In template you can add selected attribute to value from request.GET.cat:
<option value="" {% if not request.GET.cat %} selected {% endif %}>Choose...</option>
{% for cat in category %}
<option value="{{ cat.pk }}" {% if request.GET.cat == cat.pk|slugify %} selected {% endif %}>
{{ cat }}
</option>
{% endfor %}
Try setting the value of your dropdown to what the user selects.
For example: If I selected 'Cats' then update your dropdown's default value to 'Cats'.
Basically just keep updating the default value of your dropdown and then check against that.
Hope that helps! :)

Filtering items using drop down list

Would like to filter features(products) by using dropdown menu. Each of the feature(product) got a tag (e.g. food, drink, random). Idea is when user selects the tag on a menu, it shows only those items who's got that tag.
So far I went as far, but doesn't seem to work yet. PyCharm does not give an error, but not functioning. What I am missing? Thank you!
my models.py
class Feature(models.Model):
FOOD = 'food'
DRINK = 'drink'
RANDOM = 'random'
TAGS = (
(FOOD, 'food'),
(DRINK, 'drink'),
(RANDOM, 'random')
)
name = models.CharField(max_length=40, default='')
tags = models.CharField(max_length=20, choices=TAGS, default=ALL)
def __str__(self):
return self.name
my views.py
def tags(request):
if request.GET.get('tags'):
features_filter = request.GET.get('tags')
listings = Feature.objects.filter(features_filter=features_filter)
else:
listings = Feature.objects.all()
context = {'listings': listings}
return render(request, 'index', context)
my index.html
<form action="{% url 'index' %}" method="get" accept-charset="utf-8">
{% csrf_token %}
<select name="tags">
{% for feat in features %}
<option value="{{feat.tags}}">{{ feat.tags }}</option>
{% endfor %}
</select>
<input type="submit" value="submit">
</form>
{% for feature in features %}
<h1{{ feature.name }}</strong></h1>
{% endfor %}
Try changing this
listings = Feature.objects.filter(features_filter=features_filter)
to
listings = Feature.objects.filter(tags=features_filter)

Django - Saving multiple selection boxes in one form

I need to save multiple selections in one form, but it is not saving the values to my model. This is how the form looks:
This is my models.py
class ChoiceManager(models.Manager):
def rates (self, Task_id, rating2, yo):
assignment = Assignment.objects.get(id=Assignment_id)
rating = rating2
yo = FiscalYear.objects.get(fy_year=years)
for i in range(len(rating2)):
rated = Prog_capability.objects.create(
task = task,
rating2 = rating[i],
fy = yo[i]
)
class NewYear(models.Model):
year = models.CharField(max_length=5)
new_year = models.CharField(max_length=5)
class Choice(models.Model):
rating = models.CharField(max_length=255, blank=True, null=True)
year = models.ForeignKey(NewYear, related_name="choices")
assignment = models.ForeignKey(Assignment, related_name="choice")
objects = ChoiceManager()
This is my views.py
def task_rating(request, Assignment_id):
ratings = request.POST.getlist('rating2',[])
years= request.POST.getlist('yo",[])
rates= Choice.objects.rates(Assignment_id, ratings, years)
return redirect ((reverse('Project:assignment_page', kwargs={'Assignment_id': Assignment_id})))
HTML
<form action="{% url 'project:rating' %}" method="post">
{% csrf_token %}
{% for year in years %}
<li class=cap_select>
<div id=fyc>{{year.fy_year}}</div>
<select name="rating2" id="{{assignment.id}}-{{year.id}}">
<option>Choose From List</option>
<option class=hi value="high">High</option>
<option class=med value="medium">Medium</option>
<option class=low value="low">Low</option>
<option class=na value="n/a">N/A</option>
</select>
<input type="hidden" name="yo" value={{year.fy_year}}>
</li>
{% endfor %}
<br>
<input id=save_cap type="submit" value="Save">
</form>
I'm getting a
"NewYear matching query does not exist." and cannot save the data.
I've been on this for a couple of days. Any help will be appreciated.
Yes you are right
POST data is not coming as dictionary.
You are looping on years and rendering select and input tag inside form and the name attribute of all select and input tags are same that is rating and year simultaneously
in that case when you submit your form you will get a list of rating and year so you should use getlist() method while fetching it from request.POST QueryDict.
request.POST.getlist('rating[]')
request.POST.getlist('year[]')
if above doesn't work use it like
request.POST.getlist('rating', [])
request.POST.getlist('year', [])
Update your rates method like.
class ChoiceManager(models.Manager):
def rates (self, Assignment_id, rating, year):
...
# you can use year also to find the length
for i in range(len(rating)):
rated = Choice.create(
assignment = assignment,
year = year[i],
rating = rating[i]
)
change method call.
rates = Choice.objects.rates(Assignment_id,request.POST.getlist('rating[]'), request.POST.getlist('year[]'))
So this is what worked in the end:
models.py
class ChoiceManager(models.Manager):
def rates (self, Assignment_id, rating2, years, rating_id):
Assignment = Assignment.objects.get(id=Assignment_id)
rating = rating2
rating_id = rating_id
for i in range(len(rating2)):
year =NewYear.objects.get(fy_year=years[i])
rated = Choice.objects.create(
assignment = assignment,
rating = rating[i],
fy = year,
rating_id = rating_id[i]
)
views.py
def task_rating(request, Assignment_id):
ratings= request.POST.getlist('rating2',[])
years= request.POST.getlist('yo',[])
rating_id = request.POST.getlist('rating_id',[])
rates = Choice.objects.rates(Assignment_id,ratings, years,rating_id)
return redirect ((reverse('assignment:assignment_page', kwargs={'Assignment_id': Assignement_id})))
HTML
<form action="{% url 'project:rating' %}" method="post">
{% csrf_token %}
{% for year in years %}
<select name="rating2" id="{{assignment.id}}-{{year.id}}">
<option>Choose From List</option>
<option class=hi value="high">High</option>
<option class=med value="medium">Medium</option>
<option class=low value="low">Low</option>
<option class=na value="n/a">N/A</option>
</select>
<input type="hidden" name="yo" value={{year.fy_year}}>
<input type="hidden" name="rating_id" value="{{tasks.id}}-{{year.id}}">
{% endfor %}
<br>
<input id=save_cap type="submit" value="Save">
</form>

Using Django taggit output is Id of tags insted of name.How can I change my output into tags names?

I have one field that is custom tag.
When I get that particular field from model and try to print all stored values in that field, It will displays as Id's of each value.
How can I get that values which I had stored as string.
views.py
def patfirst(request):
if request.method == "GET":
return render(request, 'personal/patfirst.html')
if request.POST.get('Next'):
newSymp = request.POST.get('newSymptom')
didata = Disease.objects.all().values_list('symptoms')
args = {'newSymp' : newSymp,'didata':didata}
return render(request, 'personal/patfirst.html',args)
models.py
class TaggedSymptoms(TaggedItemBase):
content_object = models.ForeignKey("Disease")
class Disease(models.Model):
did = models.AutoField(verbose_name='Disease Id', primary_key=True,default=0)
dName = models.CharField(max_length=100)
symptoms = TaggableManager(verbose_name='symptoms list', through=TaggedSymptoms)
symptoms.rel.related_name = "+"
patfirst.html
<h1>search disease</h1>
<form method="post" action="#">
{% csrf_token %}
Enter Symptom: <input type="text" name="newSymptom"/><br><br>
<h3>
{% for s in didata %}
{{ s }}
{% endfor %}
</h3>
<input type="submit" value="Next" name="Next"/>
<input type="submit" value="None of these" name="NoneOfThese"/>
</form>
output I got is like this:
Without you providing much detail, I can guess that you can do so by printing it this way.(provide more information in order to get a more precise answer)
tags = your_object.tags
for tag in tags:
print tag, tag.your_string_field

Categories