Django request.GET. Search. Different results, depending on the value - django

There is a search form. It has a drop-down list. Each item in this list has different meanings. After clicking, a search is performed, ulr looks as follows:
http://127.0.0.1:8000/search?wantbuy=kv
http://127.0.0.1:8000/search?wantbuy=ls
http://127.0.0.1:8000/search?wantbuy=km
my views.py:
kv = kv.objects.all()
ls = ls.objects.all()
km = km.objects.all()
if 'wantbuy' in request.GET:
wantbuy = request.GET['wantbuy']
if wantbuy:
queryset_list = km
Now, it doesn’t matter what value it 'wantbuy', I get all the objects from 'km'.
Tell me how to do it better? As for each url value, display your objects? I want to:
http://127.0.0.1:8000/search?wantbuy=kv
Shows all objects
kv = kv.objects.all()
etc.
Thank!

You can have several conditions to set the variable depending on the parameter
wantbuy = request.GET.get('wantbuy')
if wantbuy == 'kv':
queryset_list = kv.object.all()
elif wantbuy == 'ls':
queryset_list = ls.object.all()
elif wantbuy == 'km':
queryset_list = km.object.all()
else:
# Raise a 404 or do something else

Related

Whats wrong with my search string? I am not getting proper data

I want to implement search on my django project. On my following queryset, with else condition its passing correct data. But with if condition whatever I search, it shows nothing.
def get_queryset(self):
category = self.request.GET['category']
query = self.request.GET['q']
if category == 'all':
products = Products.objects.filter(Q(name__icontains=query) | Q(category__name__icontains=query)).all()
else:
products = Products.objects.filter(Q(category__slug=category), Q(category__slug__icontains=self.request.GET['q']) | Q(name__icontains=self.request.GET['q']))

Flask wtforms AttributeError: 'HTMLString' object has no attribute 'paginate'

Somebody has helped me with some great code here to show the same form multiple times each with a submit button, it works a treat, But as I will have hundreds of forms I need to paginate the page, I have been able to paginate pages in the past but I dont no how to use that code with a form in a for loop.
here is my code:(with lots of help from Greg)
#bp.route('/stock', methods=['GET', 'POST'])
#bp.route('/stock/stock/', methods=['GET', 'POST'])
#login_required
def stock():
stocks = Stock.query.all()
forms = []
for stock in stocks:
form = AddStockForm()
form.id.default = stock.id
form.image.default = stock.image_url
form.date.default = stock.date
form.description.default = stock.description
form.event.default = stock.event
form.achat.default = stock.achat
form.vente.default = stock.vente
form.sold.default = stock.sold
forms.append(form)
for form in forms:
if form.validate_on_submit():
if form.modify.data:
stock = Stock.query.filter_by(id=form.id.data).one()
stock.date = form.date.data
stock.description = form.description.data
stock.event = form.event.data
stock.achat = form.achat.data
stock.vente = form.vente.data
stock.sold = form.sold.data
db.session.add(stock)
db.session.commit()
elif form.delete.data:
stock = Stock.query.filter_by(id=form.id.data).one()
db.session.delete(stock)
db.session.commit()
return redirect(url_for('stock.stock'))
form.process() # Do this after validate_on_submit or breaks CSRF token
page = request.args.get('page', 1, type=int)
forms = forms[1].id().paginate(
page, current_app.config['ITEMS_PER_PAGE'], False)
next_url = url_for('stock.stock', page=forms.next_num) \
if forms.has_next else None
prev_url = url_for('stock.stock', page=forms.prev_num) \
if forms.has_prev else None
return render_template('stock/stock.html',forms=forms.items, title=Stock, stocks=stocks)
I am trying to use the fact "forms" is a list to paginate the results, I obviously dont understand how to do this, I have looked at flask-paginate but I didnt understand that either!
all help is greatly needed
Warm regards, Paul.
EDIT
I have tried to use flask_pagination, here is my code:
#bp.route('/stock/stock/', methods=['GET', 'POST'])
#login_required
def stock():
search = False
q = request.args.get('q')
if q:
search = True
page = request.args.get(get_page_parameter(), type=int, default=1)
stocks = Stock.query.all()
forms = []
#rest of code here#
pagination = Pagination(page=page, total=stocks.count(), search=search, record_name='forms')
form.process() # Do this after validate_on_submit or breaks CSRF token
return render_template('stock/stock.html',forms=forms, title=Stock, pagination=pagination)
This gives a different error "TypeError: count() takes exactly one argument (0 given)" I also tried with "total=forms.count()" and got the same error!
I hate doing this as it shows a lack of patience at the begining but this answer may help others, I solved my problem in two ways the first was the query which decides the order of display (descending or ascending) this then allowed me to use flask-paginate to display the results on several pages, I realised that I was dealing with a list, and the example by one of the developers link showed me the way, here is my code,
from flask_paginate import Pagination, get_page_args
#bp.route('/stock', methods=['GET', 'POST'])
#bp.route('/stock/stock/', methods=['GET', 'POST'])
#login_required
def stock():
stocks = Stock.query.order_by(Stock.id.desc())# this gives order of results
forms = []
def get_forms(offset=0, per_page=25): #this function sets up the number of
return forms[offset: offset + per_page] #results per page
for stock in stocks:
form = AddStockForm()
form.id.default = stock.id
form.image.default = stock.image_url
form.date.default = stock.date
form.description.default = stock.description
form.event.default = stock.event
form.achat.default = stock.achat
form.vente.default = stock.vente
form.sold.default = stock.sold
forms.append(form)
for form in forms:
if form.validate_on_submit():
if form.modify.data:
stock = Stock.query.filter_by(id=form.id.data).one()
stock.date = form.date.data
stock.description = form.description.data
stock.event = form.event.data
stock.achat = form.achat.data
stock.vente = form.vente.data
stock.sold = form.sold.data
db.session.add(stock)
db.session.commit()
elif form.delete.data:
stock = Stock.query.filter_by(id=form.id.data).one()
db.session.delete(stock)
db.session.commit()
return redirect(url_for('stock.stock'))
#this is the code from the link that I used to paginate
page, per_page, offset = get_page_args(page_parameter='page',
per_page_parameter='per_page')
total = len(forms) # this code counts the resulting list to be displayed
pagination_forms = get_forms(offset=offset, per_page=per_page)
pagination = Pagination(page=page, per_page=per_page, total=total)
form.process() # Do this after validate_on_submit or breaks CSRF token
return render_template('stock/stock.html', title=Stock, stocks=stocks
page=page, forms=pagination_forms, per_page=per_page, pagination=pagination)
#And finally this is the pagination passed to the html
so this for all those numptys like me who struggle with everything but still love it.

Sorting through request.GET in Django

I want people to be able to sort things and not just me sorting things manually. So, for example, I will have a link to sorting, and the link will be something like, /?sort=issues and this would show a list of issues in alphabetical order, etc. Or /?sort=cover and it will show a list of issues with covers only.
Views.py
def issues(request):
issues_list = Issue.objects.order_by('-date_added')
paginator = Paginator(issues_list, 24)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
issues = paginator.page(page)
except (EmptyPage, InvalidPage):
issues = paginator.page(paginator.num_pages)
return render_to_response('comics/issues.html', {'issues': issues}, context_instance=RequestContext(request))
So, I'd want anyone to have the option of ordering by something like -date_added, date_addded, pub_date, importance, etc.
I'd imagine I'd have to fix my views and do some request.GET magic, but I am pretty new to Django and don't really know how to go about doing this. I checked Django docs, too.
sort_by = request.GET.get('sort', '-date_added')
if sort_by not in ['-date_added', 'date_addded', 'pub_date', 'importance']:
sort_by = '-date_added'
issues_list = Issue.objects.order_by(sort_by)
Put it at the top of your view:
user_specified = request.GET.get('sort', **''**)
if user_specified:
issues_list = Issue.objects.order_by(user_specified)
else: # default ordering
issues_list = Issue.objects.order_by('-date_added')
NB: Haven't tested this!

Django search form help want to be able to do partial search as well

I have a search for that looks for an alternate_id for a particular item. This search for can allow alternative if to do multiple searches if there is a comma without spaces. But I want my search form to be able to do a "partial search" as well. So searching for a*,2ndid will give all results a......,2ndid
Alternate id is a string. Not an integer. The only reason why I have as __inwas because I could perform multiple searches.
def search_item(request, client_id = 0):
client = None
if client_id != 0:
try:
client = models.Client.objects.get(pk = client_id)
except:
pass
items = None
Q_alt_ids = Q()
if request.method == 'POST':
form = forms.SearchItemForm(request.POST)
if form.is_valid():
alternate_id = form.cleaned_data['alternate_id']
items = client.storageitem_set.all()
if alternate_id:
alternate_ids= alternate_id.lower().split(",")
Q_alt_ids = Q(alternative_id__in = alternate_ids)
return render_to_response('items.html', {'items':items, 'client':client}, context_instance = RequestContext(request))
else:
HttpResponse(client)
if client is not None:
form = forms.SearchItemForm(initial = {'client':client.pk})
else:
form = forms.SearchItemForm()
return render_to_response('search_items.html', {'form':form}, context_instance = RequestContext(request))
Aside from writing your own SQL, I think the only out of the box solution that can handle a robust query like a wildcard is a regular expression. If the wildcard is always in the same place, such as followed by a partial string, you could use the field__startswith=partial lookup type.
But in general, wildcards = regex
http://docs.djangoproject.com/en/dev/ref/models/querysets/#iregex
BUT,
I have a sneaking suspicion you might be looking for the contains or icontains filter lookup type.
If you want to match 'hello', 'ello', 'world' in 'hello world':
# first split the string by comma
queries = form.cleaned_data.get('query', '').split(',')
# populate queries list
query_list = [Q(**{'alternative_id__icontains' : query }) for query in queries]
# join queries list with the OR operator
results = MyModel.objects.filter( reduce(operator.or_, query_list) )
thanks yuji for the answer. These change are needed to make it work.
return_all = False
filters = []
if alternate_id:
alternate_ids = alternate_id.lower().split(",")
for item in alternate_ids:
if '*' not in item:
filters.append( Q(alternative_id=item) )
elif item == '*':
return_all = True
elif item.startswith('*') and item.endswith('*'):
filters.append( Q(alternative_id__icontains=item.strip('*')) )
elif item.startswith('*'):
filters.append( Q(alternative_id__endswith=item.strip('*')) )
elif item.endswith('*'):
filters.append( Q(alternative_id__startswith=item.strip('*')) )
else:
assert False, "Wildcard in invalid position (valid is.. *XX, *XX*, XX*)"
if not filters or return_all:
items = items.all()
else:
items = items.filter(reduce(operator.or_, filters))

How to capture multiple arguments using a single RegEx in my Django urls.py?

I've got an application that allows you to filter data via 3 fields. I'm trying to write a RegEx in my urls.py that can capture multiple combinations without having to write-out each possible combination it it's own URL.
Here's my urls.py:
#urls.py
urlpatterns = patterns('',
# Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)),
(r'(?P<key>\w*?)=(?P<value>\w*?)&|$', views.scriptFilter),
I tested the above RegEx at pythonregex.com and it appears to capture as many key/value pairs as I can throw at it. However, when I test try it in my app, Django only returns a queryset based on the first pair, and ignores the other pairs.
For example, if I enter this:
http://MYSITE/feature=1&session=1&
Django returns the data based on feature=1 only and ignores session=1.
Here's my views.py:
#views.py
def scriptFilter(request, key, value):
if key == 'session':
sessionID = value
qs = models.Script.objects.filter(session=sessionID)
elif key == 'product':
productID = value
qs = models.Script.objects.filter(product=productID)
elif key == 'feature':
featureID = value
scriptFeature = models.Feature.objects.get(pk=featureID)
qs = models.Script.objects.filter(feature=scriptFeature)
else:
qs = models.Script.objects.all()
caseTotal = qs.aggregate(Sum('caseCount'))
scriptTotal = qs.aggregate(Count('subScriptName'))
featureTotal = qs.aggregate(Count('feature'))
return render_to_response('scripts.html', locals())
I'm new to Python & Django so please be gentle :) Any help would be really appreciated.
These may be valid URLs (not entirely certain) but they're certainly not recommended.
If you want to allow parameters sent into your application using key-value pairs (as you are doing here), I'd suggest just using query parameters. Here's a way to implement that in your view:
def scriptFilter(request, session=None, product=None, feature=None):
session = request.REQUEST.get('session',session)
product = request.REQUEST.get('product',session)
feature = request.REQUEST.get('feature',session)
if session
qs = models.Script.objects.filter(session=session)
elif product:
qs = models.Script.objects.filter(product=product)
elif feature:
qs = models.Script.objects.filter(feature=feature)
else:
qs = models.Script.objects.all()
caseTotal = qs.aggregate(Sum('caseCount'))
scriptTotal = qs.aggregate(Count('subScriptName'))
featureTotal = qs.aggregate(Count('feature'))
return render_to_response('scripts.html', locals())
You'd then call the URLs as
http://example.com/myapp?session=X
http://example.com/myapp?product=X
http://example.com/myapp?session=X&feature=Y
etc.
Note that I assume when you say that pythonregex captured all the groups, that's probably because you were looking at the .findall() response. I'm not sure of the exact mechanics of Django's url dispatcher, but even if you just think about it logically, how could it assign more than one value to key and value? Your scriptFilter function does not even handle multiple values being sent in. You really want it to read:
def scriptFilter(request, session=None, product=None, feature=None):
session = request.REQUEST.get('session',session)
product = request.REQUEST.get('product',session)
feature = request.REQUEST.get('feature',session)
qs = models.Script.objects.all()
if session
qs = qs.filter(session=session)
if product:
qs = qs.filter(product=product)
if feature:
qs = qs.filter(feature=feature)
caseTotal = qs.aggregate(Sum('caseCount'))
scriptTotal = qs.aggregate(Count('subScriptName'))
featureTotal = qs.aggregate(Count('feature'))
return render_to_response('scripts.html', locals())
Finally, I'm guessing you should rewrite these lines as well:
caseTotal = qs.aggregate(Sum('caseCount'))
scriptTotal = qs.aggregate(Count('subScriptName'))
featureTotal = qs.aggregate(Count('feature'))
The aggregate function creates a QuerySet with the aggregated values. You may as well group these into one queryset:
script_totals = qs.aggregate(Sum('casecount'), Count('subscriptname'), Count('feature'))
In the template, you'd access these values as:
{{ script_totals.casecount__sum }}
{{ script_totals.subscriptname__count }}
{{ script_totals.feature__count }}
Which is a bit cleaner than
{{ caseTotal.casecount__sum }}
{{ scriptTotal.subscriptname__count }}
{{ featureTotal.feature__count }}