[Patchew-devel] [PATCH] models: avoid IntegrityError when clearing log property

Caio Carrara ccarrara at redhat.com
Wed Nov 28 15:40:45 UTC 2018


Hello, Paolo.

Just some questions.

On Wed, Nov 28, 2018 at 03:44:59PM +0100, Paolo Bonzini wrote:
> Result's log property provides some magic to hide the existence of
> the LogEntry table (which in turn is only there to optimize access
> to results, since the logs are rarely needed).
> 
> However, deleting the entry directly in the setter does not work,
> because at this point the entry is still referenced in the Result
> table.  Postgres complains about a foreign key constraint violation
> when the setter calls entry.delete().
> 
> To fix this, which shows up as an error in the git-reset action,
> detect at save time whether the log_entry became None, and if so
> delete it _after_ saving the model.  While I am touching the code,
> fix the override of save() to correctly pass the arguments to the
> superclass.
> ---
>  api/models.py | 31 ++++++++++++++++++-------------
>  1 file changed, 18 insertions(+), 13 deletions(-)
> 
> diff --git a/api/models.py b/api/models.py
> index e24098b..8ec89b6 100644
> --- a/api/models.py
> +++ b/api/models.py
> @@ -74,11 +74,19 @@ class Result(models.Model):
>      def is_running(self):
>          return self.status == self.RUNNING
>  
> -    def save(self):
> +    def save(self, *args, **kwargs):
>          self.last_update = datetime.datetime.utcnow()
>          old_result = Result.objects.filter(pk=self.pk).first()
>          old_status = old_result.status if old_result else None
> -        super(Result, self).save()
> +        old_entry = old_result.log_entry if old_result else None
> +        super(Result, self).save(*args, **kwargs)
> +
> +        if self.log_entry is None and old_entry is not None:
> +            # Quick way to check if the field was actually saved to the database
> +            new_result = Result.objects.filter(pk=self.pk).first()
> +            if new_result.log_entry is None:
> +                old_entry.delete()
> +
>          emit_event("ResultUpdate", obj=self.obj,
>                     old_status=old_status, result=self)
>  
> @@ -109,18 +117,15 @@ class Result(models.Model):
>  
>      @log.setter
>      def log(self, value):
> -        entry = self.log_entry
>          if value is None:
> -            if entry is not None:
> -                self.log_entry = None

What if you just call self.save() here before calling entry.delete(),
whouldn't work?

> -                entry.delete()
> -        else:
> -            if entry is None:
> -                entry = LogEntry()
> -            entry.data = value
> -            entry.save()
> -            if self.log_entry is None:
> -                self.log_entry = entry
> +            self.log_entry = None

Shouldn't you have to save the model before returning here?

> +            return
> +
> +        entry = self.log_entry or LogEntry()
> +        entry.data = value
> +        entry.save()

The way the check is done here you can create a new log entry and do not
associate it with the Result. Right? Because you're checking the current
self.log_entry after creating the new one.

> +        if self.log_entry is None:
> +            self.log_entry = entry
>  
>      def get_log_url(self, request=None):
>          if not self.is_completed() or self.renderer is None:
> -- 
> 2.19.1
> 
> _______________________________________________
> Patchew-devel mailing list
> Patchew-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/patchew-devel

-- 
Caio Carrara
Software Engineer, Virt Team - Red Hat
ccarrara at redhat.com




More information about the Patchew-devel mailing list