I am trying to display distinct or unique values from my column/field category
The function from views.py:
def category(request, book_category):
latest_book_list = Books.objects.all().order_by('id')
return render_to_response('books/category.html', {'latest_book_list': latest_book_list})
I would like the line: latest_book_list = Books.objects.all().order_by('id')
To perform the mysql query:
mysql> select distinct category from books;
I have tried using the Books.objects.filter(category=book_category) but it returns blank.
Any suggestions?
Try this...
cateogries = Books.objects.values_list('category', flat=True).distinct()
latest_book_list = Books.objects.filter(category__id__in = categories)
Related
I am currently working on a coursework project for school and it is a database system with a user interface using Tkinter, Python and SQLite3. I have made a form to add, delete, update and search for customers. I am able to display the result from a single field, however, I am struggling to get the message box to display all the fields, which is what I would like it to do. I have attached photos of the form along with the code. Thank you in advance.
def SearchCustomer(self):
customerid = self.CustomerEntry.get();
with sqlite3.connect("LeeOpt.db") as db:
cursor = db.cursor()
search_customer = ('''SELECT * FROM Customer WHERE CustomerID = ?''')
cursor.execute(search_customer, [(customerid)])
results = cursor.fetchall()
if results:
for i in results:
tkinter.messagebox.showinfo("Notification",i[0])
It is because you showed only the first column (i[0]) from result.
Since there should be only one record for a specific customer ID, you should use fetchone() instead of fetchall(), then you can show the whole record as below:
def SearchCustomer(self):
customerid = self.CustomerEntry.get()
with sqlite3.connect("LeeOpt.db") as db:
cursor = db.cursor()
search_customer = "SELECT * FROM Customer WHERE CustomerID = ?"
cursor.execute(search_customer, [customerid])
result = cursor.fetchone() # there should be only one record for specific customer ID
if result:
tkinter.messagebox.showinfo("Notification", "\n".join(str(x) for x in result))
In a prototype application that uses Python and SQLAlchemy with a PostgreSQL database I have the following schema (excerpt):
class Guest(Base):
__tablename__ = 'guest'
id = Column(Integer, primary_key=True)
name = Column(String(50))
surname = Column(String(50))
email = Column(String(255))
[..]
deleted = Column(Date, default=None)
I want to build a query, using SQLAlchemy, that retrieves the list of guests, to be displayed in the back-office.
To implement pagination I will be using LIMIT and OFFSET, and also COUNT(*) OVER() to get the total amount of records while executing the query (not with a different query).
An example of the SQL query could be:
SELECT id, name, surname, email,
COUNT(*) OVER() AS total
FROM guest
WHERE (deleted IS NULL)
ORDER BY id ASC
LIMIT 50
OFFSET 0
If I were to build the query using SQLAlchemy, I could do something like:
query = session.query(Guest)
query = query.filter(Login.deleted == None)
query = query.order_by(Guest.id.asc())
query = query.offset(0)
query = query.limit(50)
result = query.all()
And if I wanted to count all the rows in the guests table, I could do something like this:
from sqlalchemy import func
query = session.query(func.count(Guest.id))
query = query.filter(Login.deleted == None)
result = query.scalar()
Now the question I am asking is how to execute one single query, using SQLAlchemy, similar to the one above, that kills two birds with one stone (returns the first 50 rows and the count of the total rows to build the pagination links, all in one query).
The interesting bit is the use of window functions in PostgreSQL which allows the abovementioned behaviour, thus saving you from having to query twice but just once.
Is it possible?
Thanks in advance.
So I could not find any examples in the SQLAlchemy documentation, but I found these functions:
count()
over()
label()
And I managed to combine them to produce exactly the result I was looking for:
from sqlalchemy import func
query = session.query(Guest, func.count(Guest.id).over().label('total'))
query = query.filter(Guest.deleted == None)
query = query.order_by(Guest.id.asc())
query = query.offset(0)
query = query.limit(50)
result = query.all()
Cheers!
P.S. I also found this question on Stack Overflow, which was unanswered.
how can i have a subquery in django's queryset? for example if i have:
select name, age from person, employee where person.id = employee.id and
employee.id in (select id from employee where employee.company = 'Private')
this is what i have done yet.
Person.objects.value('name', 'age')
Employee.objects.filter(company='Private')
but it not working because it returns two output...
as mentioned by ypercube your use case doesn't require subquery.
but anyway since many people land into this page to learn how to do sub-query here is how its done.
employee_query = Employee.objects.filter(company='Private').only('id').all()
Person.objects.value('name', 'age').filter(id__in=employee_query)
Source:
http://mattrobenolt.com/the-django-orm-and-subqueries/
ids = Employee.objects.filter(company='Private').values_list('id', flat=True)
Person.objects.filter(id__in=ids).values('name', 'age')
The correct answer on your question is here https://docs.djangoproject.com/en/2.1/ref/models/expressions/#subquery-expressions
As an example:
>>> from django.db.models import OuterRef, Subquery
>>> newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
>>> Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))
You can create subqueries in Django by using an unevaluated queryset to filter your main queryset. In your case, it would look something like this:
employee_query = Employee.objects.filter(company='Private')
people = Person.objects.filter(employee__in=employee_query)
I'm assuming that you have a reverse relationship from Person to Employee named employee. I found it helpful to look at the SQL query generated by a queryset when I was trying to understand how the filters work.
print people.query
As others have said, you don't really need a subquery for your example. You could just join to the employee table:
people2 = Person.objects.filter(employee__company='Private')
hero_qs = Hero.objects.filter(category=OuterRef("pk")).order_by("-benevolence_factor")
Category.objects.all().annotate(most_benevolent_hero=Subquery(hero_qs.values('name')[:1]))
the generated sql
SELECT "entities_category"."id",
"entities_category"."name",
(SELECT U0."name"
FROM "entities_hero" U0
WHERE U0."category_id" = ("entities_category"."id")
ORDER BY U0."benevolence_factor" DESC
LIMIT 1) AS "most_benevolent_hero"
FROM "entities_category"
For more details, see this article.
Take good care with onlyif your subqueries don't select the primary key.
Example:
class Customer:
pass
class Order:
customer: Customer
pass
class OrderItem:
order: Order
is_recalled: bool
Customer has Orders
Order has OrderItems
Now we are trying to find all customers with at least one recalled order-item.(1)
This will not work properly
order_ids = OrderItem.objects \
.filter(is_recalled=True) \
.only("order_id")
customer_ids = OrderItem.objects \
.filter(id__in=order_ids) \
.only('customer_id')
# BROKEN! BROKEN
customers = Customer.objects.filter(id__in=customer_ids)
The code above looks very fine, but it produces the following query:
select * from customer where id in (
select id -- should be customer_id
from orders
where id in (
select id -- should be order_id
from order_items
where is_recalled = true))
Instead one should use select
order_ids = OrderItem.objects \
.filter(is_recalled=True) \
.select("order_id")
customer_ids = OrderItem.objects \
.filter(id__in=order_ids) \
.select('customer_id')
customers = Customer.objects.filter(id__in=customer_ids)
(1) Note: in a real case we might consider 'WHERE EXISTS'
The code in the sequence is working fine, but looking to improve the MySQL code to a more efficient format.
The first case is about a function that received a parameter and returns the customerID from MySQL db:
def clean_table(self,customerName):
getCustomerIDMySQL="""SELECT customerID
FROM customer
WHERE customerName = %s;"""
self.cursorMySQL.execute(getCustomerIDMySQL,(customerName))
for getID_row in self.cursorMySQL:
customerID=getID_row[0]
return customerID
In the case we know before hand that the result will be just one output, how to get the same thing into my getID_row, without using "for" statement?
For the second case, the function is running with the table name ('customer') on it...
def clean_tableCustomer(self):
cleanTableQuery = """TRUNCATE TABLE customer;"""
self.cursorMySQL.execute(cleanTableQuery)
setIndexQuery = """ALTER TABLE customer AUTO_INCREMENT = 1;"""
self.cursorMySQL.execute(setIndexQuery)
then, how to replace the table name as a parameter passed through the function? Here is how I tried to get this done:
def clean_table(self,tableName):
cleanTableQuery = """TRUNCATE TABLE %s;"""
self.cursorMySQL.execute(cleanTableQuery,(tableName))
setIndexQuery = """ALTER TABLE %s AUTO_INCREMENT = 1;"""
self.cursorMySQL.execute(setIndexQuery,(tableName))
But MySQL didn't work this time.
All comments and suggestions are highly appreciated.
For the first case (simple, but easy to get a KeyError when there is no row):
customerID = self.cursorMySQL.fetchone()[0]
More correct is to implement a new method for the cursor class:
def autofetch_value(self, sql, args=None):
""" return a single value from a single row or None if there is no row
"""
self.execute(sql, args)
returned_val = None
row = self.fetchone()
if row is not None:
returned_val = row[0]
return returned_val
For the second case:
def clean_table(self,tableName):
cleanTableQuery = """TRUNCATE TABLE %s;""" % (tableName,)
self.cursorMySQL.execute(cleanTableQuery)
setIndexQuery = """ALTER TABLE %s AUTO_INCREMENT = 1;""" % (tableName,)
self.cursorMySQL.execute(setIndexQuery)
Make sure you sanitize the data, since the cursor won't.
Unfortunately, you cannot parametrize the name of a table (see this post). You will have to use Python string operations to do what you are attempting here.
Hope this helps, it took me a while to find out when I ran into this issue.
I need to write a query that returns all object less that or equal to a certain day of a certain month. The year is not important. It's easy enough to get an object by a particular day/month (assume now = datetime.datetime.now()):
posts = TodaysObject.objects.filter(publish_date__day=now.day, publish_date__month=now.month)
But I can't do this:
posts = TodaysObject.objects.filter(publish_date__day__lte=now.day, publish_date__month=now.month)
Seems that Django thinks I'm trying to do a join when combining multiple field lookups (publish_date__day__lte). What's the best way to do this in Django?
Try this:
Option 1:
from django.db.models import Q
datafilter = Q()
for i in xrange(1, now.day+1):
datafilter = datafilter | Q(publish_date__day=i)
datafilter = datafilter & Q(publish_date__month=now.month)
posts = TodaysObject.objects.filter(datafilter)
Option 2:
Perform raw sql query:
def query_dicts(query_string, *query_args):
from django.db import connection
cursor = connection.cursor()
cursor.execute(query_string, query_args)
col_names = [desc[0] for desc in cursor.description]
while True:
row = cursor.fetchone()
if row is None:
break
row_dict = dict(izip(col_names, row))
yield row_dict
return
posts = query_dicts('SELECT * FROM tablename WHERE DAY(publish_date)<=%s AND MONTH(publish_date)=%s', now.day, now.month)
Using extra() function:
posts = TodaysObject.objects.extra([where='DAY(publish_date)<=%d AND MONTH(publish_date)=%d' % (now.day, now.month)])
It's assumed that you are using MySQL. For PostgreSQL, you need to change DAY(publish_date) and MONTH(publish_date) to DATE_PART('DAY', publish_date) and DATE_PART('MONTH', publish_date) respectively.
it's not always portable from one database engine to another, but you may want to look into the extra() queryset method.
from django docs
this allows you to inject raw sql to construct more complex queries than the django queryset api.
if your application needs to be portable to different database engines, you can try restructuring so you have day, month, and year integer fields.
now = datetime.date.today()
post = TodaysObject.objects.raw("SELECT * FROM (app_name)_todaysobject WHERE DAY(publish_date) =%(day)d AND MONTH(publish_date)=%(month)d" %{'day' : today.day, 'month':today.month} )