Django'da unicode dizeyi kaydederken MySQL “yanlış dize değeri” hatası


158

İlk_adı, son_adı Django'nun auth_user modeline kaydetmeye çalıştığımda garip bir hata mesajı aldım.

Başarısız örnekler

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

Başarılı örnekler

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

MySQL ayarları

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)

Tablo karakter seti ve harmanlama

Auth_user tablosunda utf8_general_ci harmanlama içeren utf-8 karakter kümesi bulunur.

UPDATE komutunun sonuçları

UPDATE komutunu kullanarak yukarıdaki değerleri auth_user tablosuna güncellerken herhangi bir hata oluşmadı.

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

Yukarıda listelenen başarısız değerler, Django'da veritabanı arka ucunu değiştirdiğimde PostgreSQL tablosuna güncellenebilir. Bu garip.

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

Ancak http://www.postgresql.org/docs/8.1/interactive/multibyte.html adresinden aşağıdakileri buldum:

Name Bytes/Char
UTF8 1-4

Unicode char'ın PostgreSQL'de en fazla 4 bayt, MySQL'de 3 baytın yukarıdaki hataya neden olduğu anlamına mı geliyor?


2
Bu bir MySQL sorunu, Django değil: stackoverflow.com/questions/1168036/…
Vanuan

Yanıtlar:


140

Bu cevapların hiçbiri sorunu benim için çözmedi. Temel neden:

4 baytlık karakterleri utf-8 karakter kümesiyle MySQL'de depolayamazsınız.

MySQL bir sahiptir utf-8 karakter üzerine 3 bayt limitini (evet, 's wack, güzel burada Django geliştirici tarafından özetlenebilir )

Bunu çözmek için yapmanız gerekenler:

  1. Utf8mb4 karakter kümesini kullanmak için MySQL veritabanınızı, tablonuzu ve sütunlarınızı değiştirin (yalnızca MySQL 5.5'ten itibaren kullanılabilir)
  2. Django ayarları dosyanızdaki karakter kümesini aşağıdaki gibi belirtin:

settings.py

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

Not: Veritabanınızı yeniden oluştururken ' Belirtilen anahtar çok uzundu ' sorunuyla karşılaşabilirsiniz.

En olası neden, en fazla CharField255 uzunluğuna ve üzerinde bir tür dizine sahip olan bir örnektir (örneğin benzersiz). Utf8mb4, utf-8'den% 33 daha fazla alan kullandığından, bu alanları% 33 daha küçük yapmanız gerekir.

Bu durumda, max_length değerini 255'ten 191'e değiştirin.

Alternatif olarak, bu kısıtlamayı kaldırmak için MySQL yapılandırmanızı düzenleyebilirsiniz, ancak bazı django korsanlığı olmadan değil

GÜNCELLEME: Bu sorunla tekrar karşılaştım ve 191 karaktere indiremediğim için PostgreSQL'e geçtim VARCHAR.


13
bu cevabın yolu, yolu, daha fazla oyu gerekiyor. Teşekkürler! Asıl sorun, birileri 4baytlık bir karakter girmeye çalışana kadar uygulamanızın yıllarca iyi çalışabilmesi.
Michael Bylstra

2
Bu kesinlikle doğru cevap. OPTIONS ayarı, django'nun emoji karakterlerini çözmesi ve bunları MySQL'de saklaması için kritik öneme sahiptir. SQL komutları ile sadece mysql karakter kümesini utf8mb4 olarak değiştirmek yeterli değildir!
Xerion

Tüm tablonun karakter setini utf8mb4 olarak güncellemeye gerek yoktur. Gerekli sütunların karakter kümesini güncelleyin. Ayrıca 'charset': 'utf8mb4'@ Xerion'un dediği gibi Django ayarlarındaki seçenek çok önemlidir. Son olarak, dizin sorunu bir karmaşa. Sütundaki dizini kaldırın veya uzunluğunu 191'den fazla yapmayın veya TextFieldbunun yerine kullanın!
Rockallite

2
Bu alıntıya olan bağlantınızı seviyorum : Bu, MySQL'in bilerek ve geri döndürülemez bir şekilde beyin hasarlı olmasının bir başka örneğidir. :)
Qback

120

Aynı sorunu yaşadım ve sütunun karakter kümesini değiştirerek çözdüm. Veritabanınızın varsayılan bir karakter seti olmasına rağmen, veritabanı utf-8sütunlarının MySQL'de farklı bir karakter setine sahip olabileceğini düşünüyorum. İşte kullandığım SQL QUERY:

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

14
Ugh, bu cevabı gerçekten tekrar okuyana kadar elimdeki tüm karakter setlerini değiştirdim: sütunlar tablolardan ve veritabanından bağımsız olarak kendi karakter setlerine sahip olabilir . Bu çılgınca ve aynı zamanda benim sorunumdu.
markpasc

1
Bu, TextField modelinde varsayılanlarla mysql kullanarak benim için de çalıştı.
madprops

Bu benim sorunumu çözdü. Yaptığım tek değişiklik utf8 / utf8_general_ci yerine utf8mb4 ve utf8mb4_general_ci kullanmaktı.
Michal Przysucha

70

Bu sorun varsa, mysql veritabanınızın tüm sütunlarını otomatik olarak değiştirmek için bir python betiği.

#! /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
Bu çözüm, dosya ve dizin yollarını depolayan bir django uygulamasıyla tüm sorunlarımı çözdü. Djanadı django veritabanı olarak atmak ve çalıştırın. Bir cazibe gibi çalıştı!
Chris

1
Daha db.commit()önce ekleyene kadar bu kod benim için çalışmadı db.close().
Mark Erdmann

1
Bu çözüm @markpasc yorumunda tartışılan sorunu önler mi? '... MySQL 5.1'in 3 baytlık utf8 karakter setindeki emoji gibi 4 baytlık UTF-8 karakterleri'
CatShoes

çözüm ben bir kayıt yalak django admin silerken bana yardımcı, o düzenleme oluştururken herhangi bir sorun yoktu ... garip! Hatta doğrudan db silmek mümkün
Javier Vieira

Modeli her değiştirdiğimde bunu yapmalı mıyım?
Vanuan

25

Bu yeni bir proje ise, veritabanını bırakıp uygun bir karakter grubuyla yeni bir tane oluşturacağım:

CREATE DATABASE <dbname> CHARACTER SET utf8;

Merhaba lütfen bu soruyu kontrol etmenize yardımcı olur stackoverflow.com/questions/46348817/…
King

Benim durumumda, bizim db docker tarafından oluşturulur, bu yüzden düzeltmek için db: command: komut benim oluşturma dosyasında - --character-set-server=utf8
ekledi

1
Kadar basit. @Vanuan
Enku

Bu yeni bir proje değilse, db'den yedekleme alıyoruz, bırakıyoruz ve utf8 charset ile yeniden oluşturuyoruz ve yedeklemeyi geri yüklüyoruz. Yeni olmayan
Mohammad Reza

8

Yukarıdaki hataları önlemek için sadece bir yöntem anladım.

Veritabanına kaydet

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

Böyle dizeleri bir MySQL tablosuna kaydetmenin ve görüntüleme şablonları oluşturmadan önce kodunu çözmenin tek yöntemi bu mudur?


12
Benzer bir sorun yaşıyorum, ancak bunun geçerli bir çözüm olduğu konusunda hemfikir değilim. Ne zaman .encode('unicode_escape')aslında veritabanında unicode karakterleri depolamak değiliz. Tüm istemcileri kullanmadan önce kodunu açmaya zorlarsınız, bu da django.admin veya diğer her tür şeyle düzgün çalışmayacağı anlamına gelir.
muudscope

3
Karakterler yerine kaçış kodlarını saklamak tatsız gibi görünse de, bu muhtemelen MySQL 5.1'in 3 baytlık utf8karakter setinde emoji gibi 4 baytlık UTF-8 karakterlerini kaydetmenin birkaç yolundan biridir .
markpasc

2
utf8mb4Temel Çok Dilli Düzlemden daha fazlasının saklanmasına izin veren bir kodlama vardır . Unicode'u tam olarak depolamak için gereken tek şeyin "UTF8" olduğunu düşünürdüm. Whaddaya biliyorum, değil. Bkz. Dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
Mihai Danila

@jack, kabul edilen cevabı daha faydalı
olana

bu uygulanabilir bir çözümdür, ancak ben de (muudscope tarafından savunulan şekilde) kullanılmasını önermiyorum. Örneğin, mysql veritabanlarına emoji kaydedemiyorum. Bunu başaran var mı?
Marcelo Sardelich

6

Metin alanınızın harmanlamasını UTF8_general_ci olarak değiştirebilirsiniz; sorun çözülecektir.

Dikkat edin, bu Django'da yapılamaz.


1

Unicode dizeleri kaydetmeye çalışmıyorsunuz, test kodlarını UTF-8 kodlamasına kaydetmeye çalışıyorsunuz. Onları gerçek unicode dize değişmezleri yapın:

user.last_name = u'Slatkevičius'

veya (dizgi değişmezleri olmadığında) utf-8 kodlamasını kullanarak bunları çözebilirsiniz:

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

@Thomas, tam olarak söylediğin gibi denedim ama yine de aynı hataları ortaya koyuyor.
jack

0

Sadece masanızı değiştirin, hiçbir şeye gerek yok. sadece bu sorguyu veritabanında çalıştırın. ALTER MASA table_nameKARAKTER SETİNE DÖNÜŞ utf8

kesinlikle işe yarayacak.


0

@Madprops yanıtına yönelik iyileştirme - django yönetim komutu olarak çözüm:

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()

Umarım bu benden başka herkese yardımcı olur :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.