From 88f54ded71af515be74f671808e26e1326affe8b Mon Sep 17 00:00:00 2001
From: Benjamin Bertrand <benjamin.bertrand@esss.se>
Date: Wed, 27 Mar 2019 18:55:04 +0100
Subject: [PATCH] Fix model update in admin interface

Following exception is raised when trying to update a network from
flask-admin:
sqlalchemy.exc.InvalidRequestError: Can't attach instance <User at 0x7f6f0e39ce10>;
another instance with key (<class 'app.models.User'>, (1,), None) is already present in this session.

1. If we remove the caching of the load_user method, the problem
disappears.
2. When updating a Network, this trigger an inventory update in
before_flush which adds a task to the session. This is when the
current_user is added to the session and the error raised.

So the problem is linked to the user caching and flask-admin.

The workaround is to remove the user object added by flask-admin to the
session.

JIRA INFRA-908 #action In Progress
---
 app/admin/views.py | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/app/admin/views.py b/app/admin/views.py
index 5839cab..e954918 100644
--- a/app/admin/views.py
+++ b/app/admin/views.py
@@ -14,6 +14,7 @@ from flask_admin.contrib import sqla
 from flask_admin.model.form import converts
 from flask_login import current_user
 from ..validators import IPNetwork, ICS_ID_RE
+from .. import models
 
 
 # Monkey patch flask-admin Unique validator to disable it
@@ -47,6 +48,16 @@ class AdminModelView(sqla.ModelView):
     def is_accessible(self):
         return current_user.is_authenticated and current_user.is_admin
 
+    def update_model(self, form, model):
+        # Remove the current user object added by flask-admin from the session
+        # Adding the current_user object later raises an exception otherwise:
+        # sqlalchemy.exc.InvalidRequestError: Can't attach instance <User at 0x7f6f0e39ce10>;
+        # another instance with key (<class 'app.models.User'>, (1,), None) is already present in this session.
+        for obj in self.session:
+            if isinstance(obj, models.User) and obj.id == current_user.id:
+                self.session.expunge(obj)
+        return super().update_model(form, model)
+
 
 class UserAdmin(AdminModelView):
     can_create = False
-- 
GitLab