Django: Error with inserting dictionary to my_model.objects.raw() method - django

I'm performing a query in a function inside the views.py as follows:
input_dict = {'id': 'id',
'table': 'my_table',
'first_col': 'my_first_col',
'first_name': 'my_first_name'}
query = '''
SELECT %(id)s
FROM %(table)s
WHERE %(first_col)s = %(first_name)s '''
qs = my_model.objects.raw(query, input_dict)
print(qs)
which prints:
qs: <RawQuerySet:
SELECT id
FROM my_table
WHERE my_first_col = my_first_name >
However, when I try to run this line:
ids = [item.id for item in qs]
it gives me an error:
psycopg2.errors.SyntaxError: syntax error at or near "'my_table'"
LINE 3: FROM 'my_table'
and also:
django.db.utils.ProgrammingError: syntax error at or near "'my_table'"
LINE 3: FROM 'my_table'
What should I do?

django are not able to excecut your query.
I believe what you trying to acheive via the raw function it is not possible.
read the docs https://docs.djangoproject.com/en/3.0/topics/db/sql/ for better understanding how the function work
your dynamic options are realted either to the table fields and the where params:
the select fields
you can map fields in the query to model fields using the translations
argument to raw()
like :
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
the where params
If you need to perform parameterized queries, you can use the params
argument to raw()
lname = 'Doe'
Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
at the end of the day, this will work, although it's not recomended unless you know exactly what you are doing as it might expose your system to sql attacks
query = '''
SELECT %(id)s
FROM %(table)s
WHERE %(first_col)s = %(first_name)s '''
query = query%input_dict
qs = my_model.objects.raw(query)

Related

Overriding get_queryset, but get empty result set

So I have a database of books, and I want to search it based on filters and keywords so I've overridden the get_queryset method in my BookSearch view:
class BookSearch(generics.ListAPIView):
serializer_class = ProductDetailViewSerializer
model = ProductDetailView
def get_queryset(self):
queryset = None
categories = self.kwargs['categories'].rstrip()
keywords = self.kwargs['keywords'].rstrip()
if isinstance(categories, str) and isinstance(keywords, str):
book_filter = BookFilter(categories)
sql = self.get_sql(categories, keywords, book_filter)
queryset = ProductDetailView.objects.filter(
id__in=RawSQL(sql, book_filter.params)
)
message = f"{queryset.query}"
log_to_file('BookSearch.log', 'BookSearch.get_queryset', message)
return queryset
That log_to_file call logs the query that django uses, which I've abbreviated here
but is as follows:
SELECT `jester_productdetailview`.`id`,
`jester_productdetailview`.`isbn`,
`jester_productdetailview`.`title`
FROM `jester_productdetailview`
WHERE `jester_productdetailview`.`id` IN (
select id from jester_productdetailview
where ( authors like '%Beatrix%' or
illustrators like '%Beatrix%' or
title like '%Beatrix%' ) )
ORDER BY `jester_productdetailview`.`title` ASC
If I run that query in my database manually, I get 186 rows:
'119371','9780723259572','A Beatrix Potter Treasury'
'130754','9780241293348','A Christmas Wish'
'117336','9780241358740','A Pumpkin for Peter' ...
To get the query above, I call the view through the API, yet by the time the queryset is returned, there are no results ???
http://127.0.0.1:8000/api/book-search/{"filter": "all"}/Beatrix/
returns []
You are returning queryset only within the if condition only. By default, the function is sending None. Return the queryset outside the if condition as well.

Using distinct on annotations

I am trying to get a distinct list of items. The db has a created field which is datetime and I need it as a date for my query. So I added an annotation. The problem is that distinct won't work on the annotation...
distinct_failed_recharges = recharges.filter(
status=FAILED
).annotate(
created_date=TruncDate('created')
).distinct(
'created_date', 'sim', 'product_type', 'failure_reason'
).values_list('id', flat=True)
This is the error that I get:
django.core.exceptions.FieldError: Cannot resolve keyword 'created_date' into field
I get the same error in django 1.11 doing:
qs = queryset.annotate(day=TruncDay('date')).distinct('day')
ids = list(qs.values_list('id', flat=True))
results with this error:
FieldError: Cannot resolve keyword 'day' into field.
This is very weird since I try to evaluate 'id'...
The only workaround that I've found is:
qs = queryset.annotate(day=TruncDay('date')).distinct('day')
objects_list = list(qs)
ids = [object.id for object in objects_list]
This is very inefficient, but hopefully my list is not too long...

Subquery select where clause

I have this sql statement:
SELECT * FROM result WHERE bet_id IN (SELECT id FROM bet WHERE STATUS="Active")
and this is my view:
def manageresult(request):
if 'usid' in request.session:
result = Result.objects.all()
admin = Admin.objects.get(id=request.session['usid'])
return render(request, 'manageresult.html', {'result':result,'admin':admin})
else:
return redirect('login')
How to change result = Result.objects.all() to that sql statement?
This is bet model:
class Bet(models.Model):
status = models.CharField(max_length=20, default="Active")
This is Result model:
class Result(models.Model):
bet = models.OneToOneField(Bet, on_delete=models.CASCADE)
disclaimer: when writing this answer, the models in question were not known
In case the Result model has a ForeignKey to Bet, you can filter by joins - it would make it more like
result = Result.objects.filter(bet__status='Active')
which would translate to the below SQL query
SELECT result.* FROM result INNER JOIN bet on result.bet_id = bet.id WHERE bet.STATUS="Active"
See Django's documentation on Lookups that span relationships
If that is not the case, Todor's answer is the way to go
You can use bet__status=...:
result = Result.objects.filter(bet__status='Active')
You can make querysets nested
result = Result.objects.filter(bet__in=Bet.objects.filter(status='active'))

How to make a generic variable in a query

I have this query:
search = request.GET['q']
Entries = Entry.objects.filter(Q(field1__icontains=search), Q(field2__icontains=search), Q(field3__icontains=search))
Is there an elegant way to make this cleaner? (I mean make somthing generic like Q(var_field__icontains=search) and the var_field will be retrived from a requested post that is one of these values field1, field2, field3).
So, is this working for you?
search = request.GET['q']
fieldnames = ('field1', 'field2', 'field3')
filters = reduce(operator.and_,
(Q(**{'{}__icontains'.format(fieldname): search})
for fieldname in fieldnames))
Entries = Entry.objects.filter(filters)

Django set form initial data in view

I'm trying to populate a django modelform with initial data provided from an external source. To achieve that I start by pull all the needed data from the external source:
url =('http://myapi.example.com')
data = urllib2.urlopen(url)
result = json.load(data)
api_data_name = result['properties']['name']
api_data_type = result['properties']['type']
Followed by populating a dict which will serve as initial data to my form:
data = {}
for field in my_model._meta.fields:
if field.name == 'name':
data[field.name] = api_data_name
form = MyEditForm(initial=data)
Then I'm passing the form to the template and the initial data is populating my text fields as expected, but now I need to be able to set a value of a select field based on a string I receive from my external source and Im not getting how can I achieve that, since doing something like:
if field.name == 'type':
data[field.name] = api_data_type
Wont do the job cause the select element has "0", "1", "2", etc as options value and not the long description i get from api_data_type variable.
How can I get the long_description from all the options <option value="1">long_description</option> of my select field in my view so i can compare each one with api_data_type?
Heres a sample of my models.py and forms.py:
#models.py
TYPE = (
('0',_(u'Type1')),
('1',_(u'Type2')),
('2',_(u'Type3')),
)
class MyModel(models.Model):
...
type=models.CharField(max_length=30,choices=TYPE,blank=True)
...
#forms.py
class MyEditForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
...
'type': Select(attrs={'class':'select-small span2'}),
...
}
Found out how to accomplish what I asked.
# For select fields
if field.name == 'classification':
for choice in field.choices:
if choice[1].lower() == api_poi_classification.lower():
data[field.name] = choice[0]
And for any of ya trying to populate many-to-many fields (as checkboxes in my case)
# Many to many fields populate
for field in hotel_poi._meta.many_to_many:
if field.name == 'views':
if u'Vista' in api_poi_review_fields:
api_vistas = api_poi_review[u'Vista']
# The api_vistas string comes from api in the format (v1; v2; v3; v4)
views = api_vistas.split(';')
choices = field.get_choices()
temp = []
for view in views:
for choice in choices:
if view.lower().strip() == choice[1].lower().strip():
temp.append(choice[0])
data[field.name]=temp
All of this could be avoided if I had direct database access... In that case i would just need to set an object instance like m = MyModel.objects.filter(id=1) and call form = MyEditForm(instance=m)
But that was not the case and that's what makes this question a bit particular.