คุณกำลังพยายามเพิ่มฟิลด์ 'new_field' ที่ไม่เป็นโมฆะให้กับโปรไฟล์ผู้ใช้โดยไม่มีค่าเริ่มต้น


109

ฉันรู้ว่าจาก Django 1.7 ฉันไม่จำเป็นต้องใช้ South หรือระบบการโยกย้ายอื่น ๆ ดังนั้นฉันแค่ใช้คำสั่งง่ายๆ python manage.py makemigrations

อย่างไรก็ตามสิ่งที่ฉันได้รับคือข้อผิดพลาดนี้:

You are trying to add a non-nullable field 'new_field' to userprofile without a default;
we can't do that (the database needs something to populate existing rows).

นี่คือ models.py:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    website = models.URLField(blank=True)
    new_field = models.CharField(max_length=140)

ตัวเลือกคืออะไร?


3
ฉันต้องการเพิ่มว่าปัญหาเกิดขึ้นเมื่อคุณกำลังเปลี่ยนตารางที่มีอยู่ไม่ใช่เมื่อคุณกำลังสร้างตารางใหม่
x-yuri

คำตอบ:


93

คุณต้องระบุค่าเริ่มต้น:

new_field = models.CharField(max_length=140, default='SOME STRING')

จะเกิดอะไรขึ้นถ้าฉันต้องมี new_field สำเนาของฟิลด์เว็บไซต์ ว่าควรมีลักษณะอย่างไร? default = websiteไม่ทำงาน
Irmantas Želionis

8
คุณต้องการที่จะทำครั้งแรกการโยกย้ายมีค่าเริ่มต้นปลอมแล้วสร้างการโยกย้ายข้อมูลเพื่อย้ำผ่านแต่ละระเบียนและการตั้งค่าไปnew_field website
dgel

ค่าเริ่มต้นควรเป็นทางเลือกสำหรับ CharField
Florent

88

หากคุณอยู่ในช่วงเริ่มต้นของการพัฒนาและไม่สนใจข้อมูลฐานข้อมูลปัจจุบันของคุณคุณสามารถลบออกจากนั้นย้ายข้อมูลได้ แต่ก่อนอื่นคุณต้องทำความสะอาด dir การย้ายข้อมูลและลบแถวออกจากตาราง (django_migrations)

rm  your_app/migrations/*

rm db.sqlite3
python manage.py makemigrations
python manage.py migrate

8
อีกหนึ่งสิ่ง. คุณต้องล้างตารางการย้ายข้อมูลเช่นmysql -u[user] [database] -e "delete from django_migrations where app=[your_app]"
helpse

นี่คือวิธีที่ ฉันคิดว่า Django ดูดการโยกย้ายและการตรวจจับโมเดล ฯลฯ ต้องการการอัปเดตที่สำคัญ
WesternGun

ฉันได้ลบไฟล์ทั้งหมดจากการย้ายข้อมูลและลบแถวทั้งหมดจาก django_migrations และตอนนี้ฉันได้รับ "django.db.migrations.exceptions.NodeNotFoundError: Migration stories.0001_initial dependencies อ้างอิงโหนดหลักที่ไม่มีอยู่ ('sources', '0002_auto_20180903_1224')" เมื่อเรียกใช้ การโยกย้าย.
brainLoop

1
ตรวจสอบให้แน่ใจว่าคุณไม่ได้ลบออก__init__.py จากการย้ายข้อมูลมิฉะนั้นคุณจะคืนค่าในภายหลังมิฉะนั้นการย้ายข้อมูลจะไม่ทำงานstackoverflow.com/a/46362750/1649917
nmu

python manage.py sqlmigrate name_of_your_app nb_of_the_migrationก็จำเป็นเช่นกัน
zar3bski

38

ทางเลือกหนึ่งคือการประกาศค่าเริ่มต้นสำหรับ 'new_field':

new_field = models.CharField(max_length=140, default='DEFAULT VALUE')

อีกทางเลือกหนึ่งคือการประกาศ 'new_field' เป็นช่องว่าง:

new_field = models.CharField(max_length=140, null=True)

หากคุณตัดสินใจที่จะยอมรับ "new_field" เป็นช่องว่างคุณอาจต้องการยอมรับ "no input" เป็นอินพุตที่ถูกต้องสำหรับ "new_field" จากนั้นคุณต้องเพิ่มblank=Trueคำสั่งด้วย:

new_field = models.CharField(max_length=140, blank=True, null=True)

แม้จะมีnull=Trueและ / หรือblank=Trueคุณสามารถเพิ่มค่าเริ่มต้นได้หากจำเป็น:

new_field = models.CharField(max_length=140, default='DEFAULT VALUE', blank=True, null=True)

7
«หลีกเลี่ยงการใช้ null ในฟิลด์ที่ใช้สตริงเช่น CharField และ TextField หากฟิลด์ที่ใช้สตริงมี null = True นั่นหมายความว่ามีค่าที่เป็นไปได้สองค่าสำหรับ "ไม่มีข้อมูล": NULL และสตริงว่าง» - การอ้างอิงฟิลด์โมเดล
Chema

7

หากคุณเพิ่งเข้าสู่วงจรการพัฒนาคุณสามารถลองทำสิ่งนี้ -

ลบ / แสดงความคิดเห็นรุ่นนั้นและการใช้งานทั้งหมด ใช้การย้ายข้อมูล นั่นจะเป็นการลบโมเดลนั้นจากนั้นเพิ่มโมเดลอีกครั้งเรียกใช้การโอนย้ายและคุณมีโมเดลใหม่ทั้งหมดที่มีการเพิ่มฟิลด์ใหม่


และล้างตารางการย้ายข้อมูล django_migrations ตามที่อธิบายไว้ที่นี่: stackoverflow.com/questions/26185687/…หรือใช้ GUI และลบแถวที่ต้องการ
RusI

5

ในกรณีที่มีใครตั้งค่า a ForeignKeyคุณสามารถอนุญาตช่องว่างได้โดยไม่ต้องตั้งค่าเริ่มต้น:

new_field = models.ForeignKey(model, null=True)

หากคุณมีข้อมูลที่จัดเก็บไว้ในฐานข้อมูลอยู่แล้วคุณยังสามารถตั้งค่าเริ่มต้น:

new_field = models.ForeignKey(model, default=<existing model id here>)

4

หาก "เว็บไซต์" ว่างเปล่าเกินnew_fieldควรกำหนดให้ว่าง

ตอนนี้ถ้าคุณต้องการเพิ่มตรรกะในการบันทึกโดยที่ถ้าnew_fieldว่างเพื่อคว้าค่าจาก "เว็บไซต์" สิ่งที่คุณต้องทำคือแทนที่ฟังก์ชันบันทึกสำหรับModelสิ่งนี้ของคุณ:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    website = models.URLField(blank=True, default='DEFAULT VALUE')
    new_field = models.CharField(max_length=140, blank=True, default='DEFAULT VALUE')

    def save(self, *args, **kwargs):
        if not self.new_field:
            # Setting the value of new_field with website's value
            self.new_field = self.website

        # Saving the object with the default save() function
        super(UserProfile, self).save(*args, **kwargs)

3

คุณไม่สามารถเพิ่มการอ้างอิงไปยังตารางที่มีข้อมูลอยู่แล้ว
เปลี่ยนแปลง:

user = models.OneToOneField(User)

ถึง:

user = models.OneToOneField(User, default = "")

ทำ:

python manage.py makemigrations
python manage.py migrate

เปลี่ยนอีกครั้ง:

user = models.OneToOneField(User)

ทำการโยกย้ายอีกครั้ง:

python manage.py makemigrations
python manage.py migrate

2

ใน new_file ให้เพิ่มคุณสมบัติบูลีน null

new_field = models.CharField(max_length=140, null=True)

หลังจากที่คุณเรียกใช้./manage.py syncdbเพื่อรีเฟรช DB และในที่สุดคุณก็วิ่ง./manage.py makemigrations และ./manage.py migrate



2

คุณมีรายการฐานข้อมูลในตารางแล้วUserProfileหรือไม่? ถ้าเป็นเช่นนั้นเมื่อคุณเพิ่มคอลัมน์ใหม่ DB จะไม่รู้ว่าจะตั้งค่าเป็นอะไรเพราะไม่สามารถเป็นNULLได้ ดังนั้นระบบจะถามคุณว่าคุณต้องการตั้งค่าฟิลด์เหล่านั้นในคอลัมน์new_fieldsเป็นอะไร ฉันต้องลบแถวทั้งหมดจากตารางนี้เพื่อแก้ปัญหา

(ฉันรู้ว่ามีคำตอบเมื่อสักครู่แล้ว แต่ฉันเพิ่งพบปัญหานี้และนี่คือวิธีแก้ปัญหาของฉันหวังว่ามันจะช่วยทุกคนใหม่ที่เห็นสิ่งนี้)


1
ฉันยังไม่ได้แทรกแถวลงในตารางและยังคงได้รับสิ่งนี้ ฉันต้องลบประวัติการย้ายข้อมูลทั้งหมดล้างตารางการย้ายข้อมูลเพื่อเปลี่ยนโมเดลตามที่แนะนำโดย Tomasz
WesternGun

1

ฉันคิดตามตรงว่าวิธีที่ดีที่สุดในการแก้ไขปัญหานี้คือการสร้างโมเดลอื่นที่มีฟิลด์ทั้งหมดที่คุณต้องการและตั้งชื่อให้แตกต่างกันเล็กน้อย เรียกใช้การย้ายข้อมูล ลบโมเดลที่ไม่ได้ใช้และเรียกใช้การย้ายข้อมูลอีกครั้ง Voila


1

หากคุณเข้าใจดีในการตัดทอนตารางของโมเดลที่เป็นปัญหาคุณสามารถระบุค่าเริ่มต้นเพียงครั้งเดียวNoneในพรอมต์ การย้ายข้อมูลจะไม่จำเป็นdefault=Noneในขณะที่รหัสของคุณไม่มีค่าเริ่มต้น สามารถใช้งานได้ดีเนื่องจากไม่มีข้อมูลในตารางอีกต่อไปซึ่งจะต้องใช้ค่าเริ่มต้น


1

ฉันอยู่ในช่วงต้นของวงจรการพัฒนาดังนั้นสิ่งนี้อาจไม่ได้ผลสำหรับทุกคน (แต่ฉันไม่เห็นว่าทำไมมันถึงไม่ได้)

ฉันเพิ่มblank=True, null=Trueในคอลัมน์ที่ฉันได้รับข้อผิดพลาด จากนั้นฉันก็วิ่งpython manage.py makemigrationsคำสั่ง

ทันทีหลังจากเรียกใช้คำสั่งนี้ (และก่อนรันpython manage.py migrate) ฉันลบคำสั่งนี้ออกblank=True, null=Trueจากคอลัมน์ทั้งหมด จากนั้นฉันก็วิ่งpython manage.py makemigrationsอีกครั้ง ฉันได้รับตัวเลือกในการเปลี่ยนคอลัมน์ด้วยตัวเองซึ่งฉันเลือก

จากนั้นฉันก็วิ่งpython manage.py migrateและทุกอย่างทำงานได้ดี!


0

สิ่งที่ Django พูดจริงๆคือ:

ตาราง Userprofile มีข้อมูลอยู่และอาจมีnew_fieldค่าที่เป็นโมฆะ แต่ฉันไม่ทราบคุณแน่ใจหรือไม่ว่าต้องการทำเครื่องหมายคุณสมบัติว่าไม่เป็นโมฆะเพราะถ้าคุณทำคุณอาจได้รับข้อผิดพลาดหากมีค่ากับ NULL

หากคุณแน่ใจว่าไม่มีค่าใดในuserprofileตารางเป็นโมฆะ - ไม่ต้องสนใจคำเตือน

แนวทางปฏิบัติที่ดีที่สุดในกรณีดังกล่าวคือการสร้างการโยกย้าย RunPython เพื่อจัดการกับค่าว่างตามที่ระบุในตัวเลือก 2

2) ละเว้นในตอนนี้และให้ฉันจัดการแถวที่มีอยู่ด้วย NULL ด้วยตัวเอง (เช่นเพราะคุณเพิ่มการดำเนินการ RunPython หรือ RunSQL เพื่อจัดการค่า NULL ในการย้ายข้อมูลก่อนหน้านี้)

ในการโยกย้าย RunPython คุณต้องหาUserProfileอินสแตนซ์ทั้งหมดที่มีnew_fieldค่าว่างและใส่ค่าที่ถูกต้องที่นั่น (หรือค่าเริ่มต้นตามที่ Django ขอให้คุณตั้งค่าในโมเดล) คุณจะได้รับสิ่งนี้:

# please keep in mind that new_value can be an empty string. You decide whether it is a correct value.
for profile in UserProfile.objects.filter(new_value__isnull=True).iterator():
    profile.new_value = calculate_value(profile)
    profile.save() # better to use batch save

มีความสุข!


ฉันไม่พบว่าสิ่งนี้เขียนไว้ที่ไหนในเอกสาร คุณช่วยโพสต์ลิงค์ได้ไหม
Cody

0

ใน models.py

class UserProfile(models.Model): user = models.OneToOneField(User) website = models.URLField(blank=True) new_field = models.CharField(max_length=140, default="some_value")

คุณต้องเพิ่มค่าบางค่าเป็นค่าเริ่มต้น


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.