Search code examples
pythondjangodjango-viewsdjango-formsdjango-filters

How to filter a Django model to display only the logged-in user's data?


Trying to solve this for a week now. How do I filter my model to display only the data assigned to the specific logged-in user? My views.py code below is not working---it's not telling forms.py what it needs to do to accomplish this. I get the error 'ForwardManyToOneDescriptor' object has no attribute 'option1'.

It does filter effectively in the template---but I need to use the RadioSelect widget (defined in forms.py) in order to display specific choices that the specific logged-in user must choose from (and I don't think I can---or should---code that in the template).

views.py

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

    def get_context_data(self, 
                     **kwargs):
        context = 
              super().get_context_data 
              (**kwargs)
        context['Result'] = 
            Result.objects.all()
        context['user_specific'] = 
            Result.objects.filter 
       (custom_user=self.request.user)
       return context

   def get_form_kwargs(self):
       kwargs=super(). 
               get_form_kwargs()
       kwargs.update({'results': 
       Result.objects.
               filter(custom_user=
               self.request.user)})
       return kwargs

 * Added following post method, but 
   get " KeyError 'results' " error

   def post(self, request, *args, 
           **kwargs):
       form = self.form_class(data=request.POST)
    if form.is_valid():
        instance = form.save(commit=False)
        instance.custom_user = 
                 self.kwargs
        instance.save()
    return redirect('users:home')


   def get_success_url(self):
       return reverse('users:home')

forms.py

class VotingForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)

         choices = [('1', Result.decision.option1),
                    ('2', Result.decision.option2)]
         self.fields['vote'] = forms.ChoiceField(choices=choices, label="Choices", widget=forms.RadioSelect())

    class Meta:
        model = Result
        fields = ['decision', 'vote']

models.py (if needed)

class Result(models.Model):
    custom_user = models.ForeignKey(CustomUser)
    decision = models.ForeignKey(Decision) * Decision 
                                             contains 
                                             decisions and 
                                             their choices 
                                (like 'option1', 'option2')
    vote = models.CharField()
    vote_eligible = models.BooleanField(default=False)

vote_form.html

<form method="post">
    {% csrf_token %}

    {% for item in user_specific %}

    {% if item.vote_eligible ==True %}    
       <div class="field-bold">
            Decision
       </div>
       <p>{{ item.decision }}</p>

       <div class="field-bold">
       {{ form.vote|as_crispy_field }}
       </div>

    <div class="btn-container">
        <div class="btn-grp">
           <input type="submit" 
            class="btn btn-submit" 
            value="Vote"/>
           <a href="{% url 
            'users:home' %}" 
            class="btn 
            btn-cancel">Cancel</a>
        </div>
    </div>
    {% endif %}
    
    {% endfor %}

    {% if not user_specific %}
        <p>There are currently no 
         decisions awaiting your vote. 
        </p>
    {% endif %}
</form>

Solution

  • Your problem is in this line:

    choices = [('1', Result.decision.option1),
               ('2', Result.decision.option2)]
    

    What you're doing here is setting choices according to model fields. You're not using their values. Their values are tied to particular instance of the Result model.

    Usually, this problem requires that you know which instances of the Result model will be used for the options. I normally pass it in the form kwargs like this:

    def __init__(self, *args, **kwargs):
             results = kwargs.pop('results')
             super().__init__(*args, **kwargs)
    
             # Now we're using a model INSTANCE, not the model itself
             # Also, you don't need to use the number for a key value. Makes
             # it a bit easier.
             choices = []
             for result in results.all():
                 choices.append((result.decision.option1, result.decision.option1))
                 choices.append((result.decision.option2, result.decision.option2))
             self.fields['vote'] = forms.ChoiceField(choices=choices, label="Choices", widget=forms.RadioSelect())
    

    Then in the views.py add get_form_kwargs to initialize the kwargs for your form:

    def get_form_kwargs():
        kwargs = super().get_form_kwargs()
        kwargs.update({'results': Result.objects.filter(custom_user=self.request.user)})
    
        return kwargs