在django中迁移后运行.sql文件

z2acfund  于 2023-03-09  发布在  Go
关注(0)|答案(6)|浏览(291)

我已经在django中设置了我的迁移(非常小的项目);但是我有一个.sql文件,其中有一堆数据,我需要在数据库中。
在运行迁移之后/期间执行此.sql文件的最佳方法是什么(或者是否可能)?
sql文件只包含一个数据插入,如下所示:

INSERT INTO `mileages_mileages` (`id`, `miles`, `start_location`, `end_location`) VALUES
(NULL,3, 'Location 1', 'Location 2'),

我只需要在运行模型的初始迁移之后执行那个.sql文件。
这可能吗?

hm2xizp9

hm2xizp91#

migrations.RunSQL()

不接受文件作为输入。只接受原始SQL。要解决此问题,您需要使用以下任一方法:

migrations.RunSQL(
    """
    INSERT INTO 'mileages_mileages' ('id', 'miles', 'start_location', 'end_location') 
    VALUES
       (NULL,3, 'Location 1', 'Location 2');
    """)

def load_data_from_sql(apps, schema_editor):
   file_path = os.path.join(os.path.dirname(__file__), 'file_name.sql')
   sql_statement = open(file_path).read()
   with connection.cursor() as c:
       c.execute(sql_statement)

class Migration(migrations.Migration):
    dependencies = [
        ('..', '...'),
    ]

    operations = [
        migrations.RunPython(load_data_from_sql),
    ]
ahy6op9u

ahy6op9u2#

下面是将带有DELIMITER的MySQL语句转换为SQL执行的完整脚本(虽然不完美,但还是原样)。
有关SQL语句https://stackoverflow.com/a/52292690/9521312中乘法分隔符的信息

在迁移文件中添加脚本执行

下面是使用脚本的两个示例:运行sql文件或运行原始MySQL语句

from anywhere import migrate_run_sql

operations = [
              migrations.RunPython(migrate_run_sql.run_sql_file('contract_triggers.sql')),
              migrations.RunPython(migrate_run_sql.run_sql(
                                                           """
                                                           DELIMITER $$
                                                           CREATE TRIGGER trigger_name BEFORE INSERT ON table
                                                           FOR EACH ROW
                                                           BEGIN
                                                             IF NEW.number <> 'anynumber' AND NEW.number <> 'anynumber'
                                                               THEN
                                                                 SET NEW.number = 'anynumber';
                                                             END IF;
                                                           END$$
                                                           """
                                                           ))
             ]

脚本文件

# -*- coding: utf-8 -*-
from django.db import connection
import re
from StringIO import StringIO
from django.conf import settings
import os

# this function get raw MySQL statement
def run_sql(sql): 
    def load_data_from_sql(app, schema_editor):
        f = StringIO(sql)
        return _runsql(f)

    return load_data_from_sql

# this function get sql file
def run_sql_file(filename):
    def load_data_from_sql(app, schema_editor):
        filepath = os.path.join(settings.PROJECT_PATH, '../deploy/mysql/', filename)
        with open(filepath, 'rb') as f:
            return _runsql(f)

    return load_data_from_sql

# in this function content splits and checks line by line
def _runsql(f):
    with connection.cursor() as c:
        file_data = f.readlines()
        statement = ''
        delimiter = ';\n'
        for line in file_data:
            if re.findall('DELIMITER', line): # found delimiter
                if re.findall('^\s*DELIMITER\s+(\S+)\s*$', line):
                    delimiter = re.findall('^\s*DELIMITER\s+(\S+)\s*$', line)[0] + '\n'
                    continue
                else:
                    raise SyntaxError('Your usage of DELIMITER is not correct, go and fix it!')
            statement += line # add lines while not met lines with current delimiter
            if line.endswith(delimiter):
                if delimiter != ';\n':
                    statement = statement.replace(';', '; --').replace(delimiter, ';') # found delimiter, add dash symbols (or any symbols you want) for converting MySQL statements with multiply delimiters in SQL statement
                c.execute(statement) # execute current statement
                statement = '' # begin collect next statement

希望能有所帮助!

zd287kbt

zd287kbt3#

在阅读了更多内容之后,在迁移中运行SQL来加载数据是错误的,使用fixtures要好得多。
我已经添加了一个fixture,并将使用该文件引入数据。
首先,为了创建fixture,我必须将数据添加到表中(我只是手动运行SQL文件将其添加到适当的表中)。
然后我就跑了

manage.py dumpdata mileage.mileage --indent 2 --format=json --output initial_mileages.json

这创建了我的夹具,然后我加载了

manage.py loaddata initial_mileages.json

它进口时很漂亮。

yftpprvb

yftpprvb4#

使用migrations.RunSQL时,如果出现以下错误:
如果不拆分SQL,则需要sqlparse
您只需安装sqlparse即可解决此问题:

pip install sqlparse
bfrts1fy

bfrts1fy5#

您可以执行原始SQL

operations = [
    migrations.RunSQL('sql statement here')
]
xxhby3vn

xxhby3vn6#

paveldroo解决方案的灵感来自于围绕migrations.RunSQL创建一个 Package 器来加载sql文件:

# core/migrations.py
from pathlib import Path
from typing import AnyStr

from django.db import migrations

def _read_sql_file(path: Path) -> AnyStr:
    with open(path, "r") as sql_file:
        return sql_file.read()

class RunSQLFile(migrations.RunSQL):
    def __init__(
        self,
        sql_file_path: Path,
        reverse_sql=None,
        state_operations=None,
        hints=None,
        elidable=False,
    ):
        sql = _read_sql_file(sql_file_path)
        super().__init__(
            sql=sql,
            reverse_sql=reverse_sql,
            state_operations=state_operations,
            hints=hints,
            elidable=elidable,
        )
# app/models.py
class MyModelView(models.Model):
    class Meta:
        managed = False
        db_table = "my_model_view"

    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=1024)
    updated = models.DateTimeField()
-- app/sql/my_model_view.sql
DROP VIEW IF EXISTS my_model_view;

CREATE VIEW my_model_view AS (
  -- SELECT HERE
)
# app/migrations/0002_create_my_model_view.py
# Generated by Django 3.2 on 2023-03-06 22:11
from pathlib import Path

from django.db import migrations, models

from core.migrations import RunSQLFile

class Migration(migrations.Migration):

    dependencies = [
        ("0001_initial"),
    ]

    operations = [
        migrations.CreateModel(
            name="MyModelView",
            fields=[
                ("id", models.IntegerField(primary_key=True, serialize=False)),
                ("name", models.CharField(max_length=1024)),
                ("updated", models.DateTimeField()),
            ],
            options={
                "db_table": "my_model_view",
                "managed": False,
            },
        ),
        RunSQLFile(
            sql_file_path=Path(
                "./app/sql/my_model_view.sql"
            ),
            reverse_sql="DROP VIEW IF EXISTS my_model_view",
        ),
    ]

相关问题