Django में यूनिकोड स्ट्रिंग को सहेजते समय MySQL "गलत स्ट्रिंग मान" त्रुटि


158

मुझे अजीब त्रुटि संदेश मिला जब Django के Cort_user मॉडल के लिए first_name, last_name को बचाने का प्रयास किया गया।

असफल उदाहरण

user = User.object.create_user(username, email, password)
user.first_name = u'Rytis'
user.last_name = u'Slatkevičius'
user.save()
>>> Incorrect string value: '\xC4\x8Dius' for column 'last_name' at row 104

user.first_name = u'Валерий'
user.last_name = u'Богданов'
user.save()
>>> Incorrect string value: '\xD0\x92\xD0\xB0\xD0\xBB...' for column 'first_name' at row 104

user.first_name = u'Krzysztof'
user.last_name = u'Szukiełojć'
user.save()
>>> Incorrect string value: '\xC5\x82oj\xC4\x87' for column 'last_name' at row 104

सफल उदाहरण

user.first_name = u'Marcin'
user.last_name = u'Król'
user.save()
>>> SUCCEED

MySQL सेटिंग्स

mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       | 
| character_set_connection | utf8                       | 
| character_set_database   | utf8                       | 
| character_set_filesystem | binary                     | 
| character_set_results    | utf8                       | 
| character_set_server     | utf8                       | 
| character_set_system     | utf8                       | 
| character_sets_dir       | /usr/share/mysql/charsets/ | 
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

टेबल चारसेट और कॉलेशन

सारणी__erer utf8_general_ci कोलेशन के साथ utf-8 charset है।

UPDATE कमांड के परिणाम

जब UPDATE कमांड का उपयोग करके इसे ऊपर से मान_user तालिका में अद्यतन करते समय कोई त्रुटि नहीं हुई।

mysql> update auth_user set last_name='Slatkevičiusa' where id=1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select last_name from auth_user where id=100;
+---------------+
| last_name     |
+---------------+
| Slatkevi?iusa | 
+---------------+
1 row in set (0.00 sec)

PostgreSQL

ऊपर सूचीबद्ध विफल मानों को पोस्टग्रेएसक्यूएल टेबल में अद्यतन किया जा सकता है जब मैंने डेटाबेस बैकएंड को जोंगो में स्विच किया था। यह बहुत अजीब है।

mysql> SHOW CHARACTER SET;
+----------+-----------------------------+---------------------+--------+
| Charset  | Description                 | Default collation   | Maxlen |
+----------+-----------------------------+---------------------+--------+
...
| utf8     | UTF-8 Unicode               | utf8_general_ci     |      3 | 
...

लेकिन http://www.postgresql.org/docs/8.1/interactive/multibyte.html से , मुझे निम्नलिखित मिला:

Name Bytes/Char
UTF8 1-4

क्या इसका मतलब है कि यूनीकोड ​​चार में अधिकतम 4 बाइट्स की पोस्टग्रेसीक्यूएल है, लेकिन माईएसक्यूएल में 3 बाइट्स जो कि त्रुटि के कारण हुए?


2
यह एक MySQL समस्या है, न कि Django: stackoverflow.com/questions/1168036/…
वानुअन

जवाबों:


140

इनमें से किसी भी उत्तर ने मेरे लिए समस्या हल नहीं की। मूल कारण है:

आप utf-8 वर्ण सेट के साथ MySQL में 4-बाइट वर्ण संग्रहीत नहीं कर सकते।

MySQL में utf-8 अक्षरों पर 3 बाइट की सीमा है (हाँ, यह निराला है, अच्छी तरह से एक Djer डेवलपर साइट द्वारा अभिव्यक्त किया गया है )

इसे हल करने के लिए आपको निम्न की आवश्यकता है:

  1. Utf8mb4 वर्ण सेट का उपयोग करने के लिए अपने MySQL डेटाबेस, टेबल और कॉलम को बदलें (केवल MySQL 5.5 के बाद से उपलब्ध)
  2. नीचे के रूप में अपनी Django सेटिंग फ़ाइल में चारसेट निर्दिष्ट करें:

settings.py

DATABASES = {
    'default': {
        'ENGINE':'django.db.backends.mysql',
        ...
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}

नोट: जब आप अपने डेटाबेस को फिर से बनाते हैं तो आप ' निर्दिष्ट कुंजी बहुत लंबी ' समस्या में चल सकते हैं।

सबसे संभावित कारण एक है CharFieldजिसकी अधिकतम संख्या 255 है और उस पर किसी प्रकार का सूचकांक (जैसे अद्वितीय)। क्योंकि utf8mb4 utf-8 की तुलना में 33% अधिक स्थान का उपयोग करता है, इसलिए आपको इन क्षेत्रों को 33% छोटा बनाने की आवश्यकता होगी।

इस स्थिति में, max_length को 255 से 191 में बदलें।

वैकल्पिक रूप से आप इस प्रतिबंध को हटाने के लिए अपने MySQL कॉन्फ़िगरेशन को संपादित कर सकते हैं लेकिन कुछ django हैकरी के बिना नहीं

अद्यतन: मैं बस इस मुद्दे में फिर से भाग गया और PostgreSQL पर स्विच करना समाप्त कर दिया क्योंकि मैं अपने VARCHAR191 वर्णों को कम करने में असमर्थ था ।


13
इस उत्तर के लिए रास्ता, रास्ता, रास्ता अधिक आवश्यक है। धन्यवाद! असली समस्या यह है कि आपका आवेदन वर्षों तक ठीक चल सकता है जब तक कि कोई 4byte वर्ण में प्रवेश करने की कोशिश न करे।
माइकल बायलस्ट्रा

2
यह बिल्कुल सही जवाब है। विकल्प सेटिंग महत्वपूर्ण है django decode इमोजी वर्ण बनाने और उन्हें MySQL में संग्रहीत करने के लिए। सिर्फ SQL कमांड के माध्यम से utf8mb4 के लिए mysql charset बदलना काफी नहीं है!
एक्सियन

Utf8mb4 के लिए संपूर्ण तालिका के वर्ण सेट को अपडेट करने की कोई आवश्यकता नहीं है। बस आवश्यक कॉलम के चरित्र सेट को अपडेट करें। इसके अलावा 'charset': 'utf8mb4'Django सेटिंग में विकल्प महत्वपूर्ण है, जैसा कि @Xerion ने कहा। अंत में, सूचकांक समस्या एक गड़बड़ है। स्तंभ पर अनुक्रमणिका निकालें, या इसकी लंबाई 191 से अधिक न करें, या TextFieldइसके बजाय का उपयोग करें !
रॉकलाईट

2
मैं अपने प्यार लिंक इस उद्धरण के लिए: यह उद्देश्यपूर्ण और अपरिवर्तनीय जा रहा है मस्तिष्क क्षतिग्रस्त MySQL के सिर्फ एक और मामला है। :)
Qback

120

मुझे भी यही समस्या थी और स्तंभ के चरित्र सेट को बदलकर इसे हल किया गया। हालांकि आपके डेटाबेस में डिफ़ॉल्ट चरित्र सेट है, utf-8मुझे लगता है कि डेटाबेस कॉलम के लिए MySQL में एक अलग वर्ण सेट होना संभव है। यहाँ SQL QUERY का उपयोग किया गया है:

    ALTER TABLE database.table MODIFY COLUMN col VARCHAR(255)
    CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;

14
ओह, मैं सब कुछ पर सभी वर्ण सेट बदल रहा सकता है जब तक मैं वास्तव में फिर से पढ़ इस उत्तर: कॉलम उनकी हो सकता है खुद वर्ण सेट, टेबल और डेटाबेस के स्वतंत्र। वह पागल है और बिल्कुल मेरी समस्या भी थी।
मार्कपेक

1
इसने मेरे लिए भी काम किया, एक टेक्स्टफिल्ड मॉडल में डिफॉल्ट के साथ mysql का उपयोग करते हुए।
madprops

इससे मेरी समस्या हल हो गई। केवल मैंने जो बदलाव किया वह utf8 / utf8_general_ci के बजाय utf8mb4 और utf8mb4_general_ci का उपयोग करना था।
मीकल प्रेज़्यूसिहा

70

यदि आपको यहाँ यह समस्या है तो अपने mysql डेटाबेस के सभी कॉलम को स्वचालित रूप से बदलने के लिए एक पायथन स्क्रिप्ट है।

#! /usr/bin/env python
import MySQLdb

host = "localhost"
passwd = "passwd"
user = "youruser"
dbname = "yourdbname"

db = MySQLdb.connect(host=host, user=user, passwd=passwd, db=dbname)
cursor = db.cursor()

cursor.execute("ALTER DATABASE `%s` CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'" % dbname)

sql = "SELECT DISTINCT(table_name) FROM information_schema.columns WHERE table_schema = '%s'" % dbname
cursor.execute(sql)

results = cursor.fetchall()
for row in results:
  sql = "ALTER TABLE `%s` convert to character set DEFAULT COLLATE DEFAULT" % (row[0])
  cursor.execute(sql)
db.close()

4
इस समाधान ने एक django ऐप के साथ मेरे सभी मुद्दों को हल किया जो फ़ाइल और निर्देशिका पथों को संग्रहीत कर रहा था। अपने django डेटाबेस के रूप में dbname में टॉस करें और इसे चलने दें। एक जादू की तरह काम किया!
क्रिस

1
जब तक मैंने db.commit()पहले जोड़ा था तब तक यह कोड मेरे लिए काम नहीं करता था db.close()
मार्क एर्दमन

1
क्या यह समाधान @markpasc टिप्पणी में चर्चा किए गए मुद्दे से बचता है: '... 4-बाइट UTF-8 वर्ण जैसे कि MySQL 5.1 में इमोजी जैसे 3-बाइट utf8 वर्ण सेट'
कैटशॉज़

जब मैं एक रिकॉर्ड गर्त django व्यवस्थापक को हटा रहा था, तो समाधान ने मेरी मदद की, मुझे ओ संपादन बनाते समय कोई समस्या नहीं हुई ... अजीब! मैं सीधे db में भी डिलीट करने में सक्षम था
जेवियर विएरा

क्या मुझे मॉडल बदलने पर हर बार ऐसा करना चाहिए?
वानुआन

25

यदि यह एक नई परियोजना है, तो मैं सिर्फ डेटाबेस को छोड़ दूंगा, और एक उचित चारसेट के साथ एक नया निर्माण करूंगा:

CREATE DATABASE <dbname> CHARACTER SET utf8;

नमस्ते कृपया इस प्रश्न को जांचने में मदद करें stackoverflow.com/questions/46348817/…
King

मेरे मामले में, हमारे डीबी को डॉकर्स द्वारा बनाया गया है ताकि मैं ठीक कर - --character-set-server=utf8
सकूं

1
इतना सरल है। धन्यवाद @Vanuan
Enku

यदि यह एक नई परियोजना नहीं है, तो हमें db से बैकअप मिलता है, इसे छोड़ें और utf8 charset के साथ फिर से बनाएँ और फिर बैकअप को पुनर्स्थापित करें। मैंने इसे अपने प्रोजेक्ट में किया जो कि नया नहीं था ...
मोहम्मद रज़ा

8

मैंने उपरोक्त त्रुटियों से बचने के लिए सिर्फ एक तरीका निकाला है।

डेटाबेस में सहेजें

user.first_name = u'Rytis'.encode('unicode_escape')
user.last_name = u'Slatkevičius'.encode('unicode_escape')
user.save()
>>> SUCCEED

print user.last_name
>>> Slatkevi\u010dius
print user.last_name.decode('unicode_escape')
>>> Slatkevičius

क्या यह एक ऐसी विधि है जो स्ट्रिंग को MySQL टेबल में सहेजती है और डिस्प्ले के लिए टेम्प्लेट करने से पहले उसे डिकोड करती है?


12
मुझे एक समान समस्या हो रही है, लेकिन मैं सहमत नहीं हूं कि यह एक वैध समाधान है। जब आप .encode('unicode_escape')वास्तव में डेटाबेस में यूनिकोड वर्ण संग्रहीत नहीं कर रहे हैं। आप सभी ग्राहकों को उपयोग करने से पहले अनएकोड करने के लिए मजबूर कर रहे हैं, जिसका अर्थ है कि यह django.admin या अन्य सभी प्रकार की चीजों के साथ ठीक से काम नहीं करेगा।
muudscope

3
हालांकि यह वर्णों के बजाय भागने के कोड को स्टोर करने के लिए अरुचिकर लगता है, यह संभवतः कुछ तरीकों में से एक है जो 4-बाइट UTF-8 वर्णों को बचाने के लिए है जैसे कि MySQL 5.1 के 3-बाइट utf8वर्ण सेट में इमोजी ।
मार्क

2
एक एन्कोडिंग है जिसे utf8mb4बेसिक बहुभाषी प्लेन से अधिक संग्रहीत करने की अनुमति मिलती है। मुझे पता है, आपको लगता है कि "UTF8" यूनिकोड को पूरी तरह से स्टोर करने के लिए आवश्यक है। खैर, विद्या जानते हैं, यह नहीं है। Dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
Mihai Danila


यह एक व्यावहारिक समाधान है, लेकिन मैं इसे (@ muudscope द्वारा वकालत के रूप में) भी उपयोग करने की अनुशंसा नहीं करता हूं। मैं अभी भी स्टोर नहीं कर सकता, उदाहरण के लिए, mysql डेटाबेस के लिए इमोजी। क्या किसी ने इसे पूरा किया है?
मार्सेलो सर्दिलीच

6

आप अपने टेक्स्ट फ़ील्ड के कोलाज को UTF8_general_ci में बदल सकते हैं और समस्या हल हो जाएगी।

ध्यान दें, यह Django में नहीं किया जा सकता है।


1

आप यूनिकोड स्ट्रिंग्स को बचाने की कोशिश नहीं कर रहे हैं, आप यूटीएफ -8 एन्कोडिंग में बाईटस्टर को बचाने की कोशिश कर रहे हैं। उन्हें वास्तविक यूनिकोड स्ट्रिंग शाब्दिक बनाएं:

user.last_name = u'Slatkevičius'

या (जब आपके पास स्ट्रिंग शाब्दिक नहीं हैं) तो utf-8 एन्कोडिंग का उपयोग करके उन्हें डीकोड करें:

user.last_name = lastname.decode('utf-8')

@ थोमस, मैंने जैसा आपने कहा वैसा ही प्रयास किया लेकिन यह अभी भी वही त्रुटियां बढ़ा रहा है।
जैक

0

बस अपनी टेबल बदल दें, किसी भी चीज की जरूरत नहीं है। बस इस क्वेरी को डेटाबेस पर चलाएँ। चार table_nameसेट बदलने के लिए वैकल्पिक टेबल utf8 में परिवर्तित करें

यह निश्चित रूप से काम करेगा।


0

@माडप्रोप्स उत्तर में सुधार - django प्रबंधन कमांड के रूप में समाधान:

import MySQLdb
from django.conf import settings

from django.core.management.base import BaseCommand


class Command(BaseCommand):

    def handle(self, *args, **options):
        host = settings.DATABASES['default']['HOST']
        password = settings.DATABASES['default']['PASSWORD']
        user = settings.DATABASES['default']['USER']
        dbname = settings.DATABASES['default']['NAME']

        db = MySQLdb.connect(host=host, user=user, passwd=password, db=dbname)
        cursor = db.cursor()

        cursor.execute("ALTER DATABASE `%s` CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'" % dbname)

        sql = "SELECT DISTINCT(table_name) FROM information_schema.columns WHERE table_schema = '%s'" % dbname
        cursor.execute(sql)

        results = cursor.fetchall()
        for row in results:
            print(f'Changing table "{row[0]}"...')
            sql = "ALTER TABLE `%s` convert to character set DEFAULT COLLATE DEFAULT" % (row[0])
            cursor.execute(sql)
        db.close()

आशा है कि यह किसी को भी मेरी मदद करता है :)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.