กำลังโหลดข้อมูลเริ่มต้นด้วย Django 1.7 และการย้ายข้อมูล


97

ฉันเพิ่งเปลี่ยนจาก Django 1.6 เป็น 1.7 และฉันเริ่มใช้การย้ายข้อมูล (ฉันไม่เคยใช้ภาคใต้)

ก่อน 1.7 ฉันเคยโหลดข้อมูลเริ่มต้นด้วยfixture/initial_data.jsonไฟล์ซึ่งโหลดด้วยpython manage.py syncdbคำสั่ง (เมื่อสร้างฐานข้อมูล)

ตอนนี้ฉันเริ่มใช้การย้ายข้อมูลและเลิกใช้งานพฤติกรรมนี้แล้ว:

หากแอปพลิเคชันใช้การย้ายข้อมูลจะไม่มีการโหลดส่วนควบโดยอัตโนมัติ เนื่องจากจำเป็นต้องมีการโยกย้ายสำหรับแอปพลิเคชันใน Django 2.0 จึงถือว่าพฤติกรรมนี้เลิกใช้แล้ว หากคุณต้องการโหลดข้อมูลเริ่มต้นสำหรับแอปให้พิจารณาดำเนินการในการย้ายข้อมูล ( https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )

เอกสารอย่างเป็นทางการไม่ได้เป็นตัวอย่างที่ชัดเจนเกี่ยวกับวิธีการที่จะทำมันดังนั้นคำถามของฉันคือ

วิธีใดเป็นวิธีที่ดีที่สุดในการนำเข้าข้อมูลเริ่มต้นโดยใช้การย้ายข้อมูล:

  1. เขียนรหัสงูใหญ่มีหลายสายไปmymodel.create(...),
  2. ใช้หรือเขียนฟังก์ชัน Django ( เช่นการโทรloaddata ) เพื่อโหลดข้อมูลจากไฟล์ฟิกซ์เจอร์ JSON

ฉันชอบตัวเลือกที่สองมากกว่า

ฉันไม่ต้องการใช้ South เพราะ Django ดูเหมือนจะสามารถทำได้โดยกำเนิดในตอนนี้


3
นอกจากนี้ฉันต้องการเพิ่มคำถามอื่นในคำถามเดิมของ OP: เราจะย้ายข้อมูลอย่างไรสำหรับข้อมูลที่ไม่ได้อยู่ในแอปพลิเคชันของเรา ตัวอย่างเช่นหากมีใครใช้เฟรมเวิร์กไซต์เขาจำเป็นต้องมีการติดตั้งกับข้อมูลไซต์ เนื่องจากเฟรมเวิร์กไซต์ไม่เกี่ยวข้องกับแอปพลิเคชันของเราเราควรย้ายข้อมูลนั้นไปไว้ที่ใด ขอบคุณ!
Serafeim

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

อีกสถานการณ์ที่น่าสนใจคือจะเกิดอะไรขึ้นหากคุณมีการย้ายข้อมูลเพื่อสร้างการตรวจสอบสิทธิ์ตัวอย่างเช่นกลุ่มอินสแตนซ์และในภายหลังคุณมีกลุ่มใหม่ที่คุณต้องการสร้างเป็นข้อมูลเริ่มต้น คุณจะต้องสร้างการย้ายข้อมูลใหม่ สิ่งนี้อาจสร้างความรำคาญเนื่องจากข้อมูล Group seed ของคุณจะอยู่ในหลายไฟล์ นอกจากนี้ในกรณีที่คุณต้องการรีเซ็ตการย้ายข้อมูลคุณจะต้องตรวจสอบเพื่อค้นหาการย้ายข้อมูลที่ตั้งค่าข้อมูลเริ่มต้นและพอร์ตด้วย
hekevintran

@Serafeim คำถาม "ตำแหน่งที่จะใส่ข้อมูลเริ่มต้นสำหรับแอปของบุคคลที่สาม" จะไม่เปลี่ยนแปลงหากคุณใช้การย้ายข้อมูลแทนการติดตั้งเนื่องจากคุณเปลี่ยนวิธีโหลดข้อมูลเท่านั้น ฉันใช้แอปที่กำหนดเองขนาดเล็กสำหรับสิ่งต่างๆเช่นนี้ หากแอปของบุคคลที่สามเรียกว่า "foo" ฉันจะเรียกแอปง่ายๆของฉันที่มีการย้ายข้อมูล / ติดตั้ง "foo_integration"
guettli

@guettli ใช่อาจใช้แอปพลิเคชั่นเสริมเป็นวิธีที่ดีที่สุด!
Serafeim

คำตอบ:


82

อัปเดต : ดูความคิดเห็นของ @ GwynBleidD ด้านล่างสำหรับปัญหาที่วิธีแก้ปัญหานี้อาจทำให้เกิดและดูคำตอบของ @ Rockallite ด้านล่างสำหรับแนวทางที่ทนทานต่อการเปลี่ยนแปลงโมเดลในอนาคต


สมมติว่าคุณมีไฟล์ฟิกซ์เจอร์ใน <yourapp>/fixtures/initial_data.json

  1. สร้างการย้ายข้อมูลที่ว่างเปล่าของคุณ:

    ใน Django 1.7:

    python manage.py makemigrations --empty <yourapp>
    

    ใน Django 1.8+ คุณสามารถระบุชื่อ:

    python manage.py makemigrations --empty <yourapp> --name load_intial_data
    
  2. แก้ไขไฟล์การย้ายข้อมูลของคุณ <yourapp>/migrations/0002_auto_xxx.py

    2.1. การใช้งานแบบกำหนดเองโดยได้รับแรงบันดาลใจจาก Django ' loaddata(คำตอบเริ่มต้น):

    import os
    from sys import path
    from django.core import serializers
    
    fixture_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../fixtures'))
    fixture_filename = 'initial_data.json'
    
    def load_fixture(apps, schema_editor):
        fixture_file = os.path.join(fixture_dir, fixture_filename)
    
        fixture = open(fixture_file, 'rb')
        objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
        for obj in objects:
            obj.save()
        fixture.close()
    
    def unload_fixture(apps, schema_editor):
        "Brutally deleting all entries for this model..."
    
        MyModel = apps.get_model("yourapp", "ModelName")
        MyModel.objects.all().delete()
    
    class Migration(migrations.Migration):  
    
        dependencies = [
            ('yourapp', '0001_initial'),
        ]
    
        operations = [
            migrations.RunPython(load_fixture, reverse_code=unload_fixture),
        ]
    

    2.2. วิธีที่ง่ายกว่าสำหรับload_fixture(ต่อคำแนะนำของ @ juliocesar):

    from django.core.management import call_command
    
    fixture_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '../fixtures'))
    fixture_filename = 'initial_data.json'
    
    def load_fixture(apps, schema_editor):
        fixture_file = os.path.join(fixture_dir, fixture_filename)
        call_command('loaddata', fixture_file) 
    

    มีประโยชน์หากคุณต้องการใช้ไดเร็กทอรีที่กำหนดเอง

    2.3. ที่ง่ายที่สุด:โทรloaddataที่มีapp_labelการติดตั้งจะโหลดจาก<yourapp>'s fixturesdir โดยอัตโนมัติ:

    from django.core.management import call_command
    
    fixture = 'initial_data'
    
    def load_fixture(apps, schema_editor):
        call_command('loaddata', fixture, app_label='yourapp') 
    

    หากคุณไม่ระบุapp_labelloaddata จะพยายามโหลดfixtureชื่อไฟล์จากไดเรกทอรีการติดตั้งแอปทั้งหมด (ซึ่งคุณอาจไม่ต้องการ)

  3. เรียกใช้

    python manage.py migrate <yourapp>
    

1
ตกลงคุณพูดถูก ... นอกจากนี้การโทรloaddata('loaddata', fixture_filename, app_label='<yourapp>')จะไปที่ dir การติดตั้งแอปโดยตรง (ดังนั้นไม่จำเป็นต้องสร้างเส้นทางแบบเต็มของอุปกรณ์ติดตั้ง)
n__o

15
การใช้วิธีดังกล่าว serializer จะทำงานกับสถานะของโมเดลจากmodels.pyไฟล์ปัจจุบันซึ่งอาจมีฟิลด์พิเศษหรือการเปลี่ยนแปลงอื่น ๆ หากมีการเปลี่ยนแปลงบางอย่างหลังจากสร้างการย้ายข้อมูลจะล้มเหลว (เราจึงไม่สามารถสร้างการย้ายสคีมาได้หลังจากการย้ายข้อมูลนั้น) ในการแก้ไขว่าเราสามารถเปลี่ยนรีจิสตรีแอพที่ serializer ทำงานกับรีจิสตรีที่ให้ไว้กับ fuction การย้ายข้อมูลในพารามิเตอร์แรกได้ Registry to path อยู่ที่django.core.serializers.python.apps.
GwynBleidD

3
เราจะทำเช่นนี้ทำไม? ทำไม Django จึงวิ่งและดูแลรักษายากขึ้นเรื่อย ๆ ? ฉันไม่ต้องการไปแม้ว่าสิ่งนี้ฉันต้องการอินเทอร์เฟซบรรทัดคำสั่งง่ายๆที่ช่วยแก้ปัญหานี้สำหรับฉันเช่นที่เคยเป็นมากับอุปกรณ์ติดตั้ง Django ควรจะทำให้สิ่งนี้ง่ายขึ้นไม่ยากขึ้น :(
CpILL

1
@GwynBleidD นี่คือจุดสำคัญมากที่คุณกำลังทำและฉันคิดว่ามันควรจะปรากฏในคำตอบที่ยอมรับนี้ มันเป็นคำพูดเดียวกันกับที่ปรากฏเป็นความคิดเห็นในตัวอย่างรหัสการโยกย้ายข้อมูลของเอกสาร คุณรู้วิธีอื่นในการใช้ซีเรียลไลเซอร์กับสิ่งที่ให้มาapp registryโดยไม่ต้องเปลี่ยนตัวแปรส่วนกลาง (ซึ่งอาจทำให้เกิดปัญหาในอนาคตที่ไม่คาดคิดด้วยการย้ายฐานข้อมูลแบบขนาน)
Ad N

3
คำตอบนี้ได้รับการโหวตให้ kazoo พร้อมกับการยอมรับคือเหตุผลที่ฉันแนะนำให้คนไม่ใช้ stackoverflow แม้ตอนนี้จะมีความคิดเห็นและเกร็ดเล็กเกร็ดน้อยฉันก็ยังมีคนใน #django อ้างถึงสิ่งนี้
shangxiao

53

เวอร์ชั่นสั้น

คุณไม่ควรใช้loaddataคำสั่งการจัดการโดยตรงในการย้ายข้อมูล

# Bad example for a data migration
from django.db import migrations
from django.core.management import call_command


def load_fixture(apps, schema_editor):
    # No, it's wrong. DON'T DO THIS!
    call_command('loaddata', 'your_data.json', app_label='yourapp')


class Migration(migrations.Migration):
    dependencies = [
        # Dependencies to other migrations
    ]

    operations = [
        migrations.RunPython(load_fixture),
    ]

รุ่นยาว

loaddataใช้django.core.serializers.python.Deserializerซึ่งใช้โมเดลที่ทันสมัยที่สุดในการยกเลิกการต่อสายข้อมูลในอดีตในการย้ายข้อมูล นั่นเป็นพฤติกรรมที่ไม่ถูกต้อง

ตัวอย่างเช่นสมมติว่ามีการย้ายข้อมูลซึ่งใช้loaddataคำสั่งการจัดการเพื่อโหลดข้อมูลจากฟิกซ์เจอร์และมีการใช้กับสภาพแวดล้อมการพัฒนาของคุณแล้ว

หลังจากนั้นคุณตัดสินใจที่จะเพิ่มฟิลด์บังคับใหม่ให้กับโมเดลที่เกี่ยวข้องดังนั้นคุณจึงดำเนินการและทำการโอนย้ายใหม่กับโมเดลที่อัปเดตของคุณ (และอาจให้ค่าแบบครั้งเดียวสำหรับฟิลด์ใหม่เมื่อ./manage.py makemigrationsแจ้งให้คุณทราบ)

คุณเรียกใช้การย้ายข้อมูลครั้งต่อไปและทุกอย่างเรียบร้อยดี

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

อย่างไรก็ตามการโยกย้ายข้อมูลล้มเหลว นั่นเป็นเพราะloaddataคำสั่งdeserialized model from ซึ่งแสดงถึงรหัสปัจจุบันไม่สามารถบันทึกด้วยข้อมูลว่างสำหรับฟิลด์บังคับใหม่ที่คุณเพิ่มเข้าไป การติดตั้งเดิมขาดข้อมูลที่จำเป็นสำหรับมัน!

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

สรุป:ในการย้ายข้อมูลloaddataคำสั่งจะแนะนำความไม่สอดคล้องกันที่อาจเกิดขึ้นระหว่างโมเดลและฐานข้อมูล คุณควรแน่นอนไม่ใช้มันโดยตรงในการโยกย้ายข้อมูล

การแก้ไขปัญหา

loaddataคำสั่งอาศัยdjango.core.serializers.python._get_modelฟังก์ชันเพื่อรับโมเดลที่เกี่ยวข้องจากฟิกซ์เจอร์ซึ่งจะส่งคืนเวอร์ชันล่าสุดของโมเดล เราจำเป็นต้องปะลิงเพื่อให้ได้รูปแบบประวัติศาสตร์

(รหัสต่อไปนี้ใช้ได้กับ Django 1.8.x)

# Good example for a data migration
from django.db import migrations
from django.core.serializers import base, python
from django.core.management import call_command


def load_fixture(apps, schema_editor):
    # Save the old _get_model() function
    old_get_model = python._get_model

    # Define new _get_model() function here, which utilizes the apps argument to
    # get the historical version of a model. This piece of code is directly stolen
    # from django.core.serializers.python._get_model, unchanged. However, here it
    # has a different context, specifically, the apps variable.
    def _get_model(model_identifier):
        try:
            return apps.get_model(model_identifier)
        except (LookupError, TypeError):
            raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)

    # Replace the _get_model() function on the module, so loaddata can utilize it.
    python._get_model = _get_model

    try:
        # Call loaddata command
        call_command('loaddata', 'your_data.json', app_label='yourapp')
    finally:
        # Restore old _get_model() function
        python._get_model = old_get_model


class Migration(migrations.Migration):
    dependencies = [
        # Dependencies to other migrations
    ]

    operations = [
        migrations.RunPython(load_fixture),
    ]

1
Rockallite คุณสร้างจุดแข็งมาก คำตอบของคุณทำให้ฉันสงสัยว่าโซลูชัน 2.1 จากคำตอบของ @ n__o / @ mlissner จะขึ้นอยู่กับobjects = serializers.deserialize('json', fixture, ignorenonexistent=True)ความทุกข์ทรมานจากปัญหาเดียวกันloaddataหรือไม่ หรือignorenonexistent=Trueครอบคลุมประเด็นที่เป็นไปได้ทั้งหมด?
Dário

7
หากคุณดูที่แหล่งที่มาคุณจะพบว่าignorenonexistent=Trueอาร์กิวเมนต์มีผลสองอย่างคือ1)ไม่สนใจโมเดลของฟิกซ์เจอร์ที่ไม่อยู่ในนิยามของโมเดลปัจจุบันส่วนใหญ่2)จะละเว้นฟิลด์ของโมเดลของฟิกซ์เจอร์ที่ไม่ใช่ ในคำจำกัดความของโมเดลที่สอดคล้องกันล่าสุด ไม่มีของพวกเขาจัดการใหม่จำเป็นต้องใช้สนามในที่แบบจำลองสถานการณ์ loaddataดังนั้นใช่ฉันคิดว่ามันได้รับความทุกข์ปัญหาเดียวกันเป็นธรรมดา
Rockallite

สิ่งนี้ใช้ได้ผลดีเมื่อฉันพบว่า json เก่าของฉันมีโมเดลที่อ้างอิงโมเดลอื่นโดยใช้ a natural_key()ซึ่งดูเหมือนว่าวิธีนี้จะไม่รองรับ - ฉันเพิ่งแทนที่ค่า natural_key ด้วย id จริงของโมเดลที่อ้างอิง
dsummersl

1
คำตอบนี้อาจเป็นคำตอบที่ได้รับการยอมรับจะมีประโยชน์มากกว่าเนื่องจากในการเรียกใช้ testcases จะมีการสร้างฐานข้อมูลใหม่และใช้การย้ายข้อมูลทั้งหมดตั้งแต่เริ่มต้น โซลูชันนี้แก้ไขปัญหาที่โปรเจ็กต์ที่มี unittest จะประสบในกรณีที่ไม่ได้แทนที่ _get_model ในการย้ายข้อมูล Tnx
Mohammad ali baghershemirani

ขอบคุณสำหรับการอัปเดตและคำอธิบาย @Rockallite คำตอบเริ่มต้นของฉันถูกโพสต์ไม่กี่สัปดาห์หลังจากมีการแนะนำการย้ายข้อมูลใน Django 1.7 และเอกสารเกี่ยวกับวิธีดำเนินการก็ไม่ชัดเจน (และยังคงเป็นครั้งสุดท้ายที่ฉันตรวจสอบ) หวังว่า Django จะอัปเดตกลไกการโหลดข้อมูล / การโยกย้ายเพื่อพิจารณาประวัติโมเดลสักวัน
n__o

6

แรงบันดาลใจจากความคิดเห็นบางส่วน (คือของ n__o) และความจริงที่ว่าฉันมีinitial_data.*ไฟล์จำนวนมากที่กระจายออกไปในหลาย ๆ แอพฉันตัดสินใจสร้างแอพ Django ที่จะอำนวยความสะดวกในการสร้างการย้ายข้อมูลเหล่านี้

ใช้Django-โยกย้ายประจำที่คุณสามารถเรียกใช้คำสั่งต่อไปนี้การจัดการและมันจะค้นหาผ่านของคุณทั้งหมดINSTALLED_APPSสำหรับinitial_data.*ไฟล์และเปิดให้เป็นการโยกย้ายข้อมูล

./manage.py create_initial_data_fixtures
Migrations for 'eggs':
  0002_auto_20150107_0817.py:
Migrations for 'sausage':
  Ignoring 'initial_data.yaml' - migration already exists.
Migrations for 'foo':
  Ignoring 'initial_data.yaml' - not migrated.

ดูdjango-migration-fixtureสำหรับคำแนะนำในการติดตั้ง / การใช้งาน


2

หากต้องการให้ข้อมูลเริ่มต้นแก่ฐานข้อมูลของคุณให้เขียนการย้ายข้อมูล ในการย้ายข้อมูลให้ใช้ฟังก์ชันRunPythonเพื่อโหลดข้อมูลของคุณ

อย่าเขียนคำสั่ง loaddata เนื่องจากวิธีนี้เลิกใช้แล้ว

การย้ายข้อมูลของคุณจะดำเนินการเพียงครั้งเดียว การย้ายข้อมูลเป็นลำดับของการย้ายข้อมูล เมื่อมีการเรียกใช้การโอนย้าย 003_xxxx.py django migrations จะเขียนในฐานข้อมูลว่าแอปนี้ถูกย้ายไปจนถึงแอปนี้ (003) และจะเรียกใช้การย้ายข้อมูลต่อไปนี้เท่านั้น


คุณแนะนำให้ฉันโทรซ้ำmyModel.create(...)(หรือใช้ลูป) ในฟังก์ชัน RunPython หรือไม่?
Mickaël

ใช่เลย Transaactionnal databases จะจัดการได้อย่างสมบูรณ์ :)
FlogFR

1

วิธีแก้ปัญหาที่นำเสนอข้างต้นไม่ได้ผลสำหรับฉันขออภัย ฉันพบว่าทุกครั้งที่ฉันเปลี่ยนรุ่นของฉันฉันต้องอัปเดตส่วนควบของฉัน ตามหลักการแล้วฉันจะเขียนการย้ายข้อมูลแทนเพื่อแก้ไขข้อมูลที่สร้างขึ้นและข้อมูลที่โหลดการติดตั้งในทำนองเดียวกัน

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


ขอบคุณสำหรับสิ่งนี้! ฉันเขียนเวอร์ชันที่ใช้งานได้กับ Python 3 (และผ่าน Pylint ที่เข้มงวดของเรา) คุณสามารถใช้เป็นโรงงานด้วยRunPython(load_fixture('badger', 'stoat')). gist.github.com/danni/1b2a0078e998ac080111
Danielle Madeley

1

ในความคิดของฉันการแข่งขันค่อนข้างแย่ หากฐานข้อมูลของคุณมีการเปลี่ยนแปลงบ่อย ๆ การทำให้เป็นปัจจุบันอยู่เสมอจะกลายเป็นฝันร้ายในไม่ช้า จริงๆแล้วไม่ใช่แค่ความคิดเห็นของฉันเท่านั้นในหนังสือ "Two Scoops of Django" มันอธิบายได้ดีกว่ามาก

ฉันจะเขียนไฟล์ Python เพื่อตั้งค่าเริ่มต้นแทน ถ้าคุณต้องการบางสิ่งบางอย่างมากขึ้นผมจะขอแนะนำให้คุณดูเด็กโรงงาน

หากคุณจำเป็นต้องย้ายข้อมูลบางอย่างที่คุณควรใช้การโยกย้ายข้อมูล

นอกจากนี้ยังมี"Burn Your Fixtures, Use Model Factory"เกี่ยวกับการใช้อุปกรณ์ติดตั้ง


1
ฉันเห็นด้วยกับประเด็นของคุณ "ยากที่จะรักษาหากมีการเปลี่ยนแปลงบ่อย" แต่ในที่นี้การติดตั้งมีจุดมุ่งหมายเพื่อให้ข้อมูลเบื้องต้น (และน้อยที่สุด) เมื่อติดตั้งโครงการ ...
Mickaël

1
นี่คือการโหลดข้อมูลเพียงครั้งเดียวซึ่งหากดำเนินการเสร็จภายในบริบทของการย้ายข้อมูลก็สมเหตุสมผล เนื่องจากหากอยู่ในการย้ายข้อมูลจึงไม่ควรทำการเปลี่ยนแปลงข้อมูล json การเปลี่ยนแปลงสคีมาใด ๆ ที่ต้องมีการเปลี่ยนแปลงข้อมูลที่อยู่ไกลออกไปควรได้รับการจัดการผ่านการย้ายข้อมูลอื่น (ณ จุดนั้นข้อมูลอื่นอาจอยู่ในฐานข้อมูลที่จะต้องแก้ไขด้วย)
mtnpaul

0

ใน Django 2.1 ฉันต้องการโหลดบางรุ่น (เช่นชื่อประเทศเป็นต้น) พร้อมข้อมูลเริ่มต้น

แต่ฉันต้องการให้สิ่งนี้เกิดขึ้นโดยอัตโนมัติทันทีหลังจากดำเนินการย้ายข้อมูลครั้งแรก

ดังนั้นฉันจึงคิดว่ามันจะเป็นการดีที่จะมีsql/โฟลเดอร์ในแต่ละแอปพลิเคชันที่ต้องโหลดข้อมูลเริ่มต้น

จากนั้นภายในsql/โฟลเดอร์นั้นฉันจะมี.sqlไฟล์ที่มี DML ที่จำเป็นเพื่อโหลดข้อมูลเริ่มต้นลงในโมเดลที่เกี่ยวข้องเช่น:

INSERT INTO appName_modelName(fieldName)
VALUES
    ("country 1"),
    ("country 2"),
    ("country 3"),
    ("country 4");

เพื่อให้อธิบายได้ชัดเจนยิ่งขึ้นนี่คือลักษณะของแอพที่มีsql/โฟลเดอร์: ป้อนคำอธิบายภาพที่นี่

นอกจากนี้ฉันพบบางกรณีที่ฉันต้องการให้sqlสคริปต์ดำเนินการตามลำดับที่ระบุ ดังนั้นฉันจึงตัดสินใจที่จะนำหน้าชื่อไฟล์ด้วยหมายเลขติดต่อกันดังที่เห็นในภาพด้านบน

แล้วฉันจำเป็นต้องมีวิธีในการโหลดใด ๆSQLsที่มีอยู่ในโฟลเดอร์โปรแกรมใด ๆ python manage.py migrateโดยอัตโนมัติด้วยการทำ

ดังนั้นฉันจึงสร้างแอปพลิเคชั่นอื่นที่ชื่อinitial_data_migrationsแล้วเพิ่มแอพนี้ในรายการINSTALLED_APPSในsettings.pyไฟล์ จากนั้นฉันก็สร้างmigrationsโฟลเดอร์ภายในและเพิ่มไฟล์ชื่อrun_sql_scripts.py( ซึ่งจริงๆแล้วเป็นการโอนย้ายแบบกำหนดเอง ) ดังที่เห็นในภาพด้านล่าง:

ป้อนคำอธิบายภาพที่นี่

ฉันสร้างขึ้นrun_sql_scripts.pyเพื่อให้ดูแลการเรียกใช้sqlสคริปต์ทั้งหมดที่มีอยู่ในแต่ละแอปพลิเคชัน python manage.py migrateหนึ่งนี้เป็นเชื้อเพลิงแล้วเมื่อมีคนวิ่ง แบบกำหนดเองmigrationนี้ยังเพิ่มแอปพลิเคชันที่เกี่ยวข้องเป็นการอ้างอิงด้วยวิธีนี้จะพยายามเรียกใช้sqlคำสั่งหลังจากที่แอปพลิเคชันที่จำเป็นได้ดำเนินการ0001_initial.pyย้ายข้อมูลแล้วเท่านั้น (เราไม่ต้องการพยายามเรียกใช้คำสั่ง SQL กับตารางที่ไม่มีอยู่จริง)

นี่คือที่มาของสคริปต์นั้น:

import os
import itertools

from django.db import migrations
from YourDjangoProjectName.settings import BASE_DIR, INSTALLED_APPS

SQL_FOLDER = "/sql/"

APP_SQL_FOLDERS = [
    (os.path.join(BASE_DIR, app + SQL_FOLDER), app) for app in INSTALLED_APPS
    if os.path.isdir(os.path.join(BASE_DIR, app + SQL_FOLDER))
]

SQL_FILES = [
    sorted([path + file for file in os.listdir(path) if file.lower().endswith('.sql')])
    for path, app in APP_SQL_FOLDERS
]


def load_file(path):
    with open(path, 'r') as f:
        return f.read()


class Migration(migrations.Migration):

    dependencies = [
        (app, '__first__') for path, app in APP_SQL_FOLDERS
    ]

    operations = [
        migrations.RunSQL(load_file(f)) for f in list(itertools.chain.from_iterable(SQL_FILES))
    ]

ฉันหวังว่าใครบางคนจะพบว่าสิ่งนี้เป็นประโยชน์มันใช้งานได้ดีสำหรับฉัน!. หากคุณมีคำถามใด ๆ โปรดแจ้งให้เราทราบ

หมายเหตุ: นี่อาจไม่ใช่วิธีแก้ปัญหาที่ดีที่สุดเนื่องจากฉันเพิ่งเริ่มต้นใช้งาน django แต่ก็ยังอยากจะแบ่งปัน "วิธีการ" นี้กับทุกคนเนื่องจากฉันไม่พบข้อมูลมากนักในขณะที่ googling เกี่ยวกับเรื่องนี้

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