I have a django abstract model that goes like this:
class AbstractAppendOnly(models.Model):
created_at = models.DateTimeField(auto_now=True, editable=False)
db_note = models.TextField(default=None, null=True)
class Meta:
abstract = True
This model is extended to multiple other models so that a create_at
field is automatically added to all of those. Now when an object is created and saved from django server end the created_at
timestamp field is automatically produced, as expected. But it does not enforce it in database level, so anyone can insert a row with fake created_at
value.
As far as I am concerned django does not let user to set database level default value for model issue-470.
What I have found that may solve the issue -
I have found an way to customize the migration files using tool like django-add-default-value . But since migrations may sometimes needed to be pruned in big systems and we will have to write customized migrations every time we create a new model, it seems of huge error-prone.
Another way I have thought of is to add trigger using django-pgtrigger like this
@pgtrigger.register(
pgtrigger.Trigger(
name='insert_auto_created_at_timestamp',
operation=pgtrigger.Insert,
when=pgtrigger.Before,
func='''
new.created_at = now();
return new;
'''
)
)
class AbstractAppendOnly(models.Model):
created_at = models.DateTimeField(auto_now=True, editable=False)
db_note = models.TextField(default=None, null=True)
class Meta:
abstract = True
But doing so throws error while running migrations
Traceback (most recent call last):
File "/run/media/shafi/Codes/Python/tkdc/./manage.py", line 22, in <module>
main()
File "/run/media/shafi/Codes/Python/tkdc/./manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/__init__.py", line 401, in execute_from_command_line
utility.execute()
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/__init__.py", line 395, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/base.py", line 330, in run_from_argv
self.execute(*args, **cmd_options)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/base.py", line 371, in execute
output = self.handle(*args, **options)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/base.py", line 85, in wrapped
res = handle_func(*args, **kwargs)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 267, in handle
emit_post_migrate_signal(
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/core/management/sql.py", line 48, in emit_post_migrate_signal
models.signals.post_migrate.send(
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 177, in send
return [
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/dispatch/dispatcher.py", line 178, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/pgtrigger/apps.py", line 9, in install
pgtrigger.install()
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/pgtrigger/core.py", line 846, in install
trigger.install(model)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/pgtrigger/core.py", line 621, in install
cursor.execute(rendered_comment)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 98, in execute
return super().execute(sql, params)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 66, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 82, in _execute
return self.cursor.execute(sql)
File "/run/media/shafi/Codes/Python/tkdc/venv/lib/python3.9/site-packages/pgconnection/core.py", line 85, in execute
return super().execute(sql, args)
django.db.utils.ProgrammingError: relation "utils_abstractappendonly" does not exist
Which I think is because AbstractAppendOnly
is an abstract class, and the trigger translates directly to the Model it is attached to and not to the inherited class. Therefore one solution would to add the trigger to every model that extends the class.
I would be grateful if someone could point me out a solution.