即使我在保存时设置了 django 模型的值,我的 postgresql 触发器也会执行吗?
will my postgresql trigger execute even if I set the value from django model at save?
我在每一行 INSERT/UPDATE 上创建了一个触发器来分别设置 created_at 和 updated_at。
触发器:
CREATE OR REPLACE FUNCTION insert_update_function() RETURNS trigger AS $BODY$
BEGIN
-- CHECKING OPERATION TYPE AND DECIDE COLUMN
IF TG_OP = 'INSERT' THEN
NEW.created_at := current_timestamp;
ELSE
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END; $BODY$ LANGUAGE plpgsql VOLATILE;
仅应用触发器 table 其中 updated_at 列存在
DO $$
DECLARE
t text;
BEGIN
FOR t IN
SELECT table_name FROM information_schema.columns WHERE column_name = 'updated_at'
LOOP
EXECUTE format('CREATE TRIGGER insert_update_trigger
BEFORE INSERT OR UPDATE ON %I
FOR EACH ROW EXECUTE PROCEDURE insert_update_function()', t,t);
END loop;
END;
$$ language plpgsql;
因此,当我从原始 SQL 执行 INSERT/UPDATE 时,我不提供这两列,即 created_at 和 updated_at,它会自动触发并添加到我的table 这是预期的并且很好但是现在 我的查询是当我尝试通过我设置的 Django BaseModel INSERT/UPDATE 行的 table created_at和updated_at,触发器还会被调用吗?
Django 带有自定义保存的 BaseModel()
class TimeStampedModel(models.Model):
"""
An abstract base class model that provides self-updating
``created_at`` and ``modified_at`` fields.
"""
created_at = AutoCreatedField('created_at')
updated_at = AutoLastModifiedField('updated_at')
def save(self, *args, **kwargs):
"""
Overriding the save method in order to make sure that
updated_at field is updated even if it is not given as
a parameter to the update field argument.
"""
update_fields = kwargs.get('update_fields', None)
if update_fields:
kwargs['update_fields'] = set(update_fields).union({'updated_at'})
super().save(*args, **kwargs)
class Meta:
abstract = True
是的,它会被调用。我建议您为 created_at 字段创建默认值,如果 django 不提供该字段的值,它将设置值 current_timestamp
。
这是示例代码;
CREATE OR REPLACE FUNCTION public.insert_update_function()
RETURNS trigger AS
$BODY$
BEGIN
-- CHECKING OPERATION TYPE AND DECIDE COLUMN
IF TG_OP = 'INSERT' THEN
-- if created_at comes null from application code then set current timestamp
IF NEW.created_at IS NULL THEN
NEW.created_at = current_timestamp;
END IF;
ELSE
-- if updated_at is not provided in application code its value
-- will be equal to its old value even its `null`
-- then we set current timestamp
-- else nothing to do
IF OLD.updated_at IS NOT DISTINCT FROM NEW.updated_at THEN
NEW.updated_at = current_timestamp;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql
VOLATILE;
DO LANGUAGE plpgsql
$$
DECLARE
r record;
BEGIN
-- set default value for all tables which has created_at column
FOR r IN
SELECT table_schema s, table_name t FROM information_schema.columns WHERE column_name = 'created_at'
LOOP
-- set default value
EXECUTE format('ALTER TABLE %I.%I ALTER created_at SET DEFAULT CURRENT_TIMESTAMP', r.s, r.t);
-- fill empty created_at values with current timestamp
EXECUTE format('UPDATE %I.%I SET created_at = CURRENT_TIMESTAMP WHERE created_at IS NULL', r.s, r.t);
-- created_at cannot be not null anymore
EXECUTE format('ALTER TABLE %I.%I ALTER created_at SET NOT NULL', r.s, r.t);
END loop;
-- add update trigger for updated_at field.
FOR r IN
SELECT table_schema s, table_name t FROM information_schema.columns WHERE column_name = 'updated_at'
LOOP
EXECUTE format('CREATE TRIGGER %I BEFORE INSERT OR UPDATE ON %I.%I FOR EACH ROW EXECUTE PROCEDURE public.insert_update_function()',
r.t || '_insert_update_trigger',r.s,r.t,r.t);
END loop;
END;
$$;
我在每一行 INSERT/UPDATE 上创建了一个触发器来分别设置 created_at 和 updated_at。
触发器:
CREATE OR REPLACE FUNCTION insert_update_function() RETURNS trigger AS $BODY$
BEGIN
-- CHECKING OPERATION TYPE AND DECIDE COLUMN
IF TG_OP = 'INSERT' THEN
NEW.created_at := current_timestamp;
ELSE
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END; $BODY$ LANGUAGE plpgsql VOLATILE;
仅应用触发器 table 其中 updated_at 列存在
DO $$
DECLARE
t text;
BEGIN
FOR t IN
SELECT table_name FROM information_schema.columns WHERE column_name = 'updated_at'
LOOP
EXECUTE format('CREATE TRIGGER insert_update_trigger
BEFORE INSERT OR UPDATE ON %I
FOR EACH ROW EXECUTE PROCEDURE insert_update_function()', t,t);
END loop;
END;
$$ language plpgsql;
因此,当我从原始 SQL 执行 INSERT/UPDATE 时,我不提供这两列,即 created_at 和 updated_at,它会自动触发并添加到我的table 这是预期的并且很好但是现在 我的查询是当我尝试通过我设置的 Django BaseModel INSERT/UPDATE 行的 table created_at和updated_at,触发器还会被调用吗?
Django 带有自定义保存的 BaseModel()
class TimeStampedModel(models.Model):
"""
An abstract base class model that provides self-updating
``created_at`` and ``modified_at`` fields.
"""
created_at = AutoCreatedField('created_at')
updated_at = AutoLastModifiedField('updated_at')
def save(self, *args, **kwargs):
"""
Overriding the save method in order to make sure that
updated_at field is updated even if it is not given as
a parameter to the update field argument.
"""
update_fields = kwargs.get('update_fields', None)
if update_fields:
kwargs['update_fields'] = set(update_fields).union({'updated_at'})
super().save(*args, **kwargs)
class Meta:
abstract = True
是的,它会被调用。我建议您为 created_at 字段创建默认值,如果 django 不提供该字段的值,它将设置值 current_timestamp
。
这是示例代码;
CREATE OR REPLACE FUNCTION public.insert_update_function()
RETURNS trigger AS
$BODY$
BEGIN
-- CHECKING OPERATION TYPE AND DECIDE COLUMN
IF TG_OP = 'INSERT' THEN
-- if created_at comes null from application code then set current timestamp
IF NEW.created_at IS NULL THEN
NEW.created_at = current_timestamp;
END IF;
ELSE
-- if updated_at is not provided in application code its value
-- will be equal to its old value even its `null`
-- then we set current timestamp
-- else nothing to do
IF OLD.updated_at IS NOT DISTINCT FROM NEW.updated_at THEN
NEW.updated_at = current_timestamp;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql
VOLATILE;
DO LANGUAGE plpgsql
$$
DECLARE
r record;
BEGIN
-- set default value for all tables which has created_at column
FOR r IN
SELECT table_schema s, table_name t FROM information_schema.columns WHERE column_name = 'created_at'
LOOP
-- set default value
EXECUTE format('ALTER TABLE %I.%I ALTER created_at SET DEFAULT CURRENT_TIMESTAMP', r.s, r.t);
-- fill empty created_at values with current timestamp
EXECUTE format('UPDATE %I.%I SET created_at = CURRENT_TIMESTAMP WHERE created_at IS NULL', r.s, r.t);
-- created_at cannot be not null anymore
EXECUTE format('ALTER TABLE %I.%I ALTER created_at SET NOT NULL', r.s, r.t);
END loop;
-- add update trigger for updated_at field.
FOR r IN
SELECT table_schema s, table_name t FROM information_schema.columns WHERE column_name = 'updated_at'
LOOP
EXECUTE format('CREATE TRIGGER %I BEFORE INSERT OR UPDATE ON %I.%I FOR EACH ROW EXECUTE PROCEDURE public.insert_update_function()',
r.t || '_insert_update_trigger',r.s,r.t,r.t);
END loop;
END;
$$;