2017-03-02 40 views
1

我使用的是postgreSQLDjango 1.10python 3.4。我有這樣限定的Course模型:Django無法使用PostgreSQL的IntegrityError

class Course(models.Model): 

    title = models.CharField(max_length=200, unique=True) 
    content = models.TextField(max_length=200) 

然後我手動使用下面的命令添加唯一索引到title柱。

CREATE UNIQUE INDEX title_unique on wiki_course (LOWER(title));

比方說,數據庫已經擁有"Programming Basics"作爲標題。當我將"programming basics"添加到標題並點擊保存時,它會顯示以下錯誤。

IntegrityError at /admin/wiki/course/add/ 
duplicate key value violates unique constraint "title_unique" 
DETAIL: Key (lower(title::text))=(programming basics) already exists. 
Request Method: POST 
Request URL: http://127.0.0.1:8000/admin/wiki/course/add/ 
Django Version: 1.10.5 
Exception Type: IntegrityError 
Exception Value:  
duplicate key value violates unique constraint "title_unique" 

在otherhand如果我從MySQL數據庫切換,然後再試一次它會告訴我的課程已經存在。

enter image description here

有沒有辦法實現PostgreSQL的這種行爲?

請隨身攜帶,我是Django的新手。 感謝


更新:一種解決方案是使用使用citext類型字段標題。要做到這一點首先必須使用以下命令啓用citext擴展。

CREATE EXTENSION IF NOT EXISTS citext;

然後使用alter語句來更改所需的列的類型。

ALTER TABLE course ALTER COLUMN title TYPE citext;

執行這兩個查詢後。 Django在表單中顯示錯誤,而不是拋出IntegrityError異常。

如果有人知道更好的解決方案,請發佈它。

+0

,因爲'獨特= TRUE'您title'的'領域,所以如果你有重複'同名title',它應該返回錯誤.. –

+0

是的,我知道里面,但我想顯示錯誤在形式上不例外。 MySQL可以直接開箱即用。有什麼辦法可以在PostgreSQL中完成這件事。我在Google小組的某個地方閱讀了這個問題,他們建議在PostgreSQL中創建一個函數索引或其他東西。那是如何解決這個問題的? – Cody

+0

@SancaKembang我更新了問題請看看 – Cody

回答

1

MIGRATIONS

你最好的賭注是不會改變數據庫本身的約束,而是讓Django的處理更改您的機型。

假設你已經存在這個。

class Course(models.Model): 
    title = models.CharField(max_length=200) 

然後,您決定使title獨一無二。

class Course(models.Model): 
    title = models.CharField(max_length=200, unique=True) 

要強制執行此操作,您不要直接進入數據庫並調用命令。相反,我們允許Django爲我們處理遷移。

$ ./manage.py makemigrations 
$ ./manage.py migrate 

解決方案#1 - ON SAVE

class Course(models.Model): 
    title = models.CharField(max_length=200, unique=True) 

    def save(self, *args, **kwargs): 
     try: 
      Course.objects.get(title__iexact=self.title) 
      raise models.ValidationError 
     except Course.DoesNotExist: 
      pass 
     super().save(*args, **kwargs) 

這種方法的問題是,它使另一個調用到你的數據庫中的每個保存。不理想。

SOLUTION#2 - 兩個領域

你也可以有那種總是存儲小寫串空場,並提供了獨特。

class Course(models.Model): 
    title = models.CharField(max_length=200) 
    lower_title = models.CharField(max_length=200, unique=True, blank=True) 

    def save(self, *args, **kwargs): 
     self.lower_title = self.title.lower() 
     super().save(*args, **kwargs) 

在這裏,我們設置blank=True使得lower_title不會引發錯誤是空的。

這樣做的一個缺點是它會在數據庫中創建更多的開銷,因爲標題存儲在兩個位置。這裏的想法雖然是我們對一個我們總是知道的字段運行唯一約束將是小寫。然後,在需要的地方使用title

同樣,我們可以使用django的slugify來實現類似的結果。

from django.template.defaultfilters import slugify 


class Course(models.Model): 
    title = models.CharField(max_length=200) 
    slug = models.SlugField(max_length=200, unique=True) 

    def save(self, *args, **kwargs): 
     self.slug = slugify(self.title) 
     super().save(*args, **kwargs) 
+0

我知道什麼是遷移。我想在表單中顯示一個錯誤,並不是例外 – Cody

+0

此外,如果我不發出'CREATE UNIQUE INDEX title_unique on wiki_course(LOWER(title));'命令,postgresql甚至不會檢測到「Programming Basics」和「編程基礎」。它將簡單地將後面的內容添加到數據庫中。 – Cody

+0

然後,處理這個問題的「django」方法就是採用保存方法。或者,如果你在驗證表單上談論。當我回到電腦時,我會嘗試發佈我腦海中的代碼,並更新我的答案。 –