SQLAlchemy Flask, match some or all - python

I have a list of schools (1000's) with a name, city, and state.
There are name, city and state input fields.
I want to search for schools by name. If they leave city or state inputs empty I want to search by any city, any state. If they enter the city (leaving state blank) then search by name and city. Etc etc.
Bottom line: Search with the data that is available and everything else is "any".
Is there a quick way to do this other than writing every possible combination?

I'm writing this as if you're using Flask-SQLAlchemy and WTForms.
...
schools = School.query
if form.name.data:
schools = schools.filter(School.name == form.name.data)
if form.city.data:
schools = schools.filter(School.city == form.city.data)
if form.state.data:
schools = schools.filter(School.state == form.state.data)
# at this point schools is a query filtered with whatever was given
...
This is just an example, reword it, use filters besides equality, etc. I have "filter forms" like this, and would put extra methods on the form, query() and filter(query). Then they can be called like this in a view:
form = FilterForm(request.args) # use get for filtering
schools = form.query()
if form.validate():
schools = form.filter(schools)
return render_template('schools.html', form=form, schools=schools)

Related

How to limit fields returned in django queryset per field

I have rows of products, that tend to share some information, such as the same images (an image may show multiple products), descriptions, manufacturer, etc.
I would like to choose some fields to only show the information once. I can't use distinct for this (I don't think) as the information is not always identicle, such as with the description, but may be only subtly different enough that I don't want to repeat it.
At the moment, I am using a for loop to display fields from each result, which ends up with something like this:
This is the code I am using in my view:
def collection_detail(request, name=None):
template = loader.get_template('/webapps/mywebapp/furniture/main_page/templates/main_page/detail.html')
products = product.objects.filter(id=name)
cart_product_form = CartAddProductForm()
context={'products': products,
'cart_product_form': cart_product_form}
return HttpResponse(template.render(context))
What would be the appropriate way to do this in a view, ignoring subsequent rows for some fields?
edit: Example queryset results
collection_name|manufacturer|product_type|description|image_url
----------------------------------------------------------------
Testing |FakeCo |Bed |pretty nice|/img/1.jpg
Testing |FakeCo |Desk |pretty bad |/img/2.jpg
Testing |FakeCo |Nightstand |pretty ok |/img/1.jpg
Testing |FakeCo |Draws |pretty nice|/img/3.jpg
In the example of the above data, I want to show the collection name and manufacturer just once, so the first result would do, each product type, the first description regardless of what it says, and each image that is distinct and not a duplicate.
products = product.objects.filter(id=name)
collection_name = []
manufacturer = []
product_type = []
description = []
image_url = []
for product in products:
collection_name.append(product.collection_name)
manufacturer.append(product.manufacturer)
product_type.append(product.product_type)
description.append(product.description)
image_url.append(product.image_url)
collection_name = set(collection_name)
manufacturer = set(manufacturer)
product_type = set(product_type)
description = set(description)
image_url = set(image_url)
It is little long but it will solve your problem. "set" will make all common names once in the list.
By this
products = product.objects.filter(id=name)
it will return a queryset, that means it consists of several products information.
If you want to get only single objects information change the query like below:
products = product.objects.filter(id=name).first()
or,
products = product.objects.filter(id=name)[:1]
Update
If you want to display some fields one value then use this:
{{ value|first }}

Can you use .filter and .get in the same query for Django?

I have a database where data is organized by a number that is unique, and a name that isn't unique. for example:
NumCOL: NameCOL:
1 Jay
2 Joel
3 Joey
4 Joel
Could I use a filter and get statement to grab names where the number is equal to a certain number? Let's say I have a form that lets a user pick a number from the database and the user picks the number 2.
num = request.POST.get('FormNumber') #num = 2
name = Database.objects.filter(NumCOL=num).get('NameCOL')
return HttpResponse(name)
Can something like this be done? I want to grab the name wherever the user selects based on their number. Based on the code I should get a response Joel.
Thanks for your help!
name = Database.objects.get(NumCOL=num)
#name = Database.objects.filter(NumCOL=num)
return HttpResponse(name.NameCOL)
As pointed by daniel in the comments and by metmirr in his answer, you don't need to do it like this. The following works just fine.
name = Database.objects.get(NumCOL=num)
return HttpResponse(name.NameCOL)
Retrieving all the fields of a single model does not add any overhead whatsoever to the query. Get is used to retrieve a single row and not a single column.
To retrieve a single column, you can do:
name = Database.objects.filter(NumCOL=num).values('NameCol')
To retrieve a single cell you can do
name = Database.objects.filter(NumCOL=num).values_list('NameCol', flat=True)
As a side note, by convention we name the model with the first letter in upper case and fields are all lower case.

Django Query lookup using Q Not Returning the Right Result

I want to do a basic query search. I want to use the fields city and area to perform datatbase lookup.
I want a case whereby if the user input the city and area in the search field, it should return the results. Also if the user input either city or area it should return the result.
The below code didn't return any result when I input city and area in the search field, and I have objects related to the query saved in the database. Instead it returns 'no result'.
def my_search(request):
try:
q= request.GET['q']
hots= Place.objects.filter(Q(city__icontains=q)&Q(area__icontains=q))
c_hos=hots.count()
return render(request, 'search/own_search.html',{'hots':hots, 'q':q, 'c_hos':c_hos})
except KeyError:
return render(request, 'search/own_search.html')
I even tried using the | and it won't return any result when I input the city and area together in the field.
What am I missing?
You should go with the or operator "|" if you want to accept a query of city only or area only. I suggest you to print variable q after being taken from request to see what in it and do your database query again. Another thought is maybe you should split the value of q into city and area.
In general all the kwargs in filter() are AND'ed together you can know more about complex look ups using Q in the Django Documentation
# For performing OR operation
hots= Place.objects.filter(Q(city__icontains=q) | Q(area__icontains=q))
# For performing AND operation
hots= Place.objects.filter(Q(city__icontains=q), Q(area__icontains=q))
should also be working without any problem.
EDIT:
If the variable "q" from the request contains both city and area in a single variable, this wont return anything since the logic you are using is not appropriate.
Assume a table
State City
California San Francisco
Then if in your q="California San Francisco", then whole query doesn't match anything.
__icontains works with something like this
q = "San" # Or q = "Franscisco"
result = Place.objects.filter(Q(state__icontains=q) | Q(city_icontains=q))
Then result would be having the object with state=California and city=San Franscisco.
It would be easy for you to use the same logic but try to enter either city or place in order to work with it. It would be much simpler.
Re-Edit:
If you want to filter every word in a string you can try using this:
list = str(q).split()
result = Place.objects.filter(reduce(operator.and_, (Q(place__contains=x) | Q(city__contains=x) for x in list)))
# Adding .distinct() to the filter might clean the QuerySet and remove duplication in this scenario.

Building a tuple to be rendered by a form

I have a model University which has a field city. I'm trying to build a form where the user can select cities or universities. The universities selection is fine:
universities = University.objects.all()
university = forms.ModelMultipleChoiceField(widget=CheckboxSelectMultiple, queryset=universities)
The method I'm trying to get the cities is what is causing me the problem. Here's what I currently have:
cities = []
for uni in universities:
cities.append(uni.city)
cities = tuple(cities)
city_select = forms.MultipleChoiceField(widget=CheckboxSelectMultiple, choices=cities)
This gives me the error too many values to unpack because the tuple isn't key paired. Is there any easier way to return the choices I've gathered, I feel like I'm going about it in the wrong way. If not, how do I key pair the tuples of cities?
I think a simple change like below, where each entry in cities is a tuple should make this work:
cities = []
for uni in universities:
cities.append((uni.city, uni.city))
cities = tuple(cities)
city_select = forms.MultipleChoiceField(widget=CheckboxSelectMultiple, choices=cities)
MultipleChoiceField doesn't want a tuple, it wants a queryset. You can use values_list to get one with the fields you want:
city_select = forms.MultipleChoiceField(widget=CheckboxSelectMultiple, queryset=University.objects.values_list('id', 'city'))

Need help parsing XML with ElementTree

I'm trying to parse the following XML data:
http://pastebin.com/UcbQQSM2
This is just an example of the 2 types of data I will run into. Companies with the needed address information and companies without the needed information.
From the data I need to collect 3 pieces of information:
1) The Company name
2) The Company street
3) The Company zipcode
I'm able to do this with the following code:
#Creates list of Company names
CompanyList = []
for company in xmldata.findall('company'):
name = company.find('name').text
CompanyList.append(name)
#Creates list of Company zipcodes
ZipcodeList = []
for company in xmldata.findall('company'):
contact_data = company.find('contact-data')
address1 = contact_data.find('addresses')
for address2 in address1.findall('address'):
ZipcodeList.append(address2.find('zip').text)
#Creates list of Company streets
StreetList = []
for company in xmldata.findall('company'):
contact_data = company.find('contact-data')
address1 = contact_data.find('addresses')
for address2 in address1.findall('address'):
StreetList.append(address2.find('street').text)
But it doesn't really do what I want it to, and I can't figure out how to do what I want. I believe it will be some type of 'if' statement but I don't know.
The problem is that where I have:
for address2 in address1.findall('address'):
ZipcodeList.append(address2.find('zip').text)
and
for address2 in address1.findall('address'):
StreetList.append(address2.find('street').text)
It only adds to the list the places that actually have a street name or zipcode listed in the XML, but I need a placemark for the companies that also DON'T have that information listed so that my lists match up.
I hope this makes sense. Let me know if I need to add more information.
But, basically, I'm trying to find a way to say if there isn't a zipcode/street name for the Company put "None" and if there is then put the zipcode/street name.
Any help/guidance is appreciated.
Well I am going to do a bad thing and suggest you use a conditional (ternary) operator.
StreetList.append(address2.find('street').text if address2.find('street').text else 'None')
So this statement says return address2.find('street').text if **address2.find('street') is not empty else return 'None'.
Additionally you could created a new method to do the same test and call it in both places, note my python is rusty but should get you close:
def returnNoneIfEmpty(testText):
if testText:
return testText
else:
return 'None'
Then just call it:
StreetList.append(returnNoneIfEmpty(address2.find('street').text))

Categories

Resources