3

我正在尝试在我的 Django 应用程序中创建一个 MySQL 触发器。我尝试按照http://bryanmarty.com/blog/2012/12/03/mysql-triggers-django/上的示例进行操作,但是当我的自定义 SQL 运行时,我没有看到在数据库中创建的触发器(使用显示触发器;)。

我的模型名为 Process,我还有另一个模型名为 Machine。这就是我的“自定义”SQL 的样子:

delimiter |
CREATE TRIGGER processes_running_count
AFTER INSERT ON jobs_process
FOR EACH ROW 
BEGIN
    UPDATE jobs_machine SET processes_running=processes_running+1 limit 1; --
END;
|
delimiter ;

运行 python manage.py syncdb 后,我可以看到存在相应的表(jobs_process、jobs_machine 等),但不存在触发器。如果我将自定义 SQL 中的代码复制/粘贴到 MySQL 提示符中,则会创建触发器(因此我认为问题不是无效的 MySQL 语法)。

有什么建议吗?

4

2 回答 2

5

在 Django 中没有规范的方法可以做到这一点,因为它打破了关于 Model-View-Whatever 范式(将业务逻辑与持久性混合)的关注点分离。在 Django 中,对于您会考虑触发器的大多数情况,您可以覆盖Model.save()and/orModel.delete()方法(也可以查看信号)。

在这种情况下,触发器是多余的——为什么要存储一个可以计算的值?只是做类似的事情Process.objects.filter(active=True).count()

“我们应该忘记小的效率,比如大约 97% 的时间:过早的优化是万恶之源” - Knuth, Donald(1974 年 12 月)。“使用 go to 语句进行结构化编程”

也就是说,您可能担心关系数据库是状态轮询的昂贵选择,因为ACID合同带来的开销 - 数百个客户端敲击jobs_machine表对于大多数服务器来说可能会有压力。像redismemcached这样的轻量级键/值存储是此类任务的流行选择(将它们视为巨大的共享字典)。

于 2013-07-02T01:40:37.170 回答
0

这是有关 SQL 语句中的多重分隔符的信息https://stackoverflow.com/a/52292690/9521312

在您的情况下,您可以使用我的脚本(不完美,因为它是),它将带有自定义 DELIMITER 的 MySQL 语句转换为原始 SQL 语句。

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

有两个使用脚本的例子:运行 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

希望它会有所帮助!

于 2018-09-12T10:35:48.190 回答