diff --git a/ippisite/db.sqlite3.REMOVED.git-id b/ippisite/db.sqlite3.REMOVED.git-id
index 5e73a6d903ca43ff367e62ae4a49ef63a2df33db..c97e351aaba30e6c427df69118e05e9581cc99f1 100644
--- a/ippisite/db.sqlite3.REMOVED.git-id
+++ b/ippisite/db.sqlite3.REMOVED.git-id
@@ -1 +1 @@
-a95c9b99cc69a1cae8a8fa13dfa234ef88d45f4e
\ No newline at end of file
+487393aa0af165ca4ce78c99486b57039379f535
\ No newline at end of file
diff --git a/ippisite/ippidb/migrations/0009_ppi_name.py b/ippisite/ippidb/migrations/0009_ppi_name.py
new file mode 100644
index 0000000000000000000000000000000000000000..d052b637fecee050257a8ea387c71a3d1a8422b6
--- /dev/null
+++ b/ippisite/ippidb/migrations/0009_ppi_name.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2018-07-29 10:13
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0008_auto_20180727_0845'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='ppi',
+            name='name',
+            field=models.TextField(blank=True, null=True, verbose_name='PPI name'),
+        ),
+    ]
diff --git a/ippisite/ippidb/models.py b/ippisite/ippidb/models.py
index 6ccd9d0758b20fb0d5dae0b3bc7380e9742159e6..d6eb9bf951db9d0ca30d8b56962f49a80406266f 100644
--- a/ippisite/ippidb/models.py
+++ b/ippisite/ippidb/models.py
@@ -211,22 +211,21 @@ class PpiFamily(models.Model):
         return self.name
 
 
-class Ppi(models.Model):
+class Ppi(AutoFillableModel):
     pdb_id = models.CharField('PDB ID', max_length=4, null=True, blank=True)
     pockets_nb = models.IntegerField(
         'Total number of pockets in the complex', default=1)
     symmetry = models.ForeignKey(Symmetry, models.CASCADE)
     diseases = models.ManyToManyField(Disease)
     family = models.ForeignKey(PpiFamily, models.CASCADE, null=True, blank=True)
+    name = models.TextField('PPI name', null=True, blank=True)
 
     def __str__(self):
         return '{} PPI, PDB:{}'.format(self.symmetry.description, self.pdb_id or 'unknown')
 
-    def get_ppi_complexes(self):
-        """
-        return all ppi complexes belonging to this ppi
-        """
-        return PpiComplex.objects.filter(ppi=self)
+    def autofill(self):
+        # name is denormalized and stored in the database to reduce SQL queries in query mode
+        self.name = self.compute_name_from_protein_names()
 
     def get_ppi_bound_complexes(self):
         """
@@ -235,10 +234,9 @@ class Ppi(models.Model):
         # this is the less efficient query ever seen, FIXME
         return PpiComplex.objects.filter(ppi=self, complex__in=ProteinDomainBoundComplex.objects.all())
 
-    @property
-    def name(self):
+    def compute_name_from_protein_names(self):
         all_protein_names = set(
-            [ppi_complex.complex.protein.short_name for ppi_complex in self.get_ppi_complexes()])
+            [ppi_complex.complex.protein.short_name for ppi_complex in self.ppicomplex_set.all()])
         bound_protein_names = set(
             [ppi_complex.complex.protein.short_name for ppi_complex in self.get_ppi_bound_complexes()])
         partner_protein_names = all_protein_names - bound_protein_names
@@ -614,7 +612,7 @@ class TestActivityDescription(models.Model):
         depends on the modulation type
         """
         if self.test_modulation_type == 'I':
-            return self.ppi.get_ppi_complexes()
+            return self.ppi.ppicomplex_set.all()
         else:
             return self.ppi.get_ppi_bound_complexes()