Skip to content

Commit fa7f2ea

Browse files
committed
allow to make comments for non-abstract models by for_concrete_model attribute
1 parent ef74319 commit fa7f2ea

File tree

3 files changed

+66
-5
lines changed

3 files changed

+66
-5
lines changed

django_comments/forms.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,31 @@ class CommentDetailsForm(CommentSecurityForm):
105105
comment = forms.CharField(label=_('Comment'), widget=forms.Textarea,
106106
max_length=COMMENT_MAX_LENGTH)
107107

108-
def get_comment_object(self, site_id=None):
108+
def get_comment_object(self, site_id=None, for_concrete_model=True):
109109
"""
110110
Return a new (unsaved) comment object based on the information in this
111111
form. Assumes that the form is already validated and will throw a
112112
ValueError if not.
113113
114114
Does not set any of the fields that would come from a Request object
115115
(i.e. ``user`` or ``ip_address``).
116+
117+
:param for_concrete_model: To change behavior of a model specifically for `django-comments` app like
118+
overriding `_get_pk_val()` to use non-pk UUID field as key for comments in URL.
119+
120+
Django by default returns non-proxy ancestor when determining content type of proxy model.
121+
With `COMMENTS_FOR_CONCRETE_MODEL=False` the `django-comments` app will pass `for_concrete_model=False`
122+
argument when determining content type via `ContentType.get_for_model()` method.
116123
"""
117124
if not self.is_valid():
118125
raise ValueError("get_comment_object may only be called on valid forms")
119126

120127
CommentModel = self.get_comment_model()
121-
new = CommentModel(**self.get_comment_create_data(site_id=site_id))
128+
try:
129+
create_data = self.get_comment_create_data(site_id=site_id, for_concrete_model=for_concrete_model)
130+
except TypeError: # Fallback for overrides that doesn't count with more attributes
131+
create_data = self.get_comment_create_data(site_id=site_id)
132+
new = CommentModel(**create_data)
122133
new = self.check_for_duplicate_comment(new)
123134

124135
return new
@@ -131,14 +142,17 @@ def get_comment_model(self):
131142
"""
132143
return get_model()
133144

134-
def get_comment_create_data(self, site_id=None):
145+
def get_comment_create_data(self, site_id=None, for_concrete_model=True):
135146
"""
136147
Returns the dict of data to be used to create a comment. Subclasses in
137148
custom comment apps that override get_comment_model can override this
138149
method to add extra fields onto a custom comment model.
139150
"""
140151
return dict(
141-
content_type=ContentType.objects.get_for_model(self.target_object),
152+
content_type=ContentType.objects.get_for_model(
153+
self.target_object,
154+
for_concrete_model=for_concrete_model,
155+
),
142156
object_pk=force_str(self.target_object._get_pk_val()),
143157
user_name=self.cleaned_data["name"],
144158
user_email=self.cleaned_data["email"],

tests/testapp/models.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""
55

66
from django.db import models
7+
import uuid
78

89

910
class Author(models.Model):
@@ -15,13 +16,24 @@ def __str__(self):
1516

1617

1718
class Article(models.Model):
19+
uuid = models.UUIDField(editable=False, null=True)
1820
author = models.ForeignKey(Author, on_delete=models.CASCADE)
1921
headline = models.CharField(max_length=100)
2022

2123
def __str__(self):
2224
return self.headline
2325

2426

27+
class UUIDArticle(Article):
28+
""" Override _get_pk_val to use UUID as PK """
29+
30+
def _get_pk_val(self, meta=None):
31+
return self.uuid
32+
33+
class Meta:
34+
proxy = True
35+
36+
2537
class Entry(models.Model):
2638
title = models.CharField(max_length=250)
2739
body = models.TextField()

tests/testapp/tests/test_comment_form.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import time
22

33
from django.conf import settings
4+
from django.contrib.contenttypes.models import ContentType
45
from django.contrib.sites.models import Site
56

67
from django_comments.forms import CommentForm
78
from django_comments.models import Comment
89

910
from . import CommentTestCase
10-
from testapp.models import Article
11+
from testapp.models import UUIDArticle, Article
12+
from django.test.utils import override_settings
1113

1214

1315
class CommentFormTests(CommentTestCase):
@@ -75,6 +77,39 @@ def testGetCommentObject(self):
7577
c = f.get_comment_object(site_id=self.site_2.id)
7678
self.assertEqual(c.site_id, self.site_2.id)
7779

80+
def testGetCommentCreateData(self):
81+
"""
82+
Test that get_comment_create_data() returns
83+
content_type for Article even if the proxy model UUIDArticle
84+
is set to CommentForm.
85+
"""
86+
a = UUIDArticle.objects.get(pk=1)
87+
d = self.getValidData(a)
88+
d["comment"] = "testGetCommentObject with a site"
89+
f = CommentForm(UUIDArticle.objects.get(pk=1), data=d)
90+
self.assertTrue(f.is_valid())
91+
c = f.get_comment_create_data(site_id=self.site_2.id)
92+
self.assertEqual(c["content_type"], ContentType.objects.get_for_model(Article, for_concrete_model=False))
93+
94+
o = f.get_comment_object(site_id=self.site_2.id)
95+
self.assertEqual(o.content_type, ContentType.objects.get_for_model(Article, for_concrete_model=False))
96+
97+
def testGetCommentCreateDataConcreteModel(self):
98+
"""
99+
Test that get_comment_create_data() returns
100+
content_type for UUIDArticle if COMMENTS_FOR_CONCRETE_MODEL is False.
101+
"""
102+
a = UUIDArticle.objects.get(pk=1)
103+
d = self.getValidData(a)
104+
d["comment"] = "testGetCommentObject with a site"
105+
f = CommentForm(UUIDArticle.objects.get(pk=1), data=d)
106+
self.assertTrue(f.is_valid())
107+
c = f.get_comment_create_data(site_id=self.site_2.id, for_concrete_model=False)
108+
self.assertEqual(c["content_type"], ContentType.objects.get_for_model(UUIDArticle, for_concrete_model=False))
109+
110+
o = f.get_comment_object(site_id=self.site_2.id, for_concrete_model=False)
111+
self.assertEqual(o.content_type, ContentType.objects.get_for_model(UUIDArticle, for_concrete_model=False))
112+
78113
def testProfanities(self):
79114
"""Test COMMENTS_ALLOW_PROFANITIES and PROFANITIES_LIST settings"""
80115
a = Article.objects.get(pk=1)

0 commit comments

Comments
 (0)