Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there anyway to create unique TextField in django with mysql db backend?

Tags:

mysql

django

I create a model for multiple choice question. Each question has 5 choice of answers. And I need each question object to be unique based on it's question and answers. And so, I design the model like this.

from django.db import models


class MultipleChoiceQuestion(models.Model):
    ANSWERS = [('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('e', 'e')]
    question = models.TextField()
    a = models.TextField()
    b = models.TextField()
    c = models.TextField()
    d = models.TextField()
    e = models.TextField()
    true_answer = models.CharField(max_length=1, choices=ANSWERS)

    class Meta:
        unique_together = [('question', 'a', 'b', 'c', 'd', 'e')]

When I run migrate, mysql give this error:

1170, "BLOB/TEXT column 'question' used in key specification without a key length"

I found this error has been discussed here. But, I can't use CharField with it's small limit, because I need to store long text (until 10000 char or more).

sqlite3 and postgresql can do this (i mean django didn't complain about key specification for TEXT).

The reason I need to use mysql because the server where I will deploy this django app is only provide mysql, no postgresql.

So, is there anyway I could achieve this?

like image 846
Mas Bagol Avatar asked Oct 19 '15 09:10

Mas Bagol


1 Answers

It looks like this is a django/mysql bug where django blames MySql and "wontfix" it. Their suggestion is to leave the key off the model and just add the constraint manually. Huge hack but yes, that is probably the only solution. You will however need to recompile MySql if your key exceeds 1000 bytes.

The maximum key length is 1000 bytes. This can also be changed by changing the source and recompiling. For the case of a key longer than 250 bytes, a larger key block size than the default of 1024 bytes is used. From The Manual

I don't suggest that for several reasons including performance and all around hackery. Instead of recompiling I suggest you create a hash field that is unique. This creates an md5 sum of all the fields and will always be 32 characters. The odds of duplicates are 1 in 2^128 so you are pretty safe.

from django.db import models
import hashlib


class MultipleChoiceQuestion(models.Model):
    ANSWERS = [('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('e', 'e')]
    question = models.TextField()
    a = models.TextField()
    b = models.TextField()
    c = models.TextField()
    d = models.TextField()
    e = models.TextField()
    true_answer = models.CharField(max_length=1, choices=ANSWERS)
    unique_hash = models.CharField(max_length=32, unique=True)

    def save(self, *args, **kwargs):
        m = hashlib.md5()
        m.update(self.question)
        m.update(self.a)
        m.update(self.b)
        m.update(self.c)
        m.update(self.d)
        m.update(self.e)
        self.unique_hash = m.digest()
        super(MultipleChoiceQuestion, self).save(*args, **kwargs)
like image 164
dotcomly Avatar answered Oct 20 '22 22:10

dotcomly