Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change Vote.object_id type to TextField to support other primary key types like UUIDField. #26

Merged
merged 1 commit into from
Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Unreleased
* Drop Django 3.0 support.
* Add Django 3.2 support.
* Add Python 3.10 support.
* Change ``Vote.object_id`` type to ``TextField`` to support
other primary key types like ``UUIDField``.

1.0 (2021-03-10)
----------------
Expand Down
6 changes: 6 additions & 0 deletions src/voting/managers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from uuid import UUID

from django.conf import settings
from django.db import models
from django.db.models import Sum, Count
Expand Down Expand Up @@ -102,6 +104,10 @@ def get_top(self, model, limit=10, reversed=False):
id, score = item["object_id"], item["score"]
if not score:
continue
if isinstance(model._meta.pk, models.AutoField):
id = int(id)
elif isinstance(model._meta.pk, models.UUIDField):
id = UUID(id)
if id in objects:
yield objects[id], int(score)

Expand Down
16 changes: 16 additions & 0 deletions src/voting/migrations/0002_alter_vote_object_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('voting', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='vote',
name='object_id',
field=models.TextField(),
),
]
2 changes: 1 addition & 1 deletion src/voting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Vote(models.Model):

user = models.ForeignKey(User, on_delete=models.CASCADE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
object_id = models.TextField()
object = GenericForeignKey("content_type", "object_id")
vote = models.SmallIntegerField(choices=SCORES)
time_stamp = models.DateTimeField(editable=False, default=now)
Expand Down
10 changes: 10 additions & 0 deletions tests/test_app/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import uuid

from django.db import models


Expand All @@ -9,3 +11,11 @@ def __str__(self):

class Meta:
ordering = ["name"]


class ItemWithUUID(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=50)

def __str__(self):
return f"{self.name}({self.id})"
76 changes: 70 additions & 6 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.test import TestCase

from voting.models import Vote
from test_app.models import Item
from test_app.models import Item, ItemWithUUID


class BasicVotingTests(TestCase):
Expand All @@ -11,12 +11,19 @@ class BasicVotingTests(TestCase):
"""
def setUp(self):
self.item = Item.objects.create(name="test1")
self.item_with_uuid = ItemWithUUID.objects.create(name="test1")
self.users = []
for username in ["u1", "u2", "u3", "u4"]:
self.users.append(
User.objects.create_user(username, "%[email protected]" % username, "test")
)

def test_print_model_with_uuid_primary_key(self):
Vote.objects.record_vote(self.item_with_uuid, self.users[0], +1)
expected = f"u1: 1 on {self.item_with_uuid.name}({self.item_with_uuid.id})"
result = Vote.objects.all()[0]
self.assertEqual(str(result), expected)

def test_print_model(self):
Vote.objects.record_vote(self.item, self.users[0], +1)
expected = "u1: 1 on test1"
Expand Down Expand Up @@ -71,22 +78,31 @@ class VoteRetrievalTests(TestCase):
"""
def setUp(self):
self.items = []
self.items_with_uuid = []
for name in ["test1", "test2", "test3", "test4"]:
self.items.append(Item.objects.create(name=name))
self.items_with_uuid.append(ItemWithUUID.objects.create(name=name))
self.users = []
for username in ["u1", "u2", "u3", "u4"]:
self.users.append(
User.objects.create_user(username, "%[email protected]" % username, "test")
)
for user in self.users:
Vote.objects.record_vote(self.items[0], user, +1)
Vote.objects.record_vote(self.items_with_uuid[0], user, +1)
for user in self.users[:2]:
Vote.objects.record_vote(self.items[0], user, 0)
Vote.objects.record_vote(self.items_with_uuid[0], user, 0)
for user in self.users[:2]:
Vote.objects.record_vote(self.items[0], user, -1)
Vote.objects.record_vote(self.items_with_uuid[0], user, -1)
Vote.objects.record_vote(self.items[1], self.users[0], +1)
Vote.objects.record_vote(self.items[2], self.users[0], -1)
Vote.objects.record_vote(self.items[3], self.users[0], 0)
Vote.objects.record_vote(self.items_with_uuid[1], self.users[0], +1)
Vote.objects.record_vote(self.items_with_uuid[2], self.users[0], -1)
Vote.objects.record_vote(self.items_with_uuid[3], self.users[0], 0)


def test_get_pos_vote(self):
vote = Vote.objects.get_for_user(self.items[1], self.users[0])
Expand All @@ -106,7 +122,7 @@ def test_get_zero_vote(self):
def test_in_bulk1(self):
votes = Vote.objects.get_for_user_in_bulk(self.items, self.users[0])
self.assertEqual(
[(id, vote.vote) for id, vote in votes.items()], [(1, -1), (2, 1), (3, -1)]
[(id, vote.vote) for id, vote in votes.items()], [("1", -1), ("2", 1), ("3", -1)]
)

def test_empty_items(self):
Expand All @@ -122,6 +138,19 @@ def test_get_top(self):
expected = [(self.items[1], 4), (self.items[3], 3), (self.items[2], 2)]
self.assertEqual(result, expected)

def test_get_top_for_model_with_uuid_primary_key(self):
for user in self.users[1:]:
Vote.objects.record_vote(self.items_with_uuid[1], user, +1)
Vote.objects.record_vote(self.items_with_uuid[2], user, +1)
Vote.objects.record_vote(self.items_with_uuid[3], user, +1)
result = list(Vote.objects.get_top(ItemWithUUID))
expected = [
(self.items_with_uuid[1], 4),
(self.items_with_uuid[3], 3),
(self.items_with_uuid[2], 2),
]
self.assertEqual(result, expected)

def test_get_bottom(self):
for user in self.users[1:]:
Vote.objects.record_vote(self.items[1], user, +1)
Expand All @@ -135,6 +164,23 @@ def test_get_bottom(self):
expected = [(self.items[2], -4), (self.items[3], -3), (self.items[1], -2)]
self.assertEqual(result, expected)

def test_get_bottom_for_model_with_uuid_primary_key(self):
for user in self.users[1:]:
Vote.objects.record_vote(self.items_with_uuid[1], user, +1)
Vote.objects.record_vote(self.items_with_uuid[2], user, +1)
Vote.objects.record_vote(self.items_with_uuid[3], user, +1)
for user in self.users[1:]:
Vote.objects.record_vote(self.items_with_uuid[1], user, -1)
Vote.objects.record_vote(self.items_with_uuid[2], user, -1)
Vote.objects.record_vote(self.items_with_uuid[3], user, -1)
result = list(Vote.objects.get_bottom(ItemWithUUID))
expected = [
(self.items_with_uuid[2], -4),
(self.items_with_uuid[3], -3),
(self.items_with_uuid[1], -2),
]
self.assertEqual(result, expected)

def test_get_scores_in_bulk(self):
for user in self.users[1:]:
Vote.objects.record_vote(self.items[1], user, +1)
Expand All @@ -146,10 +192,28 @@ def test_get_scores_in_bulk(self):
Vote.objects.record_vote(self.items[3], user, -1)
result = Vote.objects.get_scores_in_bulk(self.items)
expected = {
1: {"score": 0, "num_votes": 4},
2: {"score": -2, "num_votes": 4},
3: {"score": -4, "num_votes": 4},
4: {"score": -3, "num_votes": 3},
"1": {"score": 0, "num_votes": 4},
"2": {"score": -2, "num_votes": 4},
"3": {"score": -4, "num_votes": 4},
"4": {"score": -3, "num_votes": 3},
}
self.assertEqual(result, expected)

def test_get_scores_in_bulk_with_uuid_primary_key(self):
for user in self.users[1:]:
Vote.objects.record_vote(self.items_with_uuid[1], user, +1)
Vote.objects.record_vote(self.items_with_uuid[2], user, +1)
Vote.objects.record_vote(self.items_with_uuid[3], user, +1)
for user in self.users[1:]:
Vote.objects.record_vote(self.items_with_uuid[1], user, -1)
Vote.objects.record_vote(self.items_with_uuid[2], user, -1)
Vote.objects.record_vote(self.items_with_uuid[3], user, -1)
result = Vote.objects.get_scores_in_bulk(self.items_with_uuid)
expected = {
str(self.items_with_uuid[0].id): {"score": 0, "num_votes": 4},
str(self.items_with_uuid[1].id): {"score": -2, "num_votes": 4},
str(self.items_with_uuid[2].id): {"score": -4, "num_votes": 4},
str(self.items_with_uuid[3].id): {"score": -3, "num_votes": 3},
}
self.assertEqual(result, expected)

Expand Down