Is there an effcient way to perform search query in django? - python

I'm creating a blog in which i want to perform a search query based on ones rating (1-5). Here my search would be like query:"smart phone tech updates", rating:"3". Result should be list of post that contains query word(at least one word) which has rating 3, in a sorted way on val(for each query word, if found in title val+=1 if found in content val+=0.4).
My models.py file has the following :
class Post(models.Model):
title = models.CharField(max_length=50)
content = models.CharField(max_length=500)
rating = models.IntegerField(default=1)
enter code here
date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
My view.py file has the following:
def search(request):
contents = Post.objects.all()
if request.method == 'GET':
query = request.GET['query']
rating = request.GET['rating']
# search function
# contents = InSearch.search_fun(contents,query,rating)
vector = SearchVector('title', weight='A') + SearchVector('content', weight='B')
qry = SearchQuery(str(query))
contents = Post.objects.annotate(rank=SearchRank(vector, qry)).order_by('-rank')
#print("---->\n\t"+query+ str(contents))
context = {
'contents': contents
}
else:
context = {
'contents': Post.objects.all()
}
return render(request, 'feed/home.html', context)
My urls.py is:
urlpatterns = [
#...
path('feed/search-result/', views.search, name='feed_search'),
]
I'm getting this error
django.db.utils.OperationalError: no such function: plainto_tsquery

You can try like this in your views for searching.
from django.db.models import Q
def search(request):
q = request.GET.get('q')
if q:
search_results = Post.objects.filter(Q(title__icontains=q)|Q(rating=q))
# if you want the exact then do Post.objects.filter(Q(title__iexact=q) & Q(rating=q))
return render(request, 'feed/home.html','search_results':search_results)
else:
messages.info(request,'no results found for {}',format(q))
If you want to sort search query result by number of matches then you can try like this:
search_results = Post.objects.filter(Q(title__icontains=q)|Q(rating=q)).annotate(title_counts=Count('title')).order_by('-title_counts')
And in your template give name='q' in the search form.
<form action="{% url 'your search action' %}">
<input type="text" name="q">
<input type='submit' value='Search'>
</form>

Related

Display PDF in django

I need to display a pdf file in a browser, but I cannot find the solution to take the PDF for the folder media, the PDF file was save in my database, but I cannot show.
my urls.py:
urlpatterns = [
path('uploadfile/', views.uploadFile, name="uploadFile"),
path('verPDF/<idtermsCondition>', views.verPDF, name='verPDF'),
]
my models.py:
class termsCondition(models.Model):
title = models.CharField(max_length=20, verbose_name="title")
uploadPDF = models.FileField(
upload_to="PDF/", null=True, blank=True)
dateTimeUploaded = models.DateTimeField(auto_now_add=True)
deleted_at = models.DateTimeField(
auto_now=False, verbose_name="Fecha eliminacion", blank=True, null=True)
class Meta:
verbose_name = "termsCondition"
verbose_name_plural = "termsConditions"
my views.py:
def uploadFile(request):
user = request.user
if user.is_authenticated:
if user.is_admin:
if request.method == "POST":
# Fetching the form data
fileTitle = request.POST["fileTitle"]
loadPDF = request.FILES["uploadPDF"]
# Saving the information in the database
termscondition = termsCondition.objects.create(
title=fileTitle,
uploadPDF=loadPDF
)
termscondition.save()
else:
listfiles = termsCondition.objects.all()[:1].get()
return render(request, 'subirTerminos.html', context={
"files": listfiles
})
else:
messages.add_message(request=request, level=messages.SUCCESS,
message="No tiene suficientes permisos para ingresar a esta página")
return redirect('customer')
else:
return redirect('login2')
def verPDF(request, idtermsCondition):
user = request.user
if user.is_authenticated():
if user.is_admin:
getPDF = termsCondition.objects.get(pk=idtermsCondition)
seePDF = {'PDF': getPDF.uploadPDF}
print(seePDF)
return render(request, 'subirTerminos.html', {'termsCondition': getPDF, 'uploadPDF': getPDF.uploadPDF})
else:
messages.error(request, 'Do not have permission')
else:
return redirect('login2')
my html:
<div>
<iframe id="verPDF" src="media/PDF/{{ uploadPDF.url }}"
style="width:800px; height:800px;"></iframe>
</div>
I want to see my pdf and I cannot do, I want to know how to do, I tried many solutions, I accept js, embed iframe whatever to can solve.
It should be user.is_authenticated not user.is_authenticated() in verPDF view and also I'd recommend you to change <idtermsCondition> to <int:idtermsCondition> as by default (if nothing is given) it is considered as string.
urls.py
urlpatterns = [
path('uploadfile/', views.uploadFile, name="uploadFile"),
path('verPDF/<int:idtermsCondition>/', views.verPDF, name='verPDF'),
]
And the {{uploadPDF.url}} already has the url (full path to the media directory) and try to use <embed> tag so:
<div>
<embed id="verPDF" src="{{uploadPDF.url}}" width="500" height="375" type="application/pdf">
</div>
Note: Always add / at the end of every route
Finally I can solve it, I had problems in my views.py and in the html, when I called uploadPDF my views called another name which was loadpdf and when I rendered it it was another name.
now, views.py:
``def uploadFile(request):
user = request.user
if user.is_authenticated:
if user.is_admin:
if request.method == "POST":
# Fetching the form data
fileTitle = request.POST["fileTitle"]
loadPDF = request.FILES["uploadPDF"]
if termsCondition.objects.all().exists():
listfiles = termsCondition.objects.all()[:1].get()
listfiles.uploadPDF = loadPDF
listfiles.save()
else:
# Saving the information in the database
termscondition = termsCondition.objects.create(
title=fileTitle,
uploadPDF=loadPDF
)
return redirect('uploadFile')
else:
if termsCondition.objects.all().exists():
listfiles = termsCondition.objects.all()[:1].get()
return render(request, 'subirTerminos.html', context={
"files": listfiles.uploadPDF
})
else:
listfiles = {}
return render(request, 'subirTerminos.html', context={"files": listfiles})
else:
messages.add_message(request=request, level=messages.SUCCESS,
message="No tiene suficientes permisos para ingresar a esta página")
return redirect('customer')
else:
return redirect('login2') ``
and html:
<h1 class="title">Visualizador de PDF</h1>
<embed id="verPDF" src="{{files.url}}" width="500" height="375" type="application/pdf">

I cannot retrieve the corresponding data when I want to relate the foreign key with the User object

models.py
class Order(models.Model):
PAYMENT_OPTIONS = (
('VISA','VISA'),
('Master','Master'),
('Octopus','Octopus'),
('Cash','Cash'),
)
STATUS = (
('Pending','Pending'),
('Delivered','Delivered'),
)
user = models.ForeignKey(User,models.CASCADE,null=True,blank=True)
customer = models.CharField(max_length=200,null=True,blank=True)
card_id = models.IntegerField(null=True,blank=True)
mobile = models.IntegerField(null=True,blank=True)
email = models.CharField(max_length=200,null=True,blank=True)
total_price = models.DecimalField(decimal_places=2,max_digits=7)
payment_method = models.CharField(max_length=50,choices=PAYMENT_OPTIONS,null=True,blank=True)
status = models.CharField(max_length=50,choices=STATUS,default='Pending')
date = models.DateTimeField(auto_now_add=True)
address = models.CharField(max_length=350,null=True,blank=True)
def __str__(self):
return str(self.customer)+'\'s Order'
views.py
def order_summary(request,order_id,user_id):
user = User.objects.get(id=user_id)
customer_order = Order.objects.get(id=order_id,user=user)
food_order = OrderDetail.objects.filter(order=customer_order)
context = {
'customer_order':customer_order,
'food_order':food_order}
return render(request,'menu/order_summary.html',context)
urls.py
path('order_summary/<int:order_id>/<int:user_id>/',views.order_summary,name='order_summary'),
navbar.html
<li><a class="dropdown-item" href="{% url 'order_summary' [order_id]%}我的訂單</a></li>
Error:
Reverse for 'order_summary' with arguments '('', 1)' not found. 1 pattern(s) tried: ['order_summary/(?P<order_id>[0-9]+)/(?P<user_id>[0-9]+)/$']
For the above code, I may want to have the hyperlink that can access the order_summary method with filtering the correct User object. However, if I try to put the corresponding parameters that are corresponding to the user's order and the user.id, I received the error message even though I already included the url in the urls.py. Can anyone find out the problem if I want to refer the parameters in the navbar of HTML?
You should add a user_id parameter in your urls.py file, so you can pass the currently logged in user (or any other user id you may desire).
So, your views would look like this:
def order_summary(request,order_id, user_id):
user = User.objects.get(id=user_id)
customer_order = Order.objects.get(id=order_id, user=user)
food_order = OrderDetail.objects.filter(order=customer_order)
context = {
'customer_order':customer_order,
'food_order':food_order}
return render(request,'menu/order_summary.html',context)
And then in your templates, you can simply pass in the user id.
<li><a class="dropdown-item" href="{% url 'order_summary' [order_id] [user_id] %}我的訂單</a></li>
I thinks you can directly get this by reverse relation you can try this,
request.user.order_set.all()
this will fetch the order data for current user.

How to generate 6 digits multiple OTP and insert into database at once with Django

I am generating 6 digits random OTP in my views.py and I just want to insert those many OTPs to be inserted into my Table at once using single QuerySet. Here is my code:
home.html
<form class="" action="" method="post">
{% csrf_token %}
<div class="" style="text-align: center:">
Mobile : <input type="text" name="mobile" value="">
<input type="submit" name="submit" value="Generate OTP">
</div>
</form>
models.py
class otpModel(models.Model):
mobile = models.CharField(max_length=30, blank=False, null=True)
otp = models.CharField(max_length=50, blank=False, null=True)
def __str__(self):
return self.mobile
views.py
import random
from app_name.models import otpModel
def registerOTP(request):
if request.method == 'POST':
mobile = request.POST['mobile']
for i in range(100):
otp = random.randint(100000, 999999)
otp = otpModel.objects.create(mobile=mobile, otp=otp)
if otp:
messages.info(request, 'OTP is saved!!!')
return render(request, 'app_name/otp.html')
else:
return render(request, 'studentexams/otp.html')
Here whenever I enter the Mobile number in the HTML form then only 1 OTP is inserted in the Table. Is there any solutions that I can insert multiple OTPs at a time using single QuerySet
Please give the Code snippet for achieving this task to be done
Try the following
from itertools import islice
def registerOTP(request):
if request.method == 'POST':
mobile = request.POST['mobile']
otps = []
for i in range(100):
otp = random.randint(100000, 999999)
otp_obj = otpModel(mobile=mobile, otp=otp)
otps.append(opt_obj)
# I advise you to do the following in a celery task
# Doing it in the request thread is a bad User experience
batch_size = 50 # 50 is the batch size, you
# can change it to any number you want based on your usecase
while True:
batch = list(islice(otps, batch_size))
if not batch:
break
otpModel.objects.bulk_create(batch, batch_size)
del otps[0:batch_size]
if otp:
messages.info(request, 'OTP is saved!!!')
return render(request, 'app_name/otp.html')
else:
return render(request, 'studentexams/otp.html')
There is a manager method called bulk_create. Using this method you can create list of objects into the database.
bulk_create(objs, batch_size=None, ignore_conflicts=False)
Your code snippet will be like:
otpModel.objects.bulk_create([otpModel(mobile=mobile, otp=otp1),
otpModel(mobile=mobile, otp=otp2),
])
This method inserts the provided list of objects into the database in an efficient manner (generally only 1 query, no matter how many objects there are):
Example:
Entry.objects.bulk_create([
... Entry(headline='This is a test'),
... Entry(headline='This is only a test'),
... ])
You can check out the official documentation here.

Search multiple models (Elasticsearch)

i added a second model to my app and now i also want to search trough the fields for that model and return it in the same view as my first model.
views.py
#Elasticsearch
def globalsearch_elastic(request):
qs = request.GET.get('qs')
page = request.GET.get('page')
if qs:
qs = PostDocument.search().query("multi_match", query=qs, fields=["title", "content", "tag"])
qs = qs.to_queryset()
else:
qs = ''
paginator = Paginator(qs, 10) # Show 10 results per page
try:
qs = paginator.page(page)
except PageNotAnInteger:
qs = paginator.page(1)
except EmptyPage:
qs = paginator.page(paginator.num_pages)
return render(request, 'MyProject/search/search_results_elastic.html', {'object_list':qs})
currently only PostDocument get searched for a match, how do i add a second one here from documents.py in this function?
or to be more specific, what do i have to change in this line to Search multiple documents?
qs = PostDocument.search().query("multi_match", query=qs, fields=["title", "content", "tag"])
this is where "qs" comes from (base.html):
<div class="globalsearch">
<form id="searchform" action="{% url 'search' %}" method="get" accept-charset="utf-8">
<div class="form-row align-items-center">
<input class="class-search-input-fields" id="searchbox" name="qs" required="required" type="text" placeholder="Search ..."><a>in</a>
<div class="custom-dropdown">
<a>{{ categorysearch_form.category }}</a>
</div>
<button class="btn btn-dark" type="submit">
<i class="fa fa-search"></i>
</button>
</div>
</form>
</div>
documents.py:
Thanks in advance
Got it working like this:
def globalsearch_elastic(request):
qs = request.GET.get('qs')
page = request.GET.get('page')
if qs:
post = PostDocument.search().query("multi_match", query=qs, fields=["title", "content", "tag"]).to_queryset()
post_extra = PostExtraDocument.search().query("multi_match", query=qs, fields=["title", "content_preview", "tag"]).to_queryset()
qs = list(
sorted(
chain(post, post_extra),
key=lambda objects: objects.pk
))
else:
qs = ''
paginator = Paginator(qs, 10) # Show 10 results per page
try:
qs = paginator.page(page)
except PageNotAnInteger:
qs = paginator.page(1)
except EmptyPage:
qs = paginator.page(paginator.num_pages)
return render(request, 'MyProject/search/search_results_elastic.html', {'object_list': qs})
i know that this is maybe not "best-practice" because i always have to load all posts, sort them afterwards and display them by creation-date (pk).
Anyways, from my point of view it's fine if you don't have to search millions f posts ... Beside that you still have the power of elastic even if that does not sort my qs list of posts
The accepted answer is correct but not efficient because it fetches rows from the database to convert the results to a querysets and finally chain and sort...
I hope my answer will help others. I'm using django-oscar and dajngo-elasticsearch-dsl and I assume that you're at least using elasticsearch-dsl. So you can simply do
# *********************************************** documents.py in user module
from django_elasticsearch_dsl import Document
from django_elasticsearch_dsl.registries import registry
from oscar.core.compat import get_user_model
User = get_user_model()
#registry.register_document
class UserDocument(Document):
class Index:
name = 'users'
settings = {
'number_of_shards': 1,
'number_of_replicas': 0
}
class Django:
model = User # <-- User model class
fields = [
'first_name',
'last_name',
'username'
]
# *********************************************** documents.py in product module
from django_elasticsearch_dsl import Document, TextField
from django_elasticsearch_dsl.registries import registry
from oscar.core.loading import get_model
Product = get_model('catalogue', 'product')
#registry.register_document
class ProductDocument(Document):
upc = TextField(attr='upc', required=False)
class Index:
name = 'products'
settings = {
'number_of_shards': 1,
'number_of_replicas': 0
}
class Django:
model = Product # <-- Product model class
fields = [
'title',
'description'
]
# *********************************************** search method
from django.shortcuts import render
from elasticsearch_dsl import Search
def global_search(request):
q = request.GET.get('q')
objects = ''
if q:
search = Search(index=['users', 'products'])
objects = search.query("multi_match", query=q, fields=['first_name', 'last_name', 'username', 'title', 'description', 'upc'])
return render(request, 'oscar/search/search.html', {'objects': objects})
In a match query you already search all the tokens in the query as an OR query in SQL . In your case, if you have a multimatch query against 3 field means that you are searching for a match of any of your token in your query against any of that 3 fields - if you haven't assigned any specific mapping to ES, it means that your text fields have processed with a standard analyzer please read here that breaks your string in tokens on whitespace. So to add a new key to be queryied, just concatenate a new value to the string qs:
from document import DocClass
def globalsearch_elastic(request):
qs = request.GET.get('qs')
document = DocClass()
document_value = document.my_function() # you invoke a function in a class inside document.py that returns to you a string
page = request.GET.get('page')
if any([qs, document_value]):
queryable = " ".join([qs, document_value])
qs = PostDocument.search().query("multi_match", query=queryable, fields=["title", "content", "tag"])
qs = qs.to_queryset()
else:
return "No values to query"

Django form, view, and forms.py not working together

My first foray into Django, allows the user to input a day of the week, and searches my database for restaurants open on that given day. Right now, the restaurant objects (Resto) have a days_open attribute, with each day separated by a comma (Monday, Tuesday, etc...).
When I input a day, the resulting page only displays the title and '[]' and I can't seem to get it to return the list of Resto objects. What is displaying the square brackets, and how do I go about fixing it to display the results of the search?
The code is below- my apologies if I neglected to include any relevant bits.
my forms.py:
from django import forms
from .models import Resto
class RestoSearch(forms.ModelForm):
class Meta:
model = Resto
fields = ('title', 'description', 'opening_hour', 'closing_hour')
models.py:
from django.db import models
class Resto(models.Model):
title = models.CharField(max_length=300)
description = models.TextField()
opening_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
closing_hour = models.TimeField(auto_now=False, auto_now_add=False, null=True)
days_open = models.TextField(blank=True)
views.py:
from django.shortcuts import render
from django.http import Http404
from django.shortcuts import HttpResponse
from belize.models import Resto
from django.core.exceptions import *
from .forms import RestoSearch
def index(request):
return render(request, 'form.html')
def search(request):
form = RestoSearch()
if request.method == 'POST':
search_id=request.POST.get('textfield', None)
try:
#I think this is where my problem is
available = Resto.objects.filter(days_open = search_id)
html = ("<H1>Hello</H1>", available)
return HttpResponse(html)
except Resto.DoesNotExist:
return HttpResponse("Please try another day")
else:
return render(request, 'belize/form.html')
def restaurant_detail(request, id):
try:
restaurant = Resto.objects.get(id=id)
except Resto.DoesNotExist:
raise Http404('This restaurant does not exist')
return render(request, 'belize/restaurant_detail.html', {
'restaurant': restaurant,
})
template form.html:
<form method="POST" action="/search/">
{% csrf_token %}
<input type="text" name="textfield">
<button type="submit">Enter a day of the week</button>
</form>
I presume what you are trying to show is the RestoForm in that case the index method is not correct. It should be
def index(request):
form = RestoForm()
return render(request, 'form.html', {'form': form })
And then your template should change as
<form method="POST" action="/search/">
{% csrf_token %}
{{ form }}
<button type="submit">Enter a day of the week</button>
</form>
For additional details please see the examples at https://docs.djangoproject.com/en/1.9/topics/forms/#the-template
The [] means that your .filter() returned no results, its not surprising as you have a few issues with your code, lets start from the top:
You are declaring a form that you never use.
You are trying to catch an exception that is never raised by .filter()
You filter condition will only work for exact matches.
I've annotated your code as well:
def search(request):
form = RestoSearch() # You aren't using this form anywhere in your code?
if request.method == 'POST':
# Where is 'textfield' coming from?
search_id = request.POST.get('textfield', None)
try:
# If search id is "Tuesday", and a restaurant is
# open on monday and tuesday, so it has "Monday,Tuesday"
# in the days_open field, then this search will not
# return any results, because its looking for an exact
# match
available = Resto.objects.filter(days_open=search_id)
html = ('<H1>Hello World</H1>', available)
return HttpResponse(html)
except Resto.DoesNotExist:
# This exception is not raised by .filter(),
# .filter() will return an empty list, [] if no results are found
# so this entire try/except is not doing anything
return HttpResponse("Please try another day")
else: # in your code, this else is not indented correctly
return render(request, 'belize/form.html')
So there is a lot going on here, lets try something simple, starting with the template:
{% if results %}
{% for restaurant in results %}
{{ restaurant }}
{% endfor %}
{% else %}
Sorry, no results for your search. Try again.
{% endif %}
<form>
{{ form }}
<input type="submit" name="Search" />
</form>
Next, the search form:
class SearchForm(forms.Form):
search_field = forms.CharField('Search', strip=True)
Finally the view:
from django.db.models import Q
def search(request):
form = SearchForm(request.GET)
results = [] # Set results to an empty list
if form.is_valid():
needle = form.cleaned_data['search_field'].capitalize()
results = Resto.objects.filter(Q(days_open__startswith='{},'.format(needle)) |
Q(days_open__endswith=',{}'.format(needle)) |
Q(days_open__contains=',{},'.format(needle)) |
Q(days_open='{},'.format(needle)) |
Q(days_open='{}'.format(needle)))
return render(request, 'search.html', {'results': results, 'form': form})
Lets assume the user entered 'Thursday' as a search field. In this view you are searching for all restaurants whose days_open field:
Either starts with Thursday, or
Ends with ,Thursday or
Contains ,Thursday, in the middle or
Has the value Thursday, or
Has the value Thursday
Your template will then only show the results if there are any values to display; since an empty list [] is false, then the {% if results %} condition will fail, so on empty lists the template will display the error notice instead.
In your view, you only do the database check if someone enters something in the search field, that's what if form.is_valid(): does. In django, by default all form fields are required - so a blank form will return an error. Using this trick, we make sure we only search if someone enters a value in the search box.
The main action happens with all the Q() calls. A Q object is a way to do multiple queries and chain them together. It is used whenever you want to do an "or" or "and" type query. Our search is an "or" type query, because we want to return any results if the value in days_open matches any number of conditions.

Categories

Resources