applegrew / django-select2

This is a Django integration for Select2

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How do I load selected options for ModelSelect2MultipleWidget

zhugejun opened this issue · comments

Hi, I am trying to load selected options when using ModelSelect2MultipleWidget. But it never showed up. What values should I provide when passing the instance to the model form in the views? A list of ids? String?

Code Snippet
In forms.py

class ProfileForm(forms.ModelForm):

    class Meta:
        model = Profile
        fields = ('subjects',)
        widgets = {'subjects': ModelSelect2MultipleWidget(
            queryset=Subject.objects.all(),
            search_fields=['name__icontains'],
        )}

In models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    subjects = models.CharField(max_length=100, null=True, blank=True)

    def __str__(self):
        return f'<Profile {self.user.username}>'

@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
    else:
        instance.profile.save()


class Subject(models.Model):

    name = models.CharField(max_length=4)

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

In views.py

@login_required(login_url='/login')
def account(request):
    curr_user = request.user

    profile = get_object_or_404(Profile, user=request.user)

    if profile.subjects:
        subjects = Subject.objects.filter(
            name__in=profile.subjects.split(','))
        profile.subjects = [str(c.id) for c in subjects]
    else:
        profile.subjects = []
    # print(profile.subjects)

    form = ProfileForm(instance=profile)
    print('-'*50)
    print(form.fields['subjects'].initial)
    form.fields['subjects'].initial = ['1', '2']

    if request.method == 'POST':
        form = ProfileForm(request.POST, instance=profile)
        if form.is_valid():
            profile = form.save(commit=False)

            subjects = Subject.objects.filter(
                pk__in=[int(i) for i in request.POST.getlist('subjects')])

            profile.user_id = request.user.id
            profile.subjects = ','.join([s.name for s in subjects])
            form.save()
            messages.success(request,
                             'You have successfully updated your subjects for scheduling.')
            return redirect('account')

    return render(request, 'home/profile.html', {'form': form})

Thank you.

Hi @zhugejun thanks for reaching out and double thanks for providing so much code. That makes my job usually a lot easier. That being said, I honestly don't see a problem in your code, everything does look fine. Maybe the problem lies in the select2 setup. Have you checked your browser console? Do you see any errors? Did you configure the cache correctly and did you add jQuery to your template as well as the form media?
Best, Joe

I guess I was not clear enough. Let me give you one example.

Assume I have one user and the user has two subjects: MATH and ENGL. As you can see, I save the subjects as String. So it will be MATH,ENGL as below in the database.

user_id subjects
1 MATH,ENGL

When I view the account or profile page, it doesn't show that these two subjects are selected. That's why I put the code below to debug.

    if profile.subjects:
        subjects = Subject.objects.filter(
            name__in=profile.subjects.split(','))
        profile.subjects = [str(c.id) for c in subjects]
    else:
        profile.subjects = []
    # print(profile.subjects)

    form = ProfileForm(instance=profile)
    print('-'*50)
    print(form.fields['subjects'].initial)
    form.fields['subjects'].initial = ['1', '2']

My question is what I should provide when view the account page so that the widget will load the selected subjects? Do I provide a list like ['MATH', 'ENGL'] or their ids [1, 2] or just just a String? None of them actually worked for me. I hope this helps. Thank you.

Hi @zhugejun thanks for expanding on your use case a little. Can you check the browser next? What does the HTML that is being rendered look like? I can't see any error in your code thus far.
Best, Joe

Here is the HTML code after being rendered.

<span class="select2 select2-container select2-container--default" dir="ltr" data-select2-id="1"
    style="width: 600px;">
    <span class="selection">
        <span class="select2-selection select2-selection--multiple"
            role="combobox" aria-haspopup="true" aria-expanded="false" tabindex="-1" aria-disabled="false">
            <ul class="select2-selection__rendered">
                <li class="select2-search select2-search--inline"><input class="select2-search__field" type="search"
                        tabindex="0" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false"
                        role="searchbox" aria-autocomplete="list" placeholder="" style="width: 0.75em;"></li>
            </ul>
        </span>
    </span>
    <span class="dropdown-wrapper" aria-hidden="true"></span>
</span>

I have one user with 2 subjects as I mentioned before. But it didn't show up here.

Hi @codingjoe I just figured how to load the selected options. I need to use MultipleChoiceField inside the ProfileForm. Thanks for this awesome package.