Incrementing a variable of a class based on initiation of another class python/django
Incrementing a variable of a class based on initiation of another class python/django
My goal is to display the total number of entries in a specific contest. Initially I tried doing this within the template section by incrementing a counter for each contest as I looped through entries. My thought is that when an entry submission is created, the total_submissions for the corresponding contest should be incremented, however I'm not sure how to do it.
Model:
class Contest(models.Model):
contest_name = models.CharField(max_length=200)
contest_description = models.CharField(max_length=5000)
contest_start_date = models.DateTimeField('start date')
contest_end_date = models.DateTimeField('end date')
submission_count = models.IntegerField(default=0)
def __str__(self):
return self.contest_name
class Submission(models.Model):
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
date_uploaded = models.DateTimeField(auto_now_add=True)
votes = models.IntegerField(default=0)
def __init__(self):
#increment the submission_count variable for the corresponding contest
Other attempt by using templates:
<h1>Current Contests</h1>
<div class="row">
% if current_contest_list %
% for contest in current_contest_list %
<div class="col-sm-4"><h1><a href="% url 'contests:detail' contest.id%"> contest.contest_name </a></h1>
<img src="media/contest.display_image" class="img-responsive" alt="image">
contest.contest_name
% for submission in submission_list %
% if submission.contest == contest.contest_name %
<P>Hi!</P>
% endif %
<p> submission.contest contest.contest_name </p>
% if submission.contest == contest.contest_name %
<P>Hi!</P>
forloop.counter
% set total_submissions = forloop.counter %
% endif %
% endfor %
<p>Total entries: total_submissions </p>
When I did this, the total_submissions variable did not increment. What am I missing?
% set total_submissions = forloop.counter %
2 Answers
2
Django really discourages changing of data within templates, and they make it pretty hard to do. What you are trying to do won't work in a template.
In your template code, where is submission_list
coming from? If you want to iterate over all the submissions in each contest in your contest_list
, you would want to do something like:
submission_list
contest_list
% for submission in contest.submission_set.all %
To get the count, if your list is actually a Queryset (it usually is).. then the easiest way to get a count within the template is to use count
on the queryset, eg:
count
contest.submission_set.all.count
This will result in another database query, but if you aren't worried about a super high volume of visitors it should be no problem.
submission_list comes from the query in the view and is all of the submissions. I think this will work but I haven't tried it out yet. Thanks!
– katcopp
Aug 30 at 0:17
But is that a queryset of ALL submissions for any contest, or of only the submissions for one particular contest? The reason I ask is because, in your template code, it looks like you are looping through all your current contests.. and then for each contest, you are looping through submission_list. Then you go to the next contest and loop through submission_list again. This will result in the same submissions being output over and over again. I think you want to get the submissions from only the one contest, underneath that contest, don't you??
– little_birdie
Aug 30 at 0:26
Right, it's a queryset of all submissions. Most of the template code above was for my testing to figure out why it wasn't working for the count. I'm confused on how to run the query because you can't query data within the template. I believe the query would be: total_count = submissions.filter( contest = Contest.contest_name).count but I'm not sure that it will reference the Contest.contest_name correctly. Plus, do I just do a for loop in the View so that I have a count for each contest? I would like an overview on the home page. I really appreciate your help, @little_birdie
– katcopp
Aug 30 at 3:02
I got it to work!! However, it involved the for loop in the view and then creating a custom template tag to access the dictionary value by key within the template. Is there a better way to do this??
– katcopp
Aug 30 at 3:44
A custom template tag is totally unnecessary for this. You can just calculate any values you want inside your view function and then add keys to the context containing those values.. and then access those in the template
– little_birdie
Aug 30 at 13:38
As I understand, you want to increase a contest's submission count when a new submission is added. To do that, you'll need
submission_count
Submission
submission_count
Submission
To do (1), you can override the save()
method of Submission
:
save()
Submission
def save(self, *args, **kwargs):
if self.id is None: # To make sure this is an INSERT, not an UPDATE
contest = Contest.objects.select_for_update().get(id=self.contest.id)
contest.submission_count += 1
contest.save()
super().save(*args, **kwargs) # Call the "real" save() method.
To do (2), you can override the delete()
method:
delete()
def delete(self):
contest = Contest.objects.select_for_update().get(id=self.contest.id)
contest.submission_count -= 1
contest.save()
super().delete()
Note that this solution doesn't work if you creating or deleting submissions in bulk. See more at https://docs.djangoproject.com/en/2.1/topics/db/models/#overriding-predefined-model-methods
Personal Opinion: I wouldn't store submission count in db, unless the query to count number of submissions has noticeable impact on performance.
If he wants to maintain the count in the database then yes this is the way, but without an exclusive lock this would result in a race condition if two people submitted at the same time.
– little_birdie
Aug 29 at 18:11
That makes sense. Thanks @little_birdie
– dvnguyen
Aug 29 at 18:13
Thank you, I think this will work! I tried to avoid storing the count in the db but I got stumped when I was running the query. There are limitless contests/submissions, so how do I pass the proper query into the template? The natural way (to me, with little django experience) is to pass all of them into the template and do the above code. However that doesn't work, so I need to figure out how to do some sort of for loop within the view for the queries and then pass a list of submission_counts in into the template? @dvnguyen
– katcopp
Aug 30 at 0:11
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I am not sure if you can change the value of a template variable like
% set total_submissions = forloop.counter %
– Deepak Saini
Aug 29 at 18:07