make django not insert certain fields
Asked Answered
C

1

3

I have a Django model against a postgresql database table where I want Django to not insert field_3 during object creation as its a timestamp field that DB is supposed to fill in.

class AbcModel:
  model_id = models.AutoField(primary_key=True)
  field_1 = models.BigIntegerField()
  field_2 = models.TextField()
  field_3 = models.DateTimeField(blank=True, null=True)

  class Meta:
    managed = False
    db_table = 'abc_table'

The corresponding table schema

abc_table

  • model_id integer not null default
    nextval('command_running_command_id_seq'::regclass)
  • field_1 integer not null
  • field_2 text not null
  • field_3 timestamp without time zone default now()

What I tried - I read that one should call Model.save(update_fields=[list of fields to be updated]).

class AbcModel(models.Model):
    def save(self, *args, **kwargs):
        u_fields = ['field_1', 'field_2']
        super(CommandRunning, self).save(update_fields=u_fields)

But I get an Exception

Cannot force an update in save() with no primary key.

If I include model_id in update_fields, I get following exception

The following fields do not exist in this model or are m2m fields: model_id

What am I missing here?

Cottier answered 2/3, 2017 at 9:29 Comment(2)
Why do you need the database to add the timestamp? You can just set field_3 = models.DateTimeField(auto_now_add=True)to add the date and not have to worry about finding a workaround? Or am I missing something?Commando
I don't own the database schema. I am supposed to provide only an admin interface over it. Django looked like a natural choice with its amazing inspectdb command but the model file it generates requires a bit of modification to truly align to database.Cottier
C
9

Fact: whenever one specifies update_fields, it forces save to update. This doesn't help during insert as we don't have auto-generated primary key available.

Final Solution:

there is a method called _do_insert which gets called just before the insert query is fired to the database. One can override it and remove fields which we don't want to insert

def _do_insert(self, manager, using, fields, update_pk, raw):
        return super(AbcModel, self)._do_insert(
            manager, using,
            [f for f in fields if f.attname not in ['field_3']],
            update_pk, raw)

For better understanding, try to look into the insert query generated. You will see that field_3 is missing in the insert SQL sent to the database. This allows database to insert the field using the specified default.

Cottier answered 2/3, 2017 at 13:7 Comment(2)
how about returning the field_3 value generated by db?Equipoise
I might also need to override _do_update as described here: https://mcmap.net/q/1356284/-django-tries-to-write-to-a-generated-columnPlace

© 2022 - 2024 — McMap. All rights reserved.