JSRB
JSRB

Reputation: 2623

How to pre-populate (Multiple)ChoiceField in Django based on previous user selection

I managed to store and update a selection of categories made by the user to the database. Now I want to pre-populate the MultipleChoiceSelect with this selection once the user queries the form again.

So my current form will always return all available categories. How can I apply the users personal selection to this to have his last selection pre-selected in the DOM?

# Models

class UserCategoryFilter(models.Model):
    """
    Saves the selected category filter by a user
    """
    user = models.ForeignKey(Account, on_delete=models.CASCADE)
    categories_selected = models.CharField(max_length=2000)


class Category(models.Model):
    """
    Model to store all available categories
    """
    poller_category = models.CharField(max_length=30)
# Form

class SelectCategoryForm(forms.Form):
    choices = forms.ModelMultipleChoiceField(queryset=Category.objects.all(),
                                             widget=forms.CheckboxSelectMultiple)
# View

@require_POST
def save_category_filter(request):

    [..]

        # Get the form instance
        filter_form = SelectCategoryForm(request.POST)

        # Form validation
        if filter_form.is_valid():


            # Get the cleaned data
            selection = filter_form.clean()

            # Check if user already has a filter instance
            instance_exists = UserCategoryFilter.objects.filter(user=request.user)

            # If not create, else update
            if not instance_exists:
                filter_instance = UserCategoryFilter(user=request.user,
                                                     categories_selected=selection)
                filter_instance.save()
            else:
                # Update existing instance
                UserCategoryFilter.objects.filter(user=request.user).update(categories_selected=selection)
                pass
        [..]

This is how the selection of the user is currently saved in categories_selected:

{'choices': <QuerySet [<Category: Sports>, <Category: Lifestyle>]>}

Upvotes: 1

Views: 817

Answers (2)

aaron
aaron

Reputation: 43098

This is how the selection of the user is currently saved in categories_selected:

{'choices': <QuerySet [<Category: Sports>, <Category: Lifestyle>]>}

That's not a friendly data structure. Instead, let's do ["Sports", "Lifestyle"].

# Get the cleaned data
selection = filter_form.clean()
selection = json.dumps([c.poller_category for c in selection['choices']])  # Add this

Now, we can json.loads the selection and .filter(poller_category__in=selection).

Then, we pass the initial choices in SelectCategoryForm(initial={'choices': choices}).

@require_GET
def select_category(request):
    choices = ()
    user_category_filter_queryset = UserCategoryFilter.objects.filter(user=request.user)
    selection = user_category_filter_queryset.values_list('categories_selected', flat=True).first()
    if selection:
        try:
            selection = json.loads(selection)
        except JSONDecodeError:
            pass
        if selection:
            choices = Category.objects.filter(poller_category__in=selection)
    form = SelectCategoryForm(initial={
        'choices': choices,
    })
    return render(request, 'select-category.html', {'form': form})

Upvotes: 1

Brian Destura
Brian Destura

Reputation: 12078

You can set the initial value of the choices field from when creating the form:

filter_form = SelectCategoryForm(
    request.POST, 
    initial={'choices': Category.objects.filter(<get_user's_category_here>)}
)

You didn't share the models so it's a bit tricky to build the query, but just change the filter to get the user's current categories.

Upvotes: 1

Related Questions