Looking for some guidance with a multiple choice poll and 2 models

#1

It’s basically one question with multiple selections. I originally had the Vote model set up to hold the user and their selections. Was working fine. Was saving etc. Then I added country and region to it, but then realized that I gotta normalize that. So I did and added more. below is what I have. Trying to understand how to add both models to one form. They also don’t have a relationship other than the user which is not a direct FK link. I looked at generic relations, looked at formsets, but could not get anything concrete as there was always a FK relation.

models.py

class Vote(models.Model):
    class Meta:
        unique_together = [('song', 'user')]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_votes')
    song = models.ForeignKey(Song, on_delete=models.CASCADE, related_name='song_votes')

    def __str__(self):
        return '%s' % self.song.song_name

    @staticmethod
    def get_absolute_url():
        return reverse('vote_results')


class VoteMeta(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    vote_date = models.DateTimeField(null=False, auto_now_add=True)
    country = models.ForeignKey('Country', null=True, blank=True, on_delete=models.SET_NULL,
                                help_text=_("What country do you live in or want to see Poison come to for a tour?"))
    region = models.ForeignKey('Region', null=True, blank=True, on_delete=models.SET_NULL,
                               help_text=_("What region of the country?"))
    ipaddress = models.IPAddressField(null=True, blank=True)


class Country(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=100, blank=False, null=False, verbose_name="Country")

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name


class Region(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=200, blank=True, null=True, verbose_name="Region")
    country = models.ForeignKey(Country, models.DO_NOTHING, blank=True, null=True, editable=False)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

forms.py

class VoteForm(forms.ModelForm):
    class Meta:
        model = Vote
        fields = ('song',)

    #         # widgets = {
    #         #     'country': forms.ModelChoiceField(Country.objects.all(), to_field_name="name",
    #         #                                       empty_label=_("Select Your Country"))
    #         #     }
    #
    #     song = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=True)
    #     country = forms.Select()
    #     region = forms.Select()
    #
    #     # country = forms.ModelChoiceField(queryset=None, to_field_name="name", empty_label=_("Select Your Country"))
    #     # region = forms.ModelChoiceField(queryset=None, to_field_name="name")
    #
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['song'].choices = [id]
        self.fields['region'].queryset = Region.objects.none()

        if 'country' in self.data:
            try:
                country_id = int(self.data.get('country'))
                self.fields['region'].queryset = Region.objects.filter(country_id=country_id).order_by('name')
            except (ValueError, TypeError):
                pass  # invalid input from the client; ignore and fallback to empty City queryset
        elif self.instance.pk:
            self.fields['region'].queryset = self.instance.country.region_set.order_by('name')


class VoteMetaForm(forms.ModelForm):
    class Meta:
        model = VoteMeta
        fields = ('country', 'region', 'ipaddress')

views.py

class VoteFormView(CreateView):
    template_name = "vote.html"
    model = Vote
    # fields = ('country', 'region', 'song')
    form_class = VoteForm
    success_url = reverse_lazy('vote_results')

    # def form_valid(self, form):
    #     super(VoteFormView, self).form_valid(form)
    #
    # def get_initial(self):
    #     initial_data = super(VoteFormView, self).get_initial()
    #     initial_data['countries'] = Country.objects.all()
    #
    #     return initial_data
    #
    # def form_invalid(self, form):
    #     return super(VoteFormView, self).form_invalid(form)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # context['countries'] = Country.objects.all()
        context['song_list'] = Song.objects.order_by('album__year_released', 'album__month_released',
                                                     'track_no').values('album__album_name', 'song_name', 'id',
                                                                        'with_cc')

        return context


@verified_email_required
def new_vote(request):
    VoteMetaFormSet = formset_factory(VoteMetaForm, extra=0)
    # If this is a POST request then process the Form data
    if request.method == 'POST':
        # Create a form instance and populate it with data from the request (binding):
        form = VoteForm(request.POST)
        formset = VoteMetaFormSet(request.POST)
        if all([form.is_valid(), formset.is_valid()]):
            vote = form.save()
            for inline_form in formset:
                if inline_form.cleaned_data:
                    meta = inline_form.save(commit=False)
                    meta.
        # Check if the form is valid:
        #        if form.is_valid():
        chosen_songs_options = request.POST.getlist('song')
        with transaction.atomic():  # all or nothing
            for song in chosen_songs_options:
                print(song)
                thissong_obj = Song.objects.get(pk=song)
                Vote.objects.create(user=request.user, song=thissong_obj)

        message = _("Thanks for voting! Here are the results so far.")

        # redirect to a new URL:
        return render(request, 'vote_results.html', {
            'form': form,
            'message': message
            })


class VoteResultsView(ListView):
    pass


def load_regions(request):
    country_id = request.GET.get('country')
    regions = Region.objects.filter(country_id=country_id).order_by('name')
    return render(request, 'region_dropdown_list_options.html', {
        'regions': regions
        })

vote.html

<form action="{% url 'cast-vote' %}" method="post" name="voteFrm" id="VoteForm"
      data-regions-url="{% url 'ajax_load_regions' %}" novalidate>
    {% csrf_token %}
    <div class="row">
        <div class="col-md-6">
            <div class="form-group">
                <label for="country">{% trans 'Country' %}</label>
                <select class="form-control" name="country" id="country">
                    {% for thiscountry in countries %}
                        <option value="{{ thiscountry.name }}">{{ thiscountry.name }}</option>
                    {% endfor %}
                </select>
                <small id="countryHelp" class="form-text text-muted">
                    {{ form.country.help_text }}
                </small>
            </div>
        </div>
        <div class="col-md-6">
            <div class="form-group">
                <label for="region">{% trans 'Region' %}</label>
                <select class="form-control" name="region" id="region">
                </select>
                <small id="regionHelp" class="form-text text-muted">
                    {{ form.region.help_text }}
                </small>
            </div>
        </div>
    </div>
    {% regroup song_list by album__album_name as song_list_group %}
    <div class="row">
        <div class="col-md-12">
            <div class="card-deck">
                {% for album__album_name, songs in song_list_group %}
                    <div class="card border-success mb-3">
                        {% with ''|add:album__album_name|slugify as slug_album %}
                            {% with 'images/'|add:slug_album|add:'.jpg' as image_static %}
                                <img class="card-img-top" src="{% static image_static %}"
                                     alt="{{ album__album_name }}">
                            {% endwith %}
                        {% endwith %}
                        <ul class="list-group checked-list-box list-group-flush">
                            {{ form.chosen_songs }}
                            {% for song in songs %}
                                <li class="list-group-item" data-check-name="song"
                                    data-check-value="{{ song.id }}"
                                    data-check-id="id_song_{{ forloop.counter0 }}">
                                    {{ song.song_name }}
                                    {% if not song.with_cc %}
                                        <i class="fa fa-asterisk text-green-bright text-xs"></i>
                                    {% endif %}
                                </li>
                            {% endfor %}
                        </ul>
                    </div>
                    {#  2 per row #}
                    {% if forloop.counter|divisibleby:2 and not forloop.last %}
                        </div> <!-- end deck -->
                        </div> <!-- end col-sm-12 -->
                        </div> <!-- end row -->
                        <div class="row">
                            <div class="col-md-12">
                                <div class="card-deck">
                                    {% elif forloop.last %}
                                </div> <!-- end deck -->
                            </div> <!-- end col-sm-12 -->
                        </div> <!-- end row -->
                    {% endif %}
                {% endfor %}
    <div class="row">
        <div class="col-md-12">
            <button type="submit" name="SaveBtn" id="SaveBtn"
                    class="btn btn-primary btn-lg btn-block mt-2">
                {% blocktrans %}Click Here to Lock in Your Set List!{% endblocktrans %}
            </button>
        </div>
    </div>
</form>
0 Likes

#2

nevermind. figured a way.

0 Likes