- Author:
- jcrocholl
- Posted:
- November 22, 2007
- Language:
- Python
- Version:
- .96
- Score:
- 4 (after 4 ratings)
Watch out! Previous versions of this snippet (without the values list) were vulnerable to SQL injection attacks. The "correct" solution is probably to wait until ticket 4102 hits the trunk. But here's my temporary fix while we wait for that happy day.
Django's model.save() method is a PITA:
- It does a SELECT first to see if the instance is already in the database.
- It has additional database performance overhead because it writes all fields, not just the ones that have been changed.
- It overwrites other model fields with data that may be out of date. This is a real problem in concurrent applications, like almost all web apps.
If you just want to update a field or two on a model instance which is already in the database, try this:
update_fields(user,
email='[email protected]',
is_staff=True,
last_login=datetime.now())
Or you can add it to your models (see below) and then do this:
user.update_fields(
email='[email protected]',
is_staff=True,
last_login=datetime.now())
To add it to your model, put it in a module called granular_update, then write this in your models.py:
import granular_update
class User(models.Model):
email = models.EmailField()
is_staff = models.BooleanField()
last_login = models.DateTimeField()
update_fields = granular_update.update_fields
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | from django.db import models, connection, transaction
def update_fields(self, **kwargs):
"""
Update selected model fields in the database, but leave the other
fields alone. Use this rather than model.save() for performance
and data consistency.
"""
sql = ['UPDATE', connection.ops.quote_name(self._meta.db_table), 'SET']
values = []
for field_name in kwargs:
setattr(self, field_name, kwargs[field_name])
field = self._meta.get_field(field_name)
value = field.get_db_prep_save(kwargs[field_name])
if isinstance(value, models.Model):
value = value.id
sql.extend((connection.ops.quote_name(field.column), '=', '%s', ','))
values.append(value)
sql.pop(-1) # Remove the last comma
sql.extend(['WHERE', 'id', '=', '%s'])
values.append(self.id)
sql = ' '.join(sql)
connection.cursor().execute(sql, values)
transaction.commit_unless_managed()
update_fields.alters_data = True
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 8 months ago
Comments
Too lengthy.
The same functionality in one line:
#
#
Please login first before commenting.