Django raw () request with similar code "tuple" has no "id" attribute
I am making multiple queries and then removing duplicates by rank for a graveyard burial application. I am trying to use PostgreSQL SIMILAR TO in my latest search ranking, but every time I use it, I get that the "tuple" object has no "id" attribute.
Here are two queries:
query1 = """
SELECT "burials_burial"."id", "burials_burial"."name_id",
"burials_burial"."grave_id",
"burials_burial"."interment_date",
"burials_burial"."user_id",
"cemeteries_grave"."search1_id" AS "cemetery_id",
'2' AS "rank"
FROM "burials_burial"
INNER JOIN "names_name"
ON ( "burials_burial"."name_id" = "names_name"."id" )
INNER JOIN "cemeteries_grave"
ON ( "burials_burial"."grave_id" =
"cemeteries_grave"."id")
WHERE (
(LOWER("names_name"."first_name") IN {0!s})
OR (LOWER("names_name"."middle_name") IN {0!s})
OR (LOWER("names_name"."last_name") IN {0!s})
)
"""
query2 = """
SELECT "burials_burial"."id", "burials_burial"."name_id",
"burials_burial"."grave_id",
"burials_burial"."interment_date",
"burials_burial"."user_id",
"cemeteries_grave"."search1_id" AS "cemetery_id",
'1' AS "rank"
FROM "burials_burial"
INNER JOIN "names_name"
ON ( "burials_burial"."name_id" = "names_name"."id" )
INNER JOIN "cemeteries_grave"
ON ( "burials_burial"."grave_id" =
"cemeteries_grave"."id")
WHERE (
(LOWER("names_name"."first_name") SIMILAR TO {0!s})
OR (LOWER("names_name"."middle_name") SIMILAR TO {0!s})
OR (LOWER("names_name"."last_name") SIMILAR TO {0!s})
)
"""
When I call the queries I provide in the relevant search parameters here:
names1 = Burial.objects.raw(query1.format(terms))
names2 = Burial.objects.raw(query2.format(terms.replace('\'', '').replace(', ', '|').replace('(', '\'%(').replace(')', ')%\'')))
Finally, I'm just trying to view the names and do something with them:
for names in (names1, names2):
for n in names:
<do something here>
Now that things are strange. For names1 I am getting my results and I can skip them. For names 2, I am getting the error noted above. Why?
I have looked at the output of the raw queries to ensure that what I am shipping to PostgreSQL is indeed. Executing queries as python shell output gives me the results as I expect:
IN: print names1.raw_query
OUT: SELECT "burials_burial"."id", "burials_burial"."name_id",
"burials_burial"."grave_id",
"burials_burial"."interment_date",
"burials_burial"."user_id",
"cemeteries_grave"."search1_id" AS "cemetery_id",
'25' AS "rank"
FROM "burials_burial"
INNER JOIN "names_name"
ON ( "burials_burial"."name_id" = "names_name"."id" )
INNER JOIN "cemeteries_grave"
ON ( "burials_burial"."grave_id" =
"cemeteries_grave"."id")
WHERE (
LOWER("names_name"."first_name") IN ('william', 'david', 'smith')
AND LOWER("names_name"."middle_name") IN ('william', 'david', 'smith')
)
IN: print names2.raw_query
OUT: SELECT "burials_burial"."id", "burials_burial"."name_id",
"burials_burial"."grave_id",
"burials_burial"."interment_date",
"burials_burial"."user_id",
"cemeteries_grave"."search1_id" AS "cemetery_id",
'1' AS "rank"
FROM "burials_burial"
INNER JOIN "names_name"
ON ( "burials_burial"."name_id" = "names_name"."id" )
INNER JOIN "cemeteries_grave"
ON ( "burials_burial"."grave_id" =
"cemeteries_grave"."id")
WHERE (
(LOWER("names_name"."first_name") SIMILAR TO '%(william|david|smith)%')
OR (LOWER("names_name"."middle_name") SIMILAR TO '%(william|david|smith)%')
OR (LOWER("names_name"."last_name") SIMILAR TO '%(william|david|smith)%')
)
I have also used a cursor directly with the query and return the results, although not as objects, so this is not ideal. But this shows that python / django can handle the request:
IN: from django.db import connections
IN: cursor = connection.cursor()
IN: cursor.execute(names2.raw_query)
IN: results = cursor.fetchall()
IN: len(results)
OUT: 1
So again, my question is. Why does the seemingly valid request give me a "tuple" object, doesn't have an id attribute error?
source to share
The problem seems to be that I have a% sign in SQL. PostgreSQL Like To is a cross between LIKE and POSIX regular expressions. It uses% as a wildcard and will have a wildcard like the LIKE statement does. I found that when I remove the% I don't get the described error, but I also don't get the actual results I'm looking for.
Python uses% to format strings, as described here: https://docs.python.org/2/library/stdtypes.html#string-formatting . According to another stack overflow, the way to overcome this is to use %%.
So there are two jobs. The first is to use the PostgreSQL operator ~, which only uses the POSIX regular expression. This allows me to take out the% template. As a result, the request is as follows:
query6 = """
SELECT "burials_burial"."id", "burials_burial"."name_id",
"burials_burial"."grave_id",
"burials_burial"."interment_date",
"burials_burial"."user_id",
"cemeteries_grave"."search1_id" AS "cemetery_id",
'1' AS "rank"
FROM "burials_burial"
INNER JOIN "names_name"
ON ( "burials_burial"."name_id" = "names_name"."id" )
INNER JOIN "cemeteries_grave"
ON ( "burials_burial"."grave_id" =
"cemeteries_grave"."id")
WHERE (
(LOWER("names_name"."first_name") ~ {0!s})
OR (LOWER("names_name"."middle_name") ~ {0!s})
OR (LOWER("names_name"."last_name") ~ {0!s})
)
"""
names6 = Burial.objects.raw(query6.format(terms.replace('\'', '')\
.replace(', ', '|')\
.replace('(', '\'(')\
.replace(')', ')\'')))
The other should have a replacement input with two% characters, bypassing the string replacement like so:
names2 = Burial.objects.raw(query2.format(terms.replace('\'', '').replace(', ', '|').replace('(', '\'%%(').replace(')', ')%%\'')))
Thanks a lot for the comments and the time for that.
source to share