views:

154

answers:

1

I have a problem when I update an object. This is the model:

class HourRecord(models.Model):
    day_of_work = models.PositiveIntegerField(max_length=1)
    date_of_work = models.DateField(verbose_name='creation date')
    who_worked = models.ForeignKey(User)
    project = models.ForeignKey(Project, related_name='hour_record_set', null=True)

    created_by = models.ForeignKey(User, editable=False, related_name='hour_record_creator')
    created = models.DateTimeField(auto_now_add=True, editable=False, verbose_name='creation date')
    modified_by = models.ForeignKey(User, editable=False, related_name='hour_record_modifier')
    modified = models.DateTimeField(auto_now=True, editable=False)


    def save(self, request):
        if not self.id:
            self.who_worked = request.user
            self.created_by = request.user
        self.modified_by = request.user
        super(HourRecord, self).save()

The created_by field should only be set if its a new object. Therefore the "if not self.id:". This worked fine till now.

Till now I updated the object like this:

if hrform.is_valid():
            hour_record = hrform.save(commit=False)
            hour_record.save(request)

But when I update the model like this I get an error:

project = Project.objects.get(id=project_pk)
hr_object = HourRecord(id=hour_record_pk, day_of_work=weekday, date_of_work=date, who_worked=request.user, project=project)
hr_object.save(request)

The error message is:

Column 'created_by_id' cannot be null

Thats strange to me since the created_by column has been already set. I checked it in the database. After this error message when I check again the object is updated and created_by_id is set to null. Actually this is strange. I get an error message + the row is updated.

The second approach is called from within a json data handling view. But I don't think that it has anything to do with it. I think I can circumvent this problem when I reset the created_by_id, but that is not what I want to do, since it corrupts the logic of this created_by field.

+1  A: 

Well, the issue appears to be that you are explicitly setting the id field when you instantiate the HourRecord object:

hr_object = HourRecord(id=hour_record_pk,...

So when you get to the save() method, it checks if it has an ID... and it does, so it doesn't set who_worked and created_by.

I wonder why you need to set the ID. Normally, you should let the database set it automatically via the autoincrement.

(Not related to your issue, but your save method should accept the force_update and force_insert parameters, and pass them to the super method. The easiest way to do this is to get into the habit of always using *args, **kwargs when overriding a method.)

Edit: To answer your question of why this doesn't work like an update statement, it's because you're explicitly replacing the old object with a new one. In effect, you're deleting the old db entry and inserting a completely new set of data. There'd be no way for Python to know whether you intended to keep a field's old value, or set it to NULL, for example.

The way to do this, as you have noted, is to get the existing object and updating its attributes explicitly.

Daniel Roseman
I want to update/overwrite the object. Therefore I give the ID to the model constructor. Is this the wrong approach? I'm following the approach of the django docs: http://docs.djangoproject.com/en/dev/ref/models/instances/#explicitly-specifying-auto-primary-key-values
Tom Tom
That's fine, but in that case you will also need to set `created_by` automatically when you instantiate the object, as it won't be done in your `save` method.
Daniel Roseman
Ok but I'm just wondering why it behaves not like an sql update statement. I'm not an expert in sql either, but I think the update statement only updates the fields provided and leave the others as they are. That is what I would expect. Is there not a django equivalent to this behaviour?
Tom Tom
I think I just have to get the HourRecord object and then modify the values that I want to modify with HourRecord.get_object_or_404(pk=hour_record_pk), then change the fields and then save it. This way I think I getting around my problem. But still I would expect HourRecord(id=hour_record_pk,..) behave different. But there are probably good reasons that it does not behave I would expect it ;-)
Tom Tom
See my edit above.
Daniel Roseman
Perfect, thanks! Now it's clear!
Tom Tom