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?

+3


source to share


1 answer


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.

+2


source







All Articles