Repository: ciudadanointeligente/votainteligente-portal-electoral

Issue:
Extra info is the same for all candidates
When using extra_info such as ribbon or colors the same values are returned for all candidates in cases such as election page or soul-mate JSON.
The correct value is returned in cases where information for only one candidate is requested, such as the candidate detail page.



Discussion:

Resolving patch:
diff --git a/elections/admin.py b/elections/admin.py
--- a/elections/admin.py
+++ b/elections/admin.py
@@ -45,6 +45,7 @@ class TakenPositionCandidateInline(admin.TabularInline):
     model = TakenPosition
     form = TakenPositionInlineModelForm
     extra = 0
+    can_delete = False
 
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'position':
diff --git a/elections/models.py b/elections/models.py
--- a/elections/models.py
+++ b/elections/models.py
@@ -9,6 +9,8 @@
 from candidator.models import Category, Topic as CanTopic
 from picklefield.fields import PickledObjectField
 from django.conf import settings
+from django.utils.encoding import python_2_unicode_compatible
+import copy
 
 
 class ExtraInfoMixin(models.Model):
@@ -19,7 +21,7 @@ class Meta:
 
     def __init__(self, *args, **kwargs):
         super(ExtraInfoMixin, self).__init__(*args, **kwargs)
-        default_extra_info = self.default_extra_info
+        default_extra_info = copy.copy(self.default_extra_info)
         default_extra_info.update(self.extra_info)
         self.extra_info = default_extra_info
 
@@ -52,9 +54,13 @@ def election(self):
         return category.election
 
 
+@python_2_unicode_compatible
 class QuestionCategory(Category):
     election = models.ForeignKey('Election', related_name='categories', null=True)
 
+    def __str__(self):
+        return u'<%s> in <%s>' % (self.name, self.election.name)
+
 
 class Election(ExtraInfoMixin, models.Model):
     name = models.CharField(max_length=255)


Test patch:
diff --git a/elections/tests/version2/models_tests.py b/elections/tests/version2/models_tests.py
--- a/elections/tests/version2/models_tests.py
+++ b/elections/tests/version2/models_tests.py
@@ -119,10 +119,10 @@ def test_can_have_extra_info(self):
         candidate.save()
         self.assertEquals(candidate.extra_info['ribbon'], "perrito")
 
-    @override_settings(DEFAULT_CANDIDATE_EXTRA_INFO={'ribbon': 'perrito'})
+    @override_settings(DEFAULT_CANDIDATE_EXTRA_INFO={'custom_ribbon': 'ribbon text'})
     def test_default_candidate_extra_info(self):
         candidate = Candidate.objects.get(id=1)
-        self.assertEquals(candidate.extra_info['ribbon'], 'perrito')
+        self.assertEquals(candidate.extra_info['custom_ribbon'], 'ribbon text')
 
     @override_settings(DEFAULT_CANDIDATE_EXTRA_INFO={'ribbon': 'perrito'})
     def test_do_not_override_settings(self):
@@ -140,6 +140,15 @@ def test_instanciate_a_personal_data(self):
         self.assertEquals(personal_data.value, u'31 años')
         self.assertIn(personal_data, candidate.personal_datas.all())
 
+    def test_bug_258(self):
+        candidate = Candidate.objects.get(id=1)
+        candidate.extra_info['custom_ribbon'] = 'Perro grande'
+        candidate.extra_info['other_thing'] = 'This is something else'
+        candidate.save()
+        candidate2 = Candidate.objects.get(id=2)
+        self.assertEquals(candidate2.extra_info['custom_ribbon'], 'ribbon text')
+        self.assertNotIn('other_thing', candidate2.extra_info.keys())
+
 
 class QuestionCategoryTestCase(Version2TestCase):
     def setUp(self):
@@ -147,7 +156,9 @@ def setUp(self):
 
     def test_instanciate_one(self):
         category = QuestionCategory.objects.create(name="Perros", election=self.election)
+
         self.assertIsInstance(category, Category)
+        self.assertEquals(category.__str__(), u"<Perros> in <the name>")
 
 
 class TopicTestCase(Version2TestCase):