Django: while updating record: ValueError: Cannot force an update in save() with no primary key

There are similar issues on SO, but none alike this.

I would like to update a field in a record of a m2m model which has a unique constraint on attendee + training. This model is defined as (model.py):

class Occurrence(models.Model):
    attendee = models.ForeignKey(Attendee, on_delete=models.CASCADE)
    training = models.ForeignKey(Training, on_delete=models.CASCADE)
    attended_date = models.DateField(default=date(1900, 12, 31))

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['attendee', 'training'], name='unique_attendee_training')
    ]

Now, consider e.g. that John has taken the Python course and already exists as record in the db. If I try get the date of when the training occurred, I would go like this:

from trainings.models import Attendee, Training, Occurrence
from datetime import date, datetime

attendee = Attendee.objects.get(pk='john@gmail.com')
training = Training.objects.get(pk='python310')
occurrence = Occurrence.objects.get(attendee=attendee, training=training)
print(occurrence.attended_date)
# datetime.date(2021, 11, 4)

However, if I try to update the date of this record, I get the error.

occurrence = Occurrence(attendee=attendee, training=training, attended_date=date(2021, 11, 5))
occurrence.save(update_fields=["attendee", "training", "attended_date"])

The error being:

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

How do I update this record?

Note

I believe this should be enough to understand the question. But if you want to reproduce the whole issue, I post here the models (model.py) for Attendees and Trainings.

class Attendee(models.Model):
    first_name = models.CharField('First Name', max_length=30, blank=False)
    last_name = models.CharField('Last Name', max_length=30, blank=False)
    email = models.EmailField("Email Address", max_length=75, blank=False, primary_key=True)

Using email as pk.

class Training(models.Model):
    long_name = models.CharField('Training Name', max_length=80, blank=False, null=False)
    short_name = models.CharField('Abbreviation', max_length=10, blank=False, null=False, primary_key=True)
    description = models.TextField('Descriprion', blank=True)
    alumni = models.ManyToManyField(Attendee, through='Occurrence')

Using short_name as pk. With alumni that relates Attendee and Occurrence.

>Solution :

You are updating the record in the wrong way,


occurrence = Occurrence.objects.get(attendee=attendee, training=training)
print(occurrence.attended_date) # # datetime.date(2021, 11, 4)

# update the record like this
occurrence.attended_date = date(2021, 11, 5)
occurrence.save()

You can tell Django to update specific fields by passing the update_fields parameter, as

# Use instead of `occurrence.save()`
occurrence.save(update_fields=["attended_date"])

Leave a Reply