Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Django from updating identity column in MSSQL

I'm working with a legacy DB in MSSQL. We have a table that has two columns that are causing me problems:

class Emp(models.Model):  
    empid = models.IntegerField(_("Unique ID"), unique=True, db_column=u'EMPID')  
    ssn = models.CharField(_("Social security number"), max_length=10, primary_key=True, db_column=u'SSN') # Field name made lowercase.  

So the table has the ssn column as primary key and the relevant part of the SQL-update code generated by django is this:

UPDATE [EMP] SET [EMPID] = 399, 
......... 
WHERE [EMP].[SSN] = 2509882579 

The problem is that EMP.EMPID is an identity field in MSSQL and thus pyodbc throws this error whenever I try to save changes to an existing employee:

ProgrammingError: ('42000', "[42000] [Microsoft][SQL Native Client][SQL Server]C
annot update identity column 'EMPID'. (8102) (SQLExecDirectW); [42000] [Microsof
t][SQL Native Client][SQL Server]Statement(s) could not be prepared. (8180)")

Having the EMP.EMPID as identity is not crucial to anything the program, so dropping it by creating a temporary column and copying, deleting, renaming seems like the logical thing to do. This creates one extra step in transferring old customers into Django, so my question is, is there any way to prevent Django from generating the '[EMPID] = XXX' snippet whenever I'm doing an update on this table?

EDIT
I've patched my model up like this:

def save(self, *args, **kwargs):
    if self.empid:
        self._meta.local_fields = [f for f in self._meta.local_fields if f.name != 'empid']
        super().save(*args, **kwargs)

This works, taking advantage of the way Django populates it's sql-sentence in django/db/models/base.py (525). If anyone has a better way or can explain why this is bad practice I'd be happy to hear it!

like image 466
Sindri Guðmundsson Avatar asked Aug 23 '11 09:08

Sindri Guðmundsson


1 Answers

This question is old and Sindri found a workable solution, but I wanted to provide a solution that I've been using in production for a few years that doesn't require mucking around in _meta.

I had to write a web application that integrated with an existing business database containing many computed fields. These fields, usually computing the status of the record, are used with almost every object access across the entire application and Django had to be able to work with them.

These types of fields are workable with a model manager that adds the required fields on to the query with an extra(select=...).

ComputedFieldsManager code snippet: https://gist.github.com/manfre/8284698

class Emp(models.Model):
    ssn = models.CharField(_("Social security number"), max_length=10, primary_key=True, db_column=u'SSN') # Field name made lowercase.

    objects = ComputedFieldsManager(computed_fields=['empid'])


# the empid is added on to the model instance
Emp.objects.all()[0].empid

# you can also search on the computed field
Emp.objects.all().computed_field_in('empid', [1234])
like image 79
Manfre Avatar answered Sep 22 '22 13:09

Manfre