1

Working on my first Django project and could use some help. I have 2 models (Decisions, Votes) linked by the foreign key called 'decision'. The template, vote_list.html, shows the user a list of decisions (generated by other users) that are contained in Decisions. The user taps a particular decision and is re-directed to a second template to vote on options pertaining to that decision. How do I autopopulate the foreign key 'decision' in Votes with the corresponding instance of Decision so that the second template, vote_form.html, displays the options for the decision they tapped on? I assume it's coded in views.py (I commented an attempt below that doesn't work), but how might it be done? Thank you!

urls.py

path('vote-list/', views.VoterView.as_view(), name='vote_list'),
path('vote-list/<pk>/vote-form/', views.VoteForm.as_view(), name='vote_form'),

models.py

class Decisions(models.Model):
    custom_user = models.ForeignKey(CustomUser, 
                  default=None, null=True, 
                  on_delete=models.SET_NULL)
    description = models.CharField(default="", 
                  max_length=100, verbose_name="Decision 
                  Summary")

class Votes(models.Model):
    decision = models.ForeignKey(Decisions, 
               default=None, null=True, 
               on_delete=models.SET_NULL)
    vote = models.CharField(default="", max_length=100, 
            verbose_name="Your vote")

views.py

class VoteForm(LoginRequiredMixin, CreateView):
    model = Votes
    form_class = VotingForm
    template_name = 'users/vote_form.html'

    def post(self, request, *args, **kwargs):
        super()
        form = self.form_class(data=request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
        
            # instance.decision = Decisions.description
        
            instance.save()
        return redirect('users:vote_list')    

forms.py

class VotingForm(forms.ModelForm):
    class Meta:
        model = Votes
        fields = ['vote']

vote_list.html

     {% for item in Decisions %}
         <tr>
            <td>{{ item.description }}</td>
            <td><a href="{% url 'users:vote_form' item.id 
                 %}">Vote</a></td>
         </tr>                   
     {% endfor %}    

vote_form.html

     {# trying to display the corresponding 
          decision description here from vote_list.html # }}

     {{ form.vote|as_crispy_field }}
Condado
  • 83
  • 7

1 Answers1

1

I think this might solve your problem:

  1. Add decision field in the voting form. This will display an option to select for which decision you need to save this Vote for. If you don't want to allow users to change the Decision, you can mark the field as disabled. See this issue for more details on how to do that. Another alternative is to completely hide the field.
class VotingForm(forms.ModelForm):
    class Meta:
        model = Votes
        fields = ['vote', 'decision']
  1. Add initial value of the decision when instantiating the VotingForm. This will automatically set which decision is selected when displaying the form.
class VoteForm(LoginRequiredMixin, CreateView):
    model = Votes
    form_class = VotingForm
    template_name = 'users/vote_form.html'

    # Use this to pass 'pk' to your context in the template
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update({'pk': self.kwargs['pk'})

        return context

    def get_initial(self):
        initial = super().get_initial()
        initial.update({'decision': self.kwargs['pk']})

        return initial

    def get_success_url():
        # Import reverse from django.urls
        return reverse('users:vote_list')

Also, your form should probably be displayed like this in the HTML template: {% crispy form %}. This way all defined fields from the VotingForm class are rendered automatically.

<form method="post" action="{% url 'users:vote_form' pk %}">
     {% crispy form %}
</form>
vinkomlacic
  • 1,822
  • 1
  • 9
  • 19
  • Thanks, the method, get_form_kwargs, in your response above is resulting in the error and how might I fix this: TypeError at /vote-list/22/vote-form/ users.forms.VotingForm() argument after ** must be a mapping, not NoneType – Condado Mar 20 '22 at 16:16
  • Sorry, I think completely replacing the `post` method might solve the issue. I will edit my answer, can you tell me if it works – vinkomlacic Mar 20 '22 at 18:13
  • I also replaced `get_form_kwargs` with `get_initial`. Let me know if problems persist. – vinkomlacic Mar 20 '22 at 18:19
  • I no longer see the error message, but it's not adding the initial value. The field 'decision' still appears with '------' as its value upon rendering (whether using self.kwargs or self.initial in above). – Condado Mar 20 '22 at 19:27
  • Hmm, weird, is the PK you send in the URL the decision URL? – vinkomlacic Mar 20 '22 at 19:33
  • Can you check what is the value of `form.decision.initial` in your template? – vinkomlacic Mar 20 '22 at 19:36
  • Yes, the PK should be the decision record. There are 2 records in Decisions. I can see those 2 decisions in the drop down menu for the 'decision' field. – Condado Mar 20 '22 at 20:09
  • Can you debug it and see if in the init method of the VotingForm, the `initial` kwarg is being set? – vinkomlacic Mar 20 '22 at 20:13
  • I will be delayed in getting back to you, but I will get back to you. Thank you for help so far! – Condado Mar 20 '22 at 20:18
  • My apologies for the delay. I'm not experienced at debugging, but using PyCharm's debugger, I couldn't locate that the 'initial' kwarg was being set in the ___init___ method....But why wouldn't the following line in my template carry over the instance of the Decision model (with the item.id identifier) into the vote_form.html template: Vote? What's the purpose of item.id? – Condado Mar 21 '22 at 23:20
  • Hmm, not sure. I have a suspicion that the `pk` in the URL might be the problem. That is a special parameter for class based views (especially UpdateView), so maybe something happens with it in CreateView too. Can you try replacing the `pk` with `decision_pk` in the URL definition and doing the same in `initial.update({'decision': self.kwargs['decision_pk']})`. Let me know if it helps – vinkomlacic Mar 22 '22 at 08:56
  • I tried that, but it causes different errors in urls.py and views.py....There's got to be a way to do this in Python Django. There must be thousands of apps that have a template with a list of links, allowing the user to click on a link and then get directed to another template with just the fields/info pertaining to that link that they clicked on. – Condado Mar 23 '22 at 00:55
  • I agree, can you post your whole current code in an edit, please – vinkomlacic Mar 23 '22 at 08:29
  • Also, can you post the whole code of your `vote_form.html` – vinkomlacic Mar 23 '22 at 09:06
  • I have just recreated your project and your intended outcome works with the code provided. The only thing that I'm unsure of is how are you passing decision ID to the `vote_form.html` template? – vinkomlacic Mar 23 '22 at 09:17
  • You're right---I don't know how to pass the decision ID (of the decision that the user selects in vote_list.html) to vote_form.html. Would that be done in views.py or in vote_form.html using DTL---or in both files? Might you show me how you would do it? – Condado Mar 24 '22 at 02:07
  • I have updated the answer to add `get_context_data` method which passes the `pk` (decision ID) to the `vote_form.html`. The only thing left is to use it in the `action` attribute in the form. – vinkomlacic Mar 24 '22 at 08:03
  • That worked! I should have caught that too. Thanks @vinkomlacic...What would I need to use the form's action item for? Do you mean to indicate where the data submitted by the user in vote_form.html is to be stored? – Condado Mar 25 '22 at 00:54
  • No, I meant HTML action attribute here:
    – vinkomlacic Mar 26 '22 at 14:16