2

ユーザーが単語やフレーズを入力し、指定されたモデルのすべてのインスタンスをフェッチできるようにするDjangoの関数を作成しました。このインスタンスでは、指定されたフィールドの範囲全体ですべての単語が任意の順序で表示されます。Django Qオブジェクトを使用して正しいクエリを作成する際に問題が発生するため、objects.rawメソッドを使用してカスタムSQLを作成することを選択しました。

def fuzzy_search(objmodel,columns,q='',limit=None,offset=0):
    """
        TEMPORARY PATCH version for fuzzy_search, gets around a native Django bug.
    """ 
    if len(q)<3:
        return [] #No results until you reach 3 chars
    words = q.strip().split(" ")
    #Get model table name:
    print "All results: %s" % objmodel.objects.all() 
    db_table = objmodel._meta.db_table
    print("DB_table = %s" % db_table)
    #Construct fields into list of kwarguments!
    sql = "SELECT * FROM %s" % (db_table,)
    userparams = []
    whereands = []
    #Construct the SQL as 
    for word in words:
        if len(word)<2:
            continue #Ignore computationally expensive single char strings
        whereors = []
        for col in columns:
            whereors.append('`%s`.`%s` LIKE "%s##*##"' % (db_table,col,"##P##"))    #STARTSWITH word... The third param will be converted via injection proof means 
            whereors.append('`%s`.`%s` LIKE "(%s##*##"' % (db_table,col,"##P##")) #STARTSWITH (word... The third param will be converted via injection proof means
            whereors.append('`%s`.`%s` LIKE "##*## %s##*##"' % (db_table,col,"##P##"))  #CONTAINS word... The third param will be converted via injection proof means 
            whereors.append('`%s`.`%s` LIKE "##*## (%s##*##"' % (db_table,col,"##P##")) #CONTAINS (word... The third param will be converted via injection proof means
        if whereors not in boolfalse:
            whereorstr= "(" + " OR ".join(whereors) + ")"
            for i in range(0,len(whereors)):
                userparams.append(word) #Need to match the number of supplied params to the number of clauses
            whereands.append(whereorstr)    #Build into an SQL string
        else:
            continue
    #Build the final sql:
    results = []
    if whereands not in boolfalse:
        sql+= " WHERE " + " AND ".join(whereands)
        sql = sql.replace("##P##","%s") #Necessary to get around %s persistence restriction
        sql = sql.replace("##*##","%%") #Makes everything a bit clearer!
        #For big datasets, it is more efficient to apply LIMITS and OFFSETS at SQL level:
        if limit:
            sql+= " LIMIT %s" % int(limit)  #This is injection proof as only ints are accepted
        if offset:
            sql+= " OFFSET %s" % int(offset)    #This is injection proof as only ints are accepted  
        #Perform the raw query, but with params carefully passed in via SQLi escaped method:
        ### objects.raw method ###
        resultsqset = objmodel.objects.raw(sql,userparams)
        print("Fuzzy SQL: \n%s\n" % resultsqset.query.__str__())    #View SQL
        results = list(resultsqset)
        print("Results: %s" % results)
        ### direct cursor method ###
        #cursor = connection.cursor()
        #cursor.execute(sql,userparams)
        #results = dictfetchall(cursor) #Ensures the results are fetched as a dict of fieldname => value
        return results
    return results

この関数は次のように呼び出されます。

from modules.documents.models import Data_icd10_en
results = fuzzy_search(Data_icd10_en,["code","long_label"],"diab mel",30)

モデルは次のとおりです。

class Data_icd10_en(models.Model):
    code = models.CharField(max_length=10)
    short_label = models.CharField(max_length=100)
    long_label = models.CharField(max_length=100)

関数を呼び出すと、コンソールに実際のSQLダンプが表示されます。

print("Fuzzy SQL: \n%s\n" % resultsqset.query.__str__())    #View SQL
Fuzzy SQL: 
<RawQuery: u'SELECT * FROM documents_data_icd10_en WHERE (`documents_data_icd10_en`.`code` LIKE "diabetes%" OR `documents_data_icd10_en`.`code` LIKE "(diabetes%" OR `documents_data_icd10_en`.`code` LIKE "% diabetes%" OR `documents_data_icd10_en`.`code` LIKE "% (diabetes%" OR `documents_data_icd10_en`.`long_label` LIKE "diabetes%" OR `documents_data_icd10_en`.`long_label` LIKE "(diabetes%" OR `documents_data_icd10_en`.`long_label` LIKE "% diabetes%" OR `documents_data_icd10_en`.`long_label` LIKE "% (diabetes%") AND (`documents_data_icd10_en`.`code` LIKE "mell%" OR `documents_data_icd10_en`.`code` LIKE "(mell%" OR `documents_data_icd10_en`.`code` LIKE "% mell%" OR `documents_data_icd10_en`.`code` LIKE "% (mell%" OR `documents_data_icd10_en`.`long_label` LIKE "mell%" OR `documents_data_icd10_en`.`long_label` LIKE "(mell%" OR `documents_data_icd10_en`.`long_label` LIKE "% mell%" OR `documents_data_icd10_en`.`long_label` LIKE "% (mell%") LIMIT 30'>

このSQLをコピーしてデータベースバックエンド(MySQL)に直接貼り付けると、正しい結果が返されます(「糖尿病」と診断された30行のバリアント)。ただし、Python関数自体は何も返しません(結果は単なる空のリストです)。print(resultsqset)を試しましたが、これにより、このRawQuerySetが明らかになります。

Results: <RawQuerySet: u'SELECT * FROM documents_data_icd10_en WHERE (`documents_data_icd10_en`.`code` LIKE "diab%" OR `documents_data_icd10_en`.`code` LIKE "(diab%" OR `documents_data_icd10_en`.`code` LIKE "% diab%" OR `documents_data_icd10_en`.`code` LIKE "% (diab%" OR `documents_data_icd10_en`.`long_label` LIKE "diab%" OR `documents_data_icd10_en`.`long_label` LIKE "(diab%" OR `documents_data_icd10_en`.`long_label` LIKE "% diab%" OR `documents_data_icd10_en`.`long_label` LIKE "% (diab%") AND (`documents_data_icd10_en`.`code` LIKE "mel%" OR `documents_data_icd10_en`.`code` LIKE "(mel%" OR `documents_data_icd10_en`.`code` LIKE "% mel%" OR `documents_data_icd10_en`.`code` LIKE "% (mel%" OR `documents_data_icd10_en`.`long_label` LIKE "mel%" OR `documents_data_icd10_en`.`long_label` LIKE "(mel%" OR `documents_data_icd10_en`.`long_label` LIKE "% mel%" OR `documents_data_icd10_en`.`long_label` LIKE "% (mel%") LIMIT 30'>

また、rawquerysetをリストにキャストし、手動で繰り返して行を印刷してみました。どちらも何も生成しません。

最後に、モデルオブジェクトが実際に私が思っているものであることを確認するために、試してみるとprint "All results: %s" % objmodel.objects.all()40程度のリストが表示<Data_icd10_en: Data_icd10_en object>されます。これは、私が期待するものです。

それで、ここで何が起こっているのですか?modelname.objects.raw()を介して実行したときにコードが何も生成しないのに、データベースシェルでまったく同じSQLを実行したときに結果をフェッチし、同じモデル名のすべての行がフェッチされたときに結果を正しくフェッチしているのはなぜですか?その機能?

----編集----テストにより、はい、Djangoアプリとシェルを介して同じデータベースに実際にアクセスしていることが確認されました。また、単純な生のクエリがすべて1行で機能します。

4

2 に答える 2

3

さらに調査し、MySQLロギングとDjango開発者への電子メールをオンにした後、私のコードに問題がないことがわかりました。

むしろ、ネイティブでマイナーなバグがありQuerySet.query.__str__()ます。これが実際のSQLコンテンツをコンソールに出力するとき、ユーザー指定のパラメーターをカプセル化する引用符を出力できません。

したがって、コンソールが次のように述べている場合:

<RawQuery: u'SELECT * FROM documents_data_icd10_en WHERE (`documents_data_icd10_en`.`code` LIKE "(diabetes%"...

実際に実行されるのは次のとおりです。

"<RawQuery: u'SELECT * FROM documents_data_icd10_en WHERE (`documents_data_icd10_en`.`code` LIKE "("diabetes%""...

...これは無効です。

話の士気:何がわかるか信じないQuerySet.query.__str__()でください。また、ユーザーが指定した文字列を引用符で囲んではいけませんModel.objects.raw(sql,PARAMS)。これはあなたのために行われます。

于 2013-03-26T18:19:26.660 に答える
0

#これは、サブクエリなどの任意のタイプの生のクエリを実行します。\

 query ="""this will be the raw query"""\ 
 X=cursor.execute(query) \
 answers = cursor.fetchall()\
 print ("answers---",answers)\
于 2021-06-12T20:22:34.337 に答える