Order by name with number Django - python

I have approximately 500 device objects in an sqlite db, with a name field such as:
Device-0
Device-1
Device-2
Device-3
...
...
Device-500
When listing these with django, I want it to list based on the number after the semicolon in the name, as shown above.
I tried:
queryset = Device.objects.all().order_by('name')
Also from this question:
queryset = Device.objects.annotate(int_sort=Cast("name", IntegerField())).order_by("int_sort", "name")
Both of these produce this result:
Device-0
Device-1
Device-10
Device-100
Device-101
...
Any help would be greatly appreciated.

You're looking for a "natural sort" ("dictionary sort") order.
That's not built-in to SQLite (nor any other database I know of).
If all of your rows do follow a XYZ-123 format, you could
add an .extra() where= column with an expression that splits the column by a dash, then casts the second part to a number
and then order_by that extra column.
Example
Here's an example you can run in your SQLite shell:
sqlite> create table device (name text);
sqlite> insert into device (name) values ('Device-1'),('Device-2'),('Device-3'),('Device-4'),('Device-5'),('Device-6'),('Device-7'),('Device-8'),('Device-9'),('Device-10'),('Device-11'),('Device-12'),('Device-13'),('Device-14'),('Device-15'),('Device-16'),('Device-17'),('Device-18'),('Device-19'),('Device-20'),('Device-21'),('Device-22'),('Device-23'),('Device-24'),('Device-25'),('Device-26'),('Device-27'),('Device-28'),('Device-29'),('Device-30'),('Device-31'),('Device-32'),('Device-33'),('Device-34'),('Device-35'),('Device-36'),('Device-37'),('Device-38'),('Device-39');
sqlite> select * from device order by name limit 10;
Device-1
Device-10
Device-11
Device-12
Device-13
Device-14
Device-15
Device-16
Device-17
Device-18
sqlite> select *, cast(substr(name,instr(name, '-')+1) as number) number from device order by number limit 10;
Device-1|1
Device-2|2
Device-3|3
Device-4|4
Device-5|5
Device-6|6
Device-7|7
Device-8|8
Device-9|9
Device-10|10
With this example, you should (but I didn't verify since I don't have a suitable Django app on my hands) be able to do
Device.objects.all().extra(
select={'device_number': "cast(substr(name,instr(name, '-')+1) as number)"},
order_by='device_number',
)

Related

How does set_group_by works in Django?

I was writing the following query:
claim_query = ClaimBillSum.objects.filter(claim__lob__in = lobObj)\
.annotate(claim_count = Count("claim__claim_id", distinct=True))\
.annotate(claim_bill_sum = Sum("bill_sum"))\
.values("claim__body_part", "claim_count", "claim_bill_sum")\
.order_by("claim__body_part")
When I checked the query property, it was grouped by all properties of the tables related in this query, not only the ones selected in the values() function, when I only wanted to group by claim__body_part.
As I searched for a way to change the group by instruction, I found the query.set_group_by() function, that when applied, fixed the query in the way I wanted:
claim_query.query.set_group_by()
SELECT
"CLAIM"."body_part",
COUNT(DISTINCT "claim_bill_sum"."claim_id") AS "claim_count",
SUM("claim_bill_sum"."bill_sum") AS "claim_bill_sum"
FROM
"claim_bill_sum"
INNER JOIN "CLAIM" ON
("claim_bill_sum"."claim_id" = "CLAIM"."claim_id")
WHERE
"CLAIM"."lob_id" IN (SELECT U0."lob_id" FROM "LOB" U0 WHERE U0."client_id" = 1)
GROUP BY
"CLAIM"."body_part"
ORDER BY
"CLAIM"."body_part" ASC
But I couldn't find any information in Django documentation or anywhere else to better describe how this function works. Why the default group by is selecting all properties, and how .set_group_by() works, selecting exactly the property I wanted?

%s variable in Query Execution Python 3.8 (pymssql)

I have a python script with a basic GUI that logs into a DB and executes a query.
The Python script also asks for 1 parameter called "collection Name" which is taken from the tkinter .get function and is added as a %s inside the Query text. The result is that each time I can execute a query with a different "Collection name". This works and it is fine
Now, I want to add a larger string of Collection Names into my .get function so I can do cursor.execute a query with multiple collection names to get more complex data. But I am having issues with inputing multiple "collection names" into my app.
Below is a piece of my Query1, which has the %s variable that it then gets from the input to tkinter.
From #Session1
Join vGSMRxLevRxQual On(#Session1.SessionId = vGSMRxLevRxQual.SessionId)
Where vGSMRxLevRxQual.RxLevSub<0 and vGSMRxLevRxQual.RxLevSub>-190
and #Session1.CollectionName in (%s)
Group by
#Session1.Operator
Order by #Session1.Operator ASC
IF OBJECT_ID('tempdb..#SelectedSession1') IS NOT NULL DROP TABLE #SelectedSession1
IF OBJECT_ID('tempdb..#Session1') IS NOT NULL DROP TABLE #Session1
Here, is where I try to execute the query
if Query == "GSMUERxLevelSub" :
result = cursor.execute(GSMUERxLevelSub, (CollectionName,))
output = cursor.fetchmany
df = DataFrame(cursor.fetchall())
filename = "2021_H1 WEEK CDF GRAPHS().xlsx"
df1 = DataFrame.transpose(df, copy=False)
Lastly, here is where I get the value for the Collection name:
CollectionName = f_CollectionName.get()
enter image description here
enter code here
Your issues are due to a list/collection being a invalid parameter.
You'll need to transform collectionName
collection_name: list[str] = ['collection1', 'collection2']
new_collection_name = ','.join(f'"{c}"' for c in collection_name)
cursor.execute(sql, (new_collection_name,))
Not sure if this approach will be susceptible to SQL injection if that's a concern.
Edit:
Forgot the DBAPI would put another set of quotes around the parameters. If you can do something like:
CollectionName = ["foo", "bar"]
sql = f"""
From #Session1
Join vGSMRxLevRxQual On(#Session1.SessionId = vGSMRxLevRxQual.SessionId)
Where vGSMRxLevRxQual.RxLevSub<0 and vGSMRxLevRxQual.RxLevSub>-190
and #Session1.CollectionName in ({",".join(["%s"] * len(CollectionName))})
"""
sql += """
Group by
#Session1.Operator
Order by #Session1.Operator ASC
"""
cursor.execute(sql, (CollectionName,))
EDIT: Update to F-string

How to write raw sql query in odoo

I want to write raw sql query for following code.
product_ids = self.env['product.product'].with_context(warehouse=warehouse_id.ids).search([]).filtered(lambda p:p.qty_available > 0)
The product qty_available is a computed field and the compute method _compute_quantities depends on the result of _compute_quantities_dict to get the quantities dict and _get_domain_locations to parses the context and returns a list of location_ids based on it.
You need to write a query that gets the same result.
You can check the PostgreSQL log to see how many queries as executed for this python statement.
The following query is the last one executed to get the result (generated using a demo database):
SELECT min("stock_move".id) AS id, count("stock_move".id) AS "product_id_count" , sum("stock_move"."product_qty") AS "product_qty","stock_move"."product_id" as "product_id"
FROM "stock_location" as "stock_move__location_dest_id","stock_location" as "stock_move__location_id","stock_move"
WHERE ("stock_move"."location_dest_id"="stock_move__location_dest_id"."id" AND "stock_move"."location_id"="stock_move__location_id"."id") AND (((("stock_move"."state" in ('waiting','confirmed','assigned','partially_available')) AND ("stock_move"."product_id" in (62,41,40,15,64,65,51,16,17,18,19,20,21,23,24,55,58,60,54,56,57,61,53,52,12,13,14,25,30,26,48,39,36,49,31,34,45,42,5,8,29,43,33,38,46,32,6,27,35,28,44,37,7,50,66,59,67))) AND ("stock_move__location_dest_id"."parent_path"::text like '1/7/%')) AND (NOT (("stock_move__location_id"."parent_path"::text like '1/7/%')))) AND ("stock_move"."location_dest_id"="stock_move__location_dest_id"."id") AND (("stock_move"."company_id" in (1)) OR "stock_move__location_dest_id"."company_id" IS NULL )
GROUP BY "stock_move"."product_id"
ORDER BY "id"
You need to carefully study the _compute_quantities method code to know how Odoo calculates these values.
Edit:
Concider the following statement:
self.env['stock.warehouse'].search([('branch_id', '=', self.env.user.branch_id.id)],
order="id desc")
To turn the above statement to an SQL query you need to know what search method does
For example, Odoo will call _where_calc to computes the WHERE clause needed to implement the domain and automatically add the ('active', '=', 1) to the domain.
Example:
query_str
SELECT "stock_warehouse".id FROM "stock_warehouse" WHERE (("stock_warehouse"."active" = %s) AND ("stock_warehouse"."branch_id" = %s)) AND ("stock_warehouse"."company_id" in (%s)) ORDER BY "stock_warehouse"."id" DESC
args
[True, 1, 1]
The final query:
SELECT "stock_warehouse".id FROM "stock_warehouse" WHERE (("stock_warehouse"."active" = true) AND ("stock_warehouse"."branch_id" = 1)) AND ("stock_warehouse"."company_id" in (1)) ORDER BY "stock_warehouse"."id" DESC

Can I use parameter insertion to specify column names for MySQL queries?

I want to know if it is possible to use parameter insertion for column names into MySQL queries using Python.
Consider the following two queries, both of which are passed to MySQLCursor.execute(). The first:
query = (
'SELECT username, COUNT(*)'
'FROM `entry`'
'GROUP BY username;'
)
cursor.execute(query)
And the second:
query = (
'SELECT %s, COUNT(*)'
'FROM `entry`'
'GROUP BY %s;'
)
data = ('username', 'username')
cursor.execute(query, data)
The first of these returns the results I expect (a count of each how many times a distinct value appears in the username column) and the second returns unexpected results, specifically [(u'username', n)] where n is the total number of rows in the database.
The problem in the second query is that the parameters are interpreted as a string by the query. Is there a way to insert them such that they can be interpreted as a non-string? I want to do this in a way that is safe from Injection attacks.
It is not recommended to use the 2nd syntax, the 1st one is ok and safe.

How to generate a unique random number when insert in MySQL?

I have a database for articles and may want to generate a unique random integer for each articles so that they can be visited through URL like https://blablabla.com/articles/8373734 etc.
I could achieve that in python backend, but how do we achieve this in MySQL sentences?
For example, a new article was done, and inserted into database:
INSERT into article_table (title, date, url_id) VALUES ('asdf', '11/11/1111', 8373734)
the url_id here is the unique random integer (1000~10000000) that automatically generated.
I believe The primary key ID and auto-increasment are good way to solve this. But my question is:
In practical scenario, do they (companies) literally use primary ID or auto-increasment? This may expose how piece of data you (ever) have in database. Take this https://www.zhihu.com/question/41490222 for example, I tried hundreds of number around 41490222, all returns 404 not found. it seems that the number are recorded very sparsely, not very possible achieved by auto-increasement.
Are there any efficient way to generate such random number without checking duplication for every loop?
Use mysql function RAND()
-------------------------
select FLOOR(RAND() * 999999)
You can use UUID(), or if it has to be numeric UUID_SHORT() for that.
albeit my sql skills are a bit rusty, I think you might want to create a function using the RAND function.
CREATE PROCEDURE GetRandomValue()
BEGIN
DECLARE newUrlId INT DEFAULT 0;
WHILE (
newUrlId = 0
OR IF EXISTS(SELECT 1 FROM yourTable WHERE url_id = newUrlId)
)
DO
SET newUrlId = SELECT FLOOR(RAND() * 999999)
END WHILE
RETURN newUrlId
END
Then again, why creating such a fuss while you could use other ways to create "bigger random numbers"
for example:
function createBiggerNumber(id) {
return (id * constants.MySecretMultiplyValue) + constants.MySecretAddedValue;
}
function extractIdFromBiggerNumber(number) {
return (number - constants.MySecretAddedValue) / constants.MySecretMultiplyValue
}
the logic is combine with their primary key | id , so we dont need re check if the data is exist or not.
DELIMITER $$
DROP TRIGGER IF EXISTS `auto_number`$$
CREATE TRIGGER `auto_number` BEFORE INSERT on users
FOR EACH ROW BEGIN
SET new.auto_number = CONCAT(new.id, LEFT(UUID(), 8));
END$$
DELIMITER ;
https://gist.github.com/yogithesymbian/698b27138a5ba89d2a32e3fc7ddd3cfb

Categories

Resources