Condado
Condado

Reputation: 65

Django: How to autopopulate foreign key with the corresponding model class instance

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 }}

Upvotes: 1

Views: 476

Answers (1)

vinkomlacic
vinkomlacic

Reputation: 1879

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>

Upvotes: 1

Related Questions