Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment faire en sorte que SQLAlchemy insère correctement des points de suspension Unicode dans une table mySQL ?

Le message d'erreur

UnicodeEncodeError: 'latin-1' codec can't encode character u'\u2026' 
in position 35: ordinal not in range(256)

semble indiquer qu'un certain code de langage Python essaie de convertir le caractère \u2026 dans une chaîne Latin-1 (ISO8859-1), et il échoue. Pas surprenant, ce caractère est U+2026 HORIZONTAL ELLIPSIS , qui n'a pas de caractère équivalent unique dans ISO8859-1.

Vous avez résolu le problème en ajoutant la requête ?charset=utf8 dans votre appel de connexion SQLAlchemy :

import sqlalchemy
from sqlalchemy import create_engine, MetaData, Table

db = create_engine('mysql://user:[email protected]/db?charset=utf8')

La section Urls de bases de données de la documentation de SQLAlchemy nous indique qu'une URL commençant par mysql indique un dialecte MySQL, en utilisant le mysql-python chauffeur.

La section suivante, DBAPI personnalisé arguments connect() , nous indique que les arguments de la requête sont transmis à la DBAPI sous-jacente.

Alors, qu'est-ce que mysql-python driver make d'un paramètre {charset: 'utf8'} ? Section Fonctions et attributs de leur documentation parle du charset attribut "...Si présent, le jeu de caractères de connexion sera remplacé par ce jeu de caractères, s'ils ne sont pas égaux."

Pour savoir ce que signifie le jeu de caractères de connexion, nous nous tournons vers 10.1.4. Jeux de caractères de connexion et classements du manuel de référence MySQL 5.6. Pour faire court, MySQL peut interpréter les requêtes entrantes comme un encodage différent du jeu de caractères de la base de données et différent de l'encodage des résultats de requête renvoyés.

Étant donné que le message d'erreur que vous avez signalé ressemble à un message d'erreur Python plutôt qu'à un message d'erreur SQL, je suppose que quelque chose dans SQLAlchemy ou mysql-python tente de convertir la requête en un codage de connexion par défaut de latin-1 avant de l'envoyer. C'est ce qui déclenche l'erreur. Cependant, la chaîne de requête ?charset=utf8 dans votre connect() call modifie l'encodage de la connexion et l'U+2026 HORIZONTAL ELLIPSIS est capable de passer.

Mise à jour : vous demandez également :"si je supprime l'option charset, puis que j'encode la description à l'aide de .encode('cp1252'), tout ira bien. Comment une ellipse peut-elle passer avec cp1252 mais pas unicode ?"

Le encodage cp1252 a un caractère d'ellipse horizontal à la valeur d'octet \x85 . Ainsi il est possible d'encoder une chaîne Unicode contenant U+2026 HORIZONTAL ELLIPSIS dans cp1252 sans erreur.

Rappelez-vous également qu'en Python, les chaînes Unicode et les chaînes d'octets sont deux types de données différents. Il est raisonnable de supposer que MySQLdb pourrait avoir pour politique d'envoyer uniquement des chaînes d'octets via une connexion SQL. Ainsi, il encoderait une requête reçue sous forme de chaîne Unicode dans une chaîne d'octets, mais laisserait seule une requête reçue sous forme de chaîne d'octets. (Ce sont des spéculations, je n'ai pas regardé le code source.)

Dans le traceback que vous avez posté, les deux dernières lignes (les plus proches de l'endroit où l'erreur s'est produite) affichent les noms de méthode literal , suivi de unicode_literal . Cela tend à soutenir la théorie selon laquelle MySQLdb encode la requête qu'il reçoit sous forme de chaîne Unicode dans une chaîne d'octets.

Lorsque vous encodez vous-même la chaîne de requête, vous contournez la partie de MySQLdb qui effectue cet encodage différemment. Notez cependant que si vous encodez la chaîne de requête différemment de ce que le jeu de caractères de connexion MySQL appelle, vous aurez alors une incompatibilité d'encodage et votre texte sera probablement mal stocké.