I am working on a listing in django project. The scenario is that When i click on FAQ listing page then it redirects me to the listings where i get all faq. Now i have to search using different keywords. There is total five keywords through which i can search for particular results. Code is
questions = Help.objects.all().filter().values('id','question','description','status','created','modified').order_by('-id')
if 'question' in ajax_data:
#add filter for question
if 'description' in ajax_data:
#add filter for description
if 'status' in ajax_data:
#add filter for status
if 'created' in ajax_data:
#add filter for created
if 'modified' in ajax_data:
#add filter for modified
questions = Help.objects.all().filter(#add all conditions here dynamically after applying filters).values('id','question','description','status','created','modified').order_by('-id')
First when page refresh it executes first query which returns all data, now using ajax filters have to be applied, i have done all ajax code just want logic of this search. Search should perform like if i enter question only it should filter according to question, but if i search using question, status and created filed it should apply filter for all these three keywords.
questions = Help.objects.all()
filters = {}
if 'question' in ajax_data:
filters['question'] = ajax_data.get('question')
if 'description' in ajax_data:
filters['description'] = ajax_data.get('description')
if 'status' in ajax_data:
filters['status'] = ajax_data.get('status')
if 'created' in ajax_data:
filters['created'] = ajax_data.get('created')
if 'modified' in ajax_data:
filters['modified'] = ajax_data.get('modified')
questions = questions.filter(**filters).values('id','question','description','status','created','modified').order_by('-id')
Check this link https://docs.python.org/3/glossary.html#term-argument
You can use request.GET.get('filter_name')
e.g if you have a filter by title:
query = Model.objects.all()
title = request.GET.get('title')
if title:
query = query.filter(title__icontains=title)
...and so on
note that your url should contain get parameters like http://myurl.com/?title=abc
EDIT
for one query filter you can use Q object from django.db.models
filter = Q()
if title:
filter &= Q(title__icontains=title)
if category:
filter &= Q(category__icontains=category)
query = query.filter(filter)
Related
It might be not new to all and need your expert guidance. I am trying to filter a column on a django-table2. Note I have not used django-filter here.
class Group(models.Model):
title = models.CharField(blank=True)
class Control(models.Model):
published = models.charField(auto_now=False)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
Now I am trying to filter in views.py as below
table = ControlTable(Control.objects.all().order_by('-published').filter(
group__in=form['contact'].value(),
)
Issue is this is working fine, but when selecting '-----' from dropdown then its showing blank table instead of all the values.
Again if I change the query filter as below
table = ControlTable(Control.objects.all().order_by('-published').filter(
Group__title__iexact=form['contact'].value(),
)
then throwing error Cannot resolve keyword 'Group' into field.
Could you please guide me on this?
That makes sense, if you select ----, then it uses None, so you are filtering for None. You should check for that:
items = ControlTable(Control.objects.all().order_by('-published')
group = form['contact'].value()
if group:
items = items.filter(
group__in=group
)
table = items
I have query that performs full text search on several columns (including on columns of models related using FK) in Django:
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
class TaskManager(models.Manager):
def search_by_text(self, text: str):
search_vector = SearchVector(
"task_type__name",
"order__registration_number",
"order__report_number",
"car_owner_name",
"task_number",
"order__customer_order_number",
"order__customer_owner",
"order__report_type__value",
)
search_query = SearchQuery(text)
return self.get_queryset().annotate(
rank=SearchRank(search_vector, search_query)
).order_by("rank")
How can I get not only found records but also column names where searched value was found for each record? Example:
>>> Entry.objects.search_by_text("some value")[0].columns_matched
["task_type__name", "task_number"]
I'm using Postgresql 10.12 and Django 2.2.10.
Solved this problem by creating migration which creates database view which has search_q column containing concatenated string with values from all searched columns.
CREATE VIEW app_taskdata_search_view
AS
SELECT Row_number()
over(
ORDER BY TASK.task_number) AS id,
Concat(tasktype.name, '|', TASK.description, '|', USER.first_name, '|',
USER.last_name) AS search_q
FROM app_taskdata AS TASK
inner join app_tasktype AS tasktype
ON TASK.task_type_id = tasktype.id
inner join users_user AS USER
ON TASK.user_id = USER.id
ORDER BY TASK.task_number;
Then in models.py:
class TaskDataSearchView(models.Model):
"""
Database view refrenced from.
"""
id = models.BigIntegerField(primary_key=True)
search_q = models.TextField()
class Meta:
db_table= "app_taskdata_search_view"
managed = False
Assuming I know the order of concatenated column values I can make a Python code which loops through result and checks if searched value was found in column:
text = "Some text to search"
records = TaskDataSearchView.objects.filter(search_q__icontains=text)
values = records[0].search_q.split("|")
# check if task_type column contains searched text
if text in values[0]:
field_mapping['task_type'] = True
P.S: Useful link
I have few form fields on search page. After performing the search, my page should display a list of possible matching results. If the user typed in only part of a title, ISBN, or author name, search page should find matches for those as well. Also if user typed only one or few field - page should show all matches.
Idk how to write query. If i have one value from request.form and other values is None - so whole query is empty
#app.route('/search', methods=("GET", "POST"))
def search_book():
books = None
if request.method == "POST":
isbn = request.form['isbn']
title = request.form['title']
author = request.form['author']
year = request.form['year']
books = db.query(Books).filter_by(isbn=isbn, title=title, author=author, year=year).all()
return render_template("search.html", books=books)
.filter_by(multiple arguments) will default to AND, meaning the user will have to enter data that matches all fields
This also means that if the user leaves fields empty, the query will only return books that have (for example) title = " " (when title form is empty), despite having entered a valid year.
This is probably not intended, from a user point of view. A way to fix this is to (1. validate input data, and then) add filters to a list if they are not empty, then add the non-empty fields using or_(*filter_list). Query will then return all rows that match any field specified by the forms.
from sqlalchemy import or_
query = db.query(Books)
filter_list = []
if request.form['isbn']:
filter_list.append(Book.isbn.ilike(request.form['isbn']))
if request.form['author']:
filter_list.append(Book.author.ilike(request.form['author']))
# etc...
if len(filter_list) > 0:
query = query.filter(or_(*filter_list))
didnt test code but *filter_list allows you to pass a list to the filter method, or_ allows you to change the default AND to OR
more here:
Using OR in SQLAlchemy
Use the 'or_()' method . This will search all the matching results for any given column
name = request.form.get('res')
result = Books.query.filter(db.or_(Books.author==name , Books.isbn==name , Books.title==name)).all()
'res' is the search entry given by the user in the search form in your html page , as mentioned it can be anything ISBN , title or the author's name
I am using elasticsearch-dsl to index data in elasticsearch index. I am am using following class to create elasticsearch document:
class BooksDoc(Document):
title = Text(
analyzer=my_analyzer
)
author = Text(
analyzer=my_analyzer
)
publisher = Text(
analyzer=my_analyzer
)
image_url = Text(analyzer=my_analyzer)
price = Text(analyzer=my_analyzer)
category =Text(analyzer=my_analyzer)
published = Boolean()
upload_date = Text()
class Index:
name = 'books'
Here is my analyzer:
my_analyzer = analyzer('my_analyzer',
tokenizer=tokenizer('trigram', 'edge_ngram', min_gram=1, max_gram=20),
filter=['lowercase']
)
I am using following function to index document:
def indexing(self):
doc = BooksDoc(
meta={'id': self.id},
title=self.book_title,
author=self.book_author,
publisher=self.book_publisher,
image_url=self.front_image,
price=self.book_price,
catagory=__catagory,
published=self.isPublished,
upload_date=self.dateadded
)
try:
doc.save()
return doc.to_dict(include_meta=True)
except:
print(traceback.format_exc())
return None
I want to implement search such that when user enters a search string i can query elasticsearch and fetch all the records where i find a match. For example, if user enters "Boo" then all the records containing string "Boo" should be returned.Right now search works fine when string matches exactly in elasticsearch but i also want to fetch record if there is a partial match. How can i do this?
It really depends what query you are using. Typically if you are using edge_ngram you want to specify a different analyzer for search so that the edge_ngram is not applied to the input. Then you can use a standard match query.
For auto-suggest though it is much better (more performant) to use Completion field and completion suggester (0, 1).
0 - https://www.elastic.co/blog/you-complete-me
1 - http://elasticsearch-dsl.readthedocs.io/en/latest/search_dsl.html#suggestions
I'm trying to make this table with a clickable field which changes the boolean for the entry to its opposite value. It works, but I want an alternative text as "False" or "True" does not look nice, and the users are mainly Norwegian.
def bool_to_norwegian(boolean):
if boolean:
return "Ja"
else:
return "Nei"
class OrderTable(tables.Table):
id = tables.LinkColumn('admin_detail', args=[A('id')])
name = tables.Column()
address = tables.Column()
order = tables.Column()
order_placed_at = tables.DateTimeColumn()
order_delivery_at = tables.DateColumn()
price = tables.Column()
comment = tables.Column()
sent = tables.LinkColumn('status_sent', args=[A('id')])
paid = tables.LinkColumn('status_paid', args=[A('id')], text=[A('paid')])
class Meta:
attrs = {'class': 'order-table'}
If you look under the "paid" entry I am testing this right now, why can't I access the data with the same accessor as I do in the args? If I change the args to args=[A('paid')] and look at the link, it does indeed have the correct data on it. The model names are the same as the ones in this table, and "paid" and "sent" are BooleanFields.
This is kind of what I ultimately want:
text=bool_to_norwegian([A('paid')])
Here is what I send to the table:
orders = Order.objects.order_by("-order_delivery_at")
orders = orders.values()
table = OrderTable(orders)
RequestConfig(request).configure(table)
The text argument expects a callable that accepts a record, and returns a text value. You are passing it a list (which it will just ignore), and your function is expecting a boolean instead of a record. There is also no need for using accessors here.
Something like this should work:
def bool_to_norwegian(record):
if record.paid:
return "Ja"
else:
return "Nei"
Then in your column:
paid = tables.LinkColumn('status_paid', text=bool_to_norwegian)
(Note, it is not clear from your question where the data is coming from - is paid a boolean? You may need to adjust this to fit).
As an aside, the way you are passing args to your columns is weird (it seems the documentation also recommends this, but I don't understand why - it's very confusing). A more standard approach would be:
id = tables.LinkColumn('admin_detail', A('id'))
or using named arguments:
id = tables.LinkColumn('admin_detail', accessor=A('id'))