diff --git a/.travis.yml b/.travis.yml
index 25e99cbb80e0a6f0c14b57c893064f94cd5e177c..cb3d3ad2cdc2b2892ef1bfd6dd11a6b6e63dc6b9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,6 @@ script:
 - py.test memopol representatives_positions representatives_recommendations
 - rm -rf db.sqlite
 - django-admin migrate
-- django-admin update_score
 after_success:
 - codecov
 deploy:
diff --git a/bin/update_all b/bin/update_all
index 5899c9c1d257bf97c5dd9b233b09a3859535eadb..2447b218e987ffc3dda2a4fdc5d109c74c609b57 100755
--- a/bin/update_all
+++ b/bin/update_all
@@ -10,7 +10,3 @@ bin/update_dossiers
 sleep 120
 
 bin/update_votes
-
-sleep 120
-
-bin/update_scores
diff --git a/bin/update_scores b/bin/update_scores
deleted file mode 100755
index a3234bc6b047aacad51c2ac82df886f70a6b63ed..0000000000000000000000000000000000000000
--- a/bin/update_scores
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-set -ex
-
-source ${OPENSHIFT_REPO_DIR}bin/lib.sh
-
-[ -n "$OPENSHIFT_REPO_DIR" ] && cd $OPENSHIFT_REPO_DIR
-./manage.py update_score
diff --git a/docs/api.rst b/docs/api.rst
index 5621a2d55a6182a559a4d46205d7d7814e130bec..73388fff32b88cab6e76b99a5fcade109afe6e99 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -146,12 +146,12 @@ The following fields are available for filtering:
 * ``weight`` (alt.: gte, lte)
 * ``search``: searches in the ``title`` and ``description`` fields
 
-Scored Votes
-------------
+Vote Scores
+-----------
 
-The ``/api/scored_votes/[<pk>/]`` endpoints give access to scored votes; that
-is, representative votes with their contribution to the representative score.
-Only votes that match a recommendation are visible using this endpoint.  The
+The ``/api/vote_scores/[<pk>/]`` endpoints give access to scored votes; that is,
+representative votes with their contribution to the representative score. Only
+votes that match a recommendation are visible using this endpoint.  The
 following fields are available for filtering:
 
 * ``representative``
diff --git a/memopol/api.py b/memopol/api.py
index 2986d2afba16ca9323dca8b6603f8117f3ea5ae4..40ae7fc6a33ed651a5ecea45fa0ed2817e7f585e 100644
--- a/memopol/api.py
+++ b/memopol/api.py
@@ -17,7 +17,7 @@ from representatives_recommendations.api import (
     DossierScoreViewSet,
     RecommendationViewSet,
     RepresentativeScoreViewSet,
-    ScoredVoteViewSet
+    VoteScoreViewSet
 )
 
 
@@ -32,5 +32,5 @@ router.register(r'proposals', ProposalViewSet)
 router.register(r'recommendations', RecommendationViewSet)
 router.register(r'representatives', RepresentativeViewSet)
 router.register(r'scores', RepresentativeScoreViewSet)
-router.register(r'scored_votes', ScoredVoteViewSet)
+router.register(r'vote_scores', VoteScoreViewSet)
 router.register(r'votes', VoteViewSet)
diff --git a/memopol/fixtures/one_representative.json b/memopol/fixtures/one_representative.json
index 6997e4934d99295d781ff96857b6ed2550d064f3..c260ea3bc586f77f7080d471becb1e3817954c0c 100644
--- a/memopol/fixtures/one_representative.json
+++ b/memopol/fixtures/one_representative.json
@@ -1096,13 +1096,6 @@
     "model": "representatives_votes.vote",
     "pk": 25535
 },
-{
-    "fields": {
-        "score": -7
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 160
-},
 {
     "fields": {
         "proposal": 5744,
diff --git a/memopol/fixtures/smaller_sample.json b/memopol/fixtures/smaller_sample.json
index 5c00da36d59bc8d097b0c28470247e40ca7613e8..7b58065f1ff35ed3b5c676c0d188fdb01b5f3391 100644
--- a/memopol/fixtures/smaller_sample.json
+++ b/memopol/fixtures/smaller_sample.json
@@ -24036,258 +24036,6 @@
     "model": "representatives_votes.vote",
     "pk": 25535
 },
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 717
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 718
-},
-{
-    "fields": {
-        "score": -10
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 439
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 748
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 744
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 681
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 684
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 719
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 692
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 736
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 678
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 738
-},
-{
-    "fields": {
-        "score": 10
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 708
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 699
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 743
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 646
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 679
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 705
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 655
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 746
-},
-{
-    "fields": {
-        "score": -7
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 160
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 642
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 645
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 650
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 670
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 747
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 644
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 750
-},
-{
-    "fields": {
-        "score": 5
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 282
-},
-{
-    "fields": {
-        "score": 3
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 647
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 651
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 653
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 661
-},
-{
-    "fields": {
-        "score": -15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 666
-},
-{
-    "fields": {
-        "score": 15
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 697
-},
-{
-    "fields": {
-        "score": -7
-    },
-    "model": "representatives_recommendations.representativescore",
-    "pk": 614
-},
 {
     "fields": {
         "proposal": 5744,
diff --git a/memopol/views.py b/memopol/views.py
index e0f26602bcd345bc3ce32d00f2381e881bdfa6d7..9e0c6e3dba284fd2843b33eecdc50faf97a3bc46 100644
--- a/memopol/views.py
+++ b/memopol/views.py
@@ -7,7 +7,7 @@ from representatives.models import Representative
 from representatives_votes import views as representatives_votes_views
 from representatives_votes.models import Dossier, Proposal
 from representatives_positions.forms import PositionForm
-from representatives_recommendations.models import ScoredVote
+from representatives_recommendations.models import VoteScore
 
 
 class RepresentativeList(
@@ -42,7 +42,7 @@ class RepresentativeDetail(representatives_views.RepresentativeDetail):
 
     def get_queryset(self):
         qs = super(RepresentativeDetail, self).get_queryset()
-        votes = ScoredVote.objects.filter(
+        votes = VoteScore.objects.filter(
             proposal__in=Proposal.objects.exclude(recommendation=None),
         ).select_related('proposal__recommendation')
         qs = qs.prefetch_related(models.Prefetch('votes', queryset=votes))
diff --git a/representatives_recommendations/api.py b/representatives_recommendations/api.py
index f33575b92cc9e8f5aa3aff3d6937990fef450448..4d035c26a02fe451401ecb05436425dcc325c53b 100644
--- a/representatives_recommendations/api.py
+++ b/representatives_recommendations/api.py
@@ -9,14 +9,14 @@ from .models import (
     DossierScore,
     Recommendation,
     RepresentativeScore,
-    ScoredVote
+    VoteScore
 )
 
 from .serializers import (
     DossierScoreSerializer,
     RecommendationSerializer,
     RepresentativeScoreSerializer,
-    ScoredVoteSerializer
+    VoteScoreSerializer
 )
 
 
@@ -85,12 +85,12 @@ class RepresentativeScoreViewSet(viewsets.ReadOnlyModelViewSet):
     serializer_class = RepresentativeScoreSerializer
 
 
-class ScoredVoteViewSet(viewsets.ReadOnlyModelViewSet):
+class VoteScoreViewSet(viewsets.ReadOnlyModelViewSet):
     """
     API endpoint to view votes with their score impact.
     This endpoint only shows votes that have a matching recommendation.
     """
-    queryset = ScoredVote.objects.select_related(
+    queryset = VoteScore.objects.select_related(
         'representative',
         'proposal',
         'proposal__dossier',
@@ -112,4 +112,4 @@ class ScoredVoteViewSet(viewsets.ReadOnlyModelViewSet):
     }
 
     pagination_class = DefaultWebPagination
-    serializer_class = ScoredVoteSerializer
+    serializer_class = VoteScoreSerializer
diff --git a/representatives_recommendations/management/commands/update_score.py b/representatives_recommendations/management/commands/update_score.py
deleted file mode 100644
index cbf2452fac8922d1bbaf040d4eee5a07f66d85ef..0000000000000000000000000000000000000000
--- a/representatives_recommendations/management/commands/update_score.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.core.management.base import BaseCommand
-
-from representatives_recommendations.models import (RepresentativeScore,
-    calculate_representative_score)
-
-
-class Command(BaseCommand):
-    def handle(self, *args, **options):
-        for score in RepresentativeScore.objects.all():
-            score.score = calculate_representative_score(
-                score.representative)
-            score.save()
diff --git a/representatives_recommendations/migrations/0003_votescore.py b/representatives_recommendations/migrations/0003_votescore.py
new file mode 100644
index 0000000000000000000000000000000000000000..7fee085cf371c5afbb805b2d9f07f2a11f841e82
--- /dev/null
+++ b/representatives_recommendations/migrations/0003_votescore.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('representatives_recommendations', '0002_dossierscore'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='VoteScore',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('position', models.CharField(max_length=10)),
+                ('score', models.IntegerField(default=0)),
+            ],
+            options={
+                'ordering': ['proposal__datetime'],
+                'db_table': 'representatives_recommendations_votescores',
+                'managed': False,
+            },
+        ),
+        migrations.DeleteModel(
+            name='ScoredVote',
+        ),
+        migrations.RunSQL(
+            """
+            CREATE VIEW "representatives_recommendations_votescores"
+            AS SELECT
+                "representatives_votes_vote"."id",
+                "representatives_votes_vote"."position",
+                "representatives_votes_vote"."proposal_id",
+                "representatives_votes_vote"."representative_id",
+                CASE WHEN "representatives_votes_vote"."position" = ("representatives_recommendations_recommendation"."recommendation")
+                    THEN "representatives_recommendations_recommendation"."weight"
+                    ELSE (0 - "representatives_recommendations_recommendation"."weight")
+                END AS "score"
+            FROM "representatives_votes_vote"
+            INNER JOIN "representatives_votes_proposal"
+                ON ( "representatives_votes_vote"."proposal_id" = "representatives_votes_proposal"."id" )
+            LEFT OUTER JOIN "representatives_recommendations_recommendation"
+                ON ( "representatives_votes_proposal"."id" = "representatives_recommendations_recommendation"."proposal_id" )
+            WHERE "representatives_recommendations_recommendation"."id" IS NOT NULL
+            """
+        )
+    ]
diff --git a/representatives_recommendations/migrations/0004_dossierscore_rewrite.py b/representatives_recommendations/migrations/0004_dossierscore_rewrite.py
new file mode 100644
index 0000000000000000000000000000000000000000..6879a9660dbeaaae34614fcb96ff2e4c924e89ab
--- /dev/null
+++ b/representatives_recommendations/migrations/0004_dossierscore_rewrite.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('representatives_recommendations', '0003_votescore'),
+    ]
+
+    operations = [
+    	migrations.RunSQL(
+    		"""
+    		DROP VIEW "representatives_recommendations_dossierscores"
+    		"""
+    	),
+        migrations.RunSQL(
+            """
+            CREATE VIEW "representatives_recommendations_dossierscores"
+            AS SELECT
+                "representatives_recommendations_votescores"."representative_id" || ':' || "representatives_votes_proposal"."dossier_id" AS "id",
+                "representatives_recommendations_votescores"."representative_id",
+                "representatives_votes_proposal"."dossier_id",
+                SUM("representatives_recommendations_votescores"."score") AS "score"
+            FROM "representatives_recommendations_votescores"
+            INNER JOIN "representatives_votes_proposal"
+                ON ( "representatives_recommendations_votescores"."proposal_id" = "representatives_votes_proposal"."id" )
+            GROUP BY
+                "representatives_recommendations_votescores"."representative_id",
+                "representatives_votes_proposal"."dossier_id"
+            """
+        ),
+    ]
diff --git a/representatives_recommendations/migrations/0005_representativescore.py b/representatives_recommendations/migrations/0005_representativescore.py
new file mode 100644
index 0000000000000000000000000000000000000000..292af629c52c6a9d97fe2e960454fa9ab5011c92
--- /dev/null
+++ b/representatives_recommendations/migrations/0005_representativescore.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('representatives_recommendations', '0004_dossierscore_rewrite'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='representativescore',
+            options={
+                'managed': False,
+                'db_table': 'representatives_recommendations_representativescore',
+            },
+        ),
+        migrations.RunSQL(
+            """
+            DROP TABLE "representatives_recommendations_representativescore"
+            """
+        ),
+        migrations.RunSQL(
+            """
+            CREATE VIEW "representatives_recommendations_representativescore"
+            AS SELECT
+                "representatives_representative"."id" as "representative_id",
+                COALESCE(SUM("representatives_recommendations_votescores"."score"), 0) AS "score"
+            FROM
+                "representatives_representative"
+                LEFT OUTER JOIN "representatives_recommendations_votescores"
+                    ON "representatives_recommendations_votescores"."representative_id" = "representatives_representative"."id"
+            GROUP BY "representatives_representative"."id"
+            """
+        )
+    ]
diff --git a/representatives_recommendations/models.py b/representatives_recommendations/models.py
index 49a6ecf59c999f0f2102972bd39985cf3e60c609..5f6e64efe891a785f413500a25d479ba30d2a8c4 100644
--- a/representatives_recommendations/models.py
+++ b/representatives_recommendations/models.py
@@ -1,7 +1,5 @@
 # coding: utf-8
 from django.db import models
-from django.db.models.signals import post_save
-from django.utils.functional import cached_property
 
 from representatives_votes.contrib.parltrack.import_votes import \
     vote_pre_import
@@ -23,11 +21,29 @@ class DossierScore(models.Model):
         db_table = 'representatives_recommendations_dossierscores'
 
 
+class VoteScore(models.Model):
+    proposal = models.ForeignKey(Proposal, related_name='votescores')
+
+    representative = models.ForeignKey(
+        Representative, related_name='votescores', null=True)
+    position = models.CharField(max_length=10)
+    score = models.IntegerField(default=0)
+
+    class Meta:
+        managed = False
+        ordering = ['proposal__datetime']
+        db_table = 'representatives_recommendations_votescores'
+
+
 class RepresentativeScore(models.Model):
     representative = models.OneToOneField('representatives.representative',
         primary_key=True, related_name='score')
     score = models.IntegerField(default=0)
 
+    class Meta:
+        managed = False
+        db_table = 'representatives_recommendations_representativescore'
+
 
 class Recommendation(models.Model):
     proposal = models.OneToOneField(
@@ -44,20 +60,6 @@ class Recommendation(models.Model):
         ordering = ['proposal__datetime']
 
 
-class ScoredVote(Vote):
-    class Meta:
-        proxy = True
-
-    @cached_property
-    def absolute_score(self):
-        recommendation = self.proposal.recommendation
-
-        if self.position == recommendation.recommendation:
-            return recommendation.weight
-        else:
-            return -recommendation.weight
-
-
 def skip_votes(sender, vote_data=None, **kwargs):
     dossiers = getattr(sender, 'memopol_filters', None)
 
@@ -75,28 +77,3 @@ def skip_representatives(sender, representative_data=None, **kwargs):
     if not representative_data.get('active', False):
         return False
 representative_pre_import.connect(skip_representatives)
-
-
-def create_representative_vote_profile(sender, instance=None, created=None,
-        **kwargs):
-
-    if not created:
-        return
-
-    RepresentativeScore.objects.create(representative=instance)
-post_save.connect(create_representative_vote_profile, sender=Representative)
-
-
-def calculate_representative_score(representative):
-    score = 0
-
-    votes = representative.votes.exclude(
-        proposal__recommendation=None
-    ).select_related('proposal__recommendation')
-
-    votes = ScoredVote.objects.filter(pk__in=votes.values_list('pk'))
-
-    for vote in votes:
-        score += vote.absolute_score
-
-    return score
diff --git a/representatives_recommendations/serializers.py b/representatives_recommendations/serializers.py
index 4a1ae0d0dc5fb94720d32bcd00a273ecb4969299..330e53e4424378b836bb6222d1d9b57cb0fd27e9 100644
--- a/representatives_recommendations/serializers.py
+++ b/representatives_recommendations/serializers.py
@@ -4,7 +4,7 @@ from .models import (
     DossierScore,
     Recommendation,
     RepresentativeScore,
-    ScoredVote
+    VoteScore
 )
 
 
@@ -30,16 +30,8 @@ class RepresentativeScoreSerializer(serializers.HyperlinkedModelSerializer):
         fields = ('representative', 'score')
 
 
-class ScoredVoteSerializer(serializers.HyperlinkedModelSerializer):
-    """
-    Scored Vote serializer
-    """
+class VoteScoreSerializer(serializers.HyperlinkedModelSerializer):
 
     class Meta:
-        model = ScoredVote
-        fields = (
-            'proposal',
-            'representative',
-            'position',
-            'absolute_score'
-        )
+        model = VoteScore
+        fields = ('proposal', 'representative', 'position', 'score')
diff --git a/templates/representatives/representative_detail.haml b/templates/representatives/representative_detail.haml
index 2775cb9d5444361574f4bfbf0947358dd11052b9..94629ffda1bc816dceb3cb36c4007f2b58942510 100644
--- a/templates/representatives/representative_detail.haml
+++ b/templates/representatives/representative_detail.haml
@@ -34,7 +34,7 @@
             %td.icon-cell
               = vote.position|position_icon
             %td.icon-cell
-              = vote.absolute_score|score_label
+              = vote.score|score_label
 
 
   %h2 Mandates