Let’s say I have a song model and I want to allow users to post comments on a song. This is what the comment model looks like (I omitted the choices for rating for the sake of brevity):
class Comment(models.Model):
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
song = models.ForeignKey(Song, on_delete=models.CASCADE)
text = models.TextField(max_length=5000)
rating = models.PositiveSmallIntegerField(choices=Ratings.choices)
create_date=models.DateTimeField(auto_now_add=True)
And the form:
class AddCommentForm(ModelForm):
class Meta:
model = Comment
fields = ("rating", "text")
It has two foreign keys, profile (which is connected to the authenticated user) and song. The only fields the user should see in the form are text and rating (which is a score from 1-10). These are both required to save a comment since they’re non-nullable, but they’re not in the form - which means I will need to add them in the view.
There are also two constraints: a user cannot comment on their own song, and they can’t comment on a song they’ve already commented on.
Now, here is my implementation using CreateView
. Is this the best way for me to use it?
To verify my constraints: I override dispatch
to check if the user has commented on the song already or if it’s the user’s own song. While I’m at it, I store the song into extra_context so I don’t have to do another database query for it later.
To add the foreign keys before saving: I override form_valid
to add the profile and song IDs to the comment that is about to be saved.
To provide context data: I override get_context_data
to add the song to the context data. If the song was loaded in dispatch
, it will already be in extra_context
, so I can just pull it from there instead of querying the database again. The context data is only used in the template, so this is only relevant to the GET request.
My questions:
Is dispatch
the best place for me to check my constraints? Or should I do that in form_valid
, or somewhere else?
Is my override of form_valid
the right way of adding the foreign key values to the comment? I know I could put both the user id and song id in the form itself as hidden fields, but I don’t want to allow for the possibility of the user changing those by modifying the HTML locally.
class AddCommentView(LoginRequiredMixin, CreateView):
form_class = AddCommentForm
template_name = "add_comment.html"
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return super().dispatch(request, *args, **kwargs)
song_id = kwargs['pk']
song = Song.objects.get(id=song_id)
profile_id = request.user.profile.id
is_own_song = song.artist_set.all().filter(profile_id=profile_id).exists()
has_commented = song.comment_set.all().filter(profile_id=profile_id).exists()
# Storing the song into extra_context to save a database request
self.extra_context={'song': song}
if (is_own_song or has_commented):
return redirect('comment_rejected')
return super().dispatch(request, *args, **kwargs)
# Add profile and song ID to the comment in order to save it
def form_valid(self, form):
comment_instance = form.save(commit=False)
comment_instance.profile = self.request.user.profile
comment_instance.song_id = self.kwargs['pk']
comment_instance.save()
return redirect('view_song', self.kwargs['pk'])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['song'] = self.extra_context['song'] if self.extra_context['song'] else Song.objects.get(self.kwargs['pk'])
return context