Why is Django Paramaterized query not working - django

I have been struggling with this simple query in django. I have checked a lot over internet. I tried using similar syntax - yet no luck.
In my application on html page I need to display some specific record. And for that i want to use parameterized select statement. But the query is not working..
I have checked id2 is getting correct value from previous html page..
And I know I can use APIs but for my databases classes I need to use raw queries in my application.
Can someone please help me here...
def details(request, id2):
temp = 'test3'
data = Posts.objects.raw('''select * from posts_posts where posts_posts.id = %s ''', id2)
context ={
'post' : data,

If you run that code you will see an error, since that is not the correct format for a call to raw. If you can't see the error output anywhere, then you have yet another problem for another post.
The correct format for raw is: .raw(sql, iterable-values), like this:
posts = Posts.objects.raw("select * ..", [id2, ]) # or use (id2,) for a tuple
<RawQuerySet: select * from ... where id = 5>
To get the actual list, since that just gives you a Query, you need to evaluate it somehow:
posts_list = list(posts)
first_post = posts[0]
Be careful, if you don't evaluate the QuerySet then it can be re-run a second time. Please convert it to a list() before doing further operations on it.

Related

Object is not legal as a SQL literal value

Long time reader, first time poster, please be gentle.
I've been working on a web app using Flask and SQLAlchemy that allows users to review and comment on MMA fights. I have a list of fights in a SQL table appropriately named "fights" and I'm trying to use dynamic routing to filter through the data. I have a list of all the fights on one route like so:
#app.route('/ufc251')
#login_required
def ufc251():
return render_template('ufc251.html', fights=Fight.query.all())
which helped me make a slick page with all the fights listed, and then made another route for info on individual fights like so:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html')
so far, so good. If I click on a fight from the main page i get sent to a url fightsite/fights/<fight_id>, which is perfect. The problem that I'm having is that I can't for the life of me figure out how to call the data from the row for a single fight. If i change my route to:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id).first())
I get the error
sqlalchemy.exc.ArgumentError: Object <Fight 1> is not legal as a SQL literal value
but if i give id a value (i.e. id=1) it will display the data from the first row in my fights table, so i feel like the problem is in the (id=id) part, but after hours of scouring the internet, I can't seem to find a solution.
Any help would be greatly appreciated. And yes, I've read the other StackOverflow article on this subject, however the answer doesn't seem to apply to this situation.
Thank you in advance!
I figured it out, however I decided I'd leave the question in case anybody else has this issue.
i changed:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id).first())
to:
#app.route('/fight/<int:id>')
#login_required
def fight(id):
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id.id).first())
because initially it was passing the argument 'fight_1' instead of just '1'. I hope nobody else has to spend this long trying to solve the same problem!
The use of id to hold a Fight confuses things. Then there's the double query when one would suffice.
Consider changing
id = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=Fight.query.filter_by(id=id).first())
to
fight = Fight.query.filter_by(id=id).first_or_404()
return render_template('fight.html', fight=fight)

How to run a combination of query and filter in elasticsearch?

I am experimenting using elasticsearch in a dummy project in django. I am attempting to make a search page using django-elasticsearch-dsl. The user may provide a title, summary and a score to search for. The search should match all the information given by the user, but if the user does not provide any info about something, this should be skipped.
I am running the following code to search for all the values.
client = Elasticsearch()
s = Search().using(client).query("match", title=title_value)\
.query("match", summary=summary_value)\
.filter('range', score={'gt': scorefrom_value, 'lte': scoreto_value})
When I have a value for all the fields then the search works correctly, but if for example I do not provide a value for the summary_value, although I am expecting the search to continue searching for the rest of the values, the result is that it comes up with nothing as a result.
Is there some value that the fields should have by default in case the user does not provide a value? Or how should I approach this?
UPDATE 1
I tried using the following, but it returns every time no matter the input i am giving the same results.
s = Search(using=client)
if title:
s.query("match", title=title)
if summary:
s.query("match", summary=summary)
response = s.execute()
UPDATE 2
I can print using the to_dict().
if it is like the following then s is empty
s = Search(using=client)
s.query("match", title=title)
if it is like this
s = Search(using=client).query("match", title=title)
then it works properly but still if i add s.query("match", summary=summary) it does nothing.
You need to assign back into s:
if title:
s = s.query("match", title=title)
if summary:
s = s.query("match", summary=summary)
I can see in the Search example that django-elasticsearch-dsl lets you apply aggregations after a search so...
How about "staging" your search? I can think if the following:
#first, declare the Search object
s = Search(using=client, index="my-index")
#if parameter1 exists
if parameter1:
s.filter("term", field1= parameter1)
#if parameter2 exists
if parameter2:
s.query("match", field=parameter2)
Do the same for all your parameters (with the needed method for each) so only the ones that exist will appear in your query. At the end just run
response = s.execute()
and everything should work as you want :D
I would recommend you to use the Python ES Client. It lets you manage multiple things related to your cluster: set mappings, health checks, do queries, etc.
In its method .search(), the body parameter is where you send your query as you normally would run it ({"query"...}). Check the Usage example.
Now, for your particular case, you can have a template of your query stored in a variable. You first start with, let's say, an "empty query" only with filter, just like:
query = {
"query":{
"bool":{
"filter":[
]
}
}
}
From here, you now can build your query from the parameters you have.
This is:
#This would look a little messy, but it's useful ;)
#if parameter1 is not None or emtpy
#(change the if statement for your particular case)
if parameter1:
query["query"]["bool"]["filter"].append({"term": {"field1": parameter1}})
Do the same for all your parameters (for strings, use "term", for ranges use "range" as usual) and send the query in the .search()'s body parameter and it should work as you want.
Hope this is helpful! :D

How to make filtering non model data in flask-admin

I have to make dashboard like view in flask-admin that will use data retrieved from external API. I have already written a functions that get date ranges and return data from that range. I should use BaseView probably but I don't know how to actually write it to make filters work. This is example function that i have to use: charts = generate_data_for_dashboard('164', '6423FACA-FC71-489D-BF32-3A671AB747E3', '2018-03-01', '2018-09-01'). Those params should be chosen from 3 different dropdowns. So far I know only how to render views with pre coded data like this :
class DashboardView(BaseView):
kwargs = {}
#expose('/', methods=('GET',))
def statistics_charts(self):
user = current_user
company = g.company
offices = Office.query.filter_by(company_id=company.id)
self.kwargs['user'] = user
self.kwargs['company'] = company
charts = generate_data_for_dashboard('164', '6423FACA-FC71-489D-BF32-3A671AB747E3', '2018-03-01', '2018-09-01')
self.kwargs['chart1'] = charts[0]
self.kwargs['chart2'] = charts[1]
return self.render('stats/dashboard.html', **self.kwargs)
But I need some kind of form to filter it. In addition date filter dropdown should have dynamic options : current_week, last_week, current_month, last_month, last_year. Don't know where to start.
You should use WTForms to build a form. You then have to decide if you want the data to be fetched on Submit or without a reload of the page. In the former case, you can just return the fetched information on the response page in your statistics_charts view. But if you want the data to update without a reload, you'll need to use JavaScript to track the form field changes, send the AJAX request to the API, and then interpret the resulting JSON and update your dashboard graphs and tables as needed.
I have not used it, but this tutorial says you can use Dash for substantial parts of this task, while mostly writing in Python. So that could be something to check out. There is also flask_jsondash which might work for you.

How to use query results in url regex in Django

I want to use a database query to generate my URL configuration. Something like:
states = State.objects.all().values_list('pk', flat=True)
And then a regex like:
(r'^state/(?P<state>' + '|'.join(states) + ')/$'
The idea is that I can have URLs like:
/state/ca/
/state/az/
Etc.
The problem is that when I do syncdb, the query above fails, throwing a DatabaseError.
Is there a good way to do this? I've tried the obvious change, which would be:
try:
states = State.objects.all().values_list('pk', flat=True)
except DatabaseError:
# First sync, use dummy data
states = []
But this doesn't work because the exception is thrown at the regex, not at the query definition.
Ideas?
Why do you need to constrain this in the URL pattern itself? Much better to accept all two-letter codes, and check in the view.
(r'^state/(?P<state_code>\w{2})/$'
def view_state(request, state_code):
state = get_object_or_404(State, pk=state_code)

Django: How to use django.forms.ModelChoiceField with a Raw SQL query?

I'm trying to render a form with a combo that shows related entities. Therefore I'm using a ModelChoiceField.
This approach works well, until I needed to limit which entities to show. If I use a simple query expression it also works well, but things break if I use a raw SQL query.
So my code that works, sets the queryset to a filter expression.
class ReservationForm(forms.Form):
location_time_slot = ModelChoiceField(queryset=LocationTimeSlot.objects.all(), empty_label="Select your prefered time")
def __init__(self,*args,**kwargs):
city_id = kwargs.pop("city_id") # client is the parameter passed from views.py
super(ReservationForm, self).__init__(*args,**kwargs)
# TODO: move this to a manager
self.fields['location_time_slot'].queryset = LocationTimeSlot.objects.filter(city__id = city_id )
BUT, if I change that to a raw query I start having problems. Code that does not work:
class ReservationForm(forms.Form):
location_time_slot = ModelChoiceField(queryset=LocationTimeSlot.objects.all(), empty_label="Select your prefered time")
def __init__(self,*args,**kwargs):
city_id = kwargs.pop("city_id") # client is the parameter passed from views.py
super(ReservationForm, self).__init__(*args,**kwargs)
# TODO: move this to a manager
query = """SELECT ts.id, ts.datetime_to, ts.datetime_from, ts.available_reserves, l.name, l.'order'
FROM reservations_locationtimeslot AS ts
INNER JOIN reservations_location AS l ON l.id = ts.location_id
WHERE l.city_id = %s
AND ts.available_reserves > 0
AND ts.datetime_from > datetime() """
time_slots = LocationTimeSlot.objects.raw(query, [city_id])
self.fields['location_time_slot'].queryset = time_slots
The first error I get when trying to render the widget is: 'RawQuerySet' object has no attribute 'all'
I could solve that one thanks to one of the commets in enter link description here, by doing:
time_slots.all = time_slots.__iter__ # Dummy fix to allow default form rendering with raw SQL
But now I'm getting something similar when posting the form:
'RawQuerySet' object has no attribute 'get'
Is there a proper way to prepare a RawQuerySet to be used by ModelChoiceField?
Thanks!
Are you sure you actually need a raw query there? Just looking at that query, I can't see any reason you can't just do it with filter(location__city=city_id, available_reserves__gte=0, datetime_from__gt=datetime.datetime.now()).
Raw query sets are missing a number of methods that are defined on conventional query sets, so just dropping them in place isn't likely to work without writing your own definitions for all those methods.
I temporarily fixed the problem adding the missing methods.
The way I'm currently using the ModelChoiceField I only needed to add the all() and get() methods, but in different scenarios you might need to add some other methods as well. Also this is not a perfect solution because:
1) Defining the get method this way migth produce incorrect results. I think the get() method is used to validate that the selected option is within the options returned by all(). The way I temporarily implemented it only validates that the id exists in the table.
2) I guess the get method is less performant specified this way.
If anyone can think of a better solution, please let me know.
So my temporary solution:
class LocationTimeSlotManager(models.Manager):
def availableSlots(self, city_id):
query = """SELECT ts.id, ts.datetime_to, ts.datetime_from, ts.available_reserves, l.name, l.'order'
FROM reservations_locationtimeslot AS ts
.....
.....
MORE SQL """
time_slots = LocationTimeSlot.objects.raw(query, [city_id])
# Dummy fix to allow default form rendering with raw SQL
time_slots.all = time_slots.__iter__
time_slots.get = LocationTimeSlot.objects.get
return time_slots