Reputation: 1972
I have a model
class Survey(models.Model):
created_by = models.ForeignKey(User)
question = models.CharField(max_length=150)
active = models.NullBooleanField()
def __unicode__(self):
return self.question
and now I want to update only the active
field. So I do this:
survey = get_object_or_404(Survey, created_by=request.user, pk=question_id)
survey.active = True
survey.save(["active"])
Now I get an error IntegrityError: PRIMARY KEY must be unique
.
Am I right with this method to update?
Upvotes: 150
Views: 181320
Reputation: 308769
To update a subset of fields, you can use update_fields
:
survey.save(update_fields=["active"])
The update_fields
argument was added in Django 1.5. In earlier versions, you could use the update()
method instead:
Survey.objects.filter(pk=survey.pk).update(active=True)
Upvotes: 284
Reputation: 2029
You can use this mixin that can be used to detect which fields have changed in a model instance and pass that information to the save method. Here is a description of the mixin:
By using this mixin in a model, you can conveniently track and save only the fields that have been modified, which can be helpful for optimizing database updates and reducing unnecessary database queries.
from django.db import models
class UpdateFieldsMixin(models.Model):
""" Detects which field changed and passes it to save method """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
fields = (field.name for field in self._meta.fields)
self.old_instance_data = {name: getattr(self, name, None)
for name in fields}
def save(self, update_fields=None, **kwargs):
if self.pk:
update_fields = update_fields or {
k for k, v in self.old_instance_data.items()
if getattr(self, k, None) != v}
return super().save(update_fields=update_fields, **kwargs)
class Meta:
abstract = True
Use it like:
class YourModel(UpdateFieldsMixin, models.Model):
# Any code
def save(**kwargs):
# Any code
return super().save(**kwargs)
That's all. If you have a custom save method, make sure you call UpdateFieldsMixin.save. I'd suggest using super() like in the example above.
Upvotes: 0
Reputation: 9584
Usually, the correct way of updating certain fields in one or more model instances is to use the update()
method on the respective queryset. Then you do something like this:
affected_surveys = Survey.objects.filter(
# restrict your queryset by whatever fits you
# ...
).update(active=True)
This way, you don't need to call save()
on your model anymore because it gets saved automatically. Also, the update()
method returns the number of survey instances that were affected by your update.
Upvotes: 22