Multiple Select with ModelForm

Hello, I am a beginner in Django. Please excuse me if I have a stupid question.

I am trying to create a multiple select input with ManyToManyField and SelectMultiple. I can properly save the values to the database, but the selected values ​​are not visible in the multiple select input field on my edit page. I’m missing something but not sure what it is ?

Model.py

class Store(models.Model):
    Id =  models.AutoField(primary_key=True)
    Name = models.CharField(max_length=50)
    Location = models.CharField(max_length=50)
    Authorized_personals = models.ManyToManyField(settings.AUTH_USER_MODEL,default= None)
    Add_date = models.DateTimeField(auto_now_add=True, blank=True)
    Upd_date = models.DateTimeField(auto_now_add=True, blank=True)
    Add_userid = models.IntegerField(default = 0,blank=True)
    Upd_userid = models.IntegerField(default = 0,blank=True)

Form.py

class addStoreForm(ModelForm):
    Authorized_personals = forms.MultipleChoiceField(choices=[(user.id,user.first_name+" "+user.last_name ) for user in User.objects.all()],widget=forms.SelectMultiple)
    class Meta:
        model = Store
        fields = ["Name","Location","Authorized_personals"]
        widgets = {
            "Name" : forms.TextInput(attrs={'class':'form-control','placeholder':'Store Name'}),
            "Location" : forms.TextInput(attrs={'class':'form-control','placeholder':'Store Location'}),
        }

Views.py


def updStore(request,store_id):
    store = get_object_or_404(Store, pk=store_id)
    if request.method == "POST":
        form = addStoreForm(request.POST,instance=store)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse("updStore", args=(store_id,)))
    else:
        form = addStoreForm(instance=store)
        return render(request, "stok_analiz/addStore.html", {'form': form,"data":request})

Template html

{% extends "stok_analiz/base.html"%} {% block content %}
<form method="post" action="#">
    {% csrf_token %}
  <section class="">
    <div class="container-fluid" style="margin-top: 20px; height: 80vh">
        <div class="row">
            <div class="col-lg-12">
              <div class="row">
                <div class="col">{{form.Name}}</div>
              </div>
              <div class="row" style="margin-top: 20px;">
                <div class="col">{{form.Location}}</div>
              </div>
              <div class="row" style="margin-top: 20px;">
                <div class="col">{{form.Authorized_personals}}</div>
              </div>
            </div>
        </div>
        </section>
  <section class="">
    <div class="container-fluid">
      <button type="submit" class="btn btn-primary" style="float: right">
        Save
      </button>
    </div>
  </section>
</form>
{% endblock %}

Welcome @ressif3lt !

This isn’t going to work the way you want it to.

As a field definition, this is evaluated once, when the module is being loaded - not each time that an instance of this form is being created.

This sort of customization would need to be done in the __init__ method of the form class.

However, you are also defining this field as a MultipleChoiceField where it should be a ModelMultipleChoiceField. Instead of choices, it should be a queryset defined for these selections, which also allows for the initialization of the existing relationship with the Store instance.

1 Like

Hello Ken,
Thank your for replying.
I understand using queryset is much more proper way to do it rather than choices.
I dont want to set a str in my Store model, Beause i want to use it in diffrent ways.

Is it possible to create custom queryset or i have to use the model ?

I found a diffrent method to solve this issue by adding.
form["Authorized_personals"].initial = [c.pk for c in store.Authorized_personals.all()] in my views.py

But still not sure if its the right attitute

def updStore(request,store_id):
    store = get_object_or_404(Store, pk=store_id)
    if request.method == "POST":
        form = addStoreForm(request.POST,instance=store)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse("updStore", args=(store_id,)))
    else:
        form = addStoreForm(instance=store)
        form["Authorized_personals"].initial = [c.pk for c in store.Authorized_personals.all()]
        return render(request, "stok_analiz/addStore.html", {'form': form,"data":request})

It is the correct way to do it. If you want this to work properly, you want to use the ModelMultipleChoiceField.

You don’t need to use __str__, the docs I referenced show you how to use a custom label. (Side note: It’s not Store that would need the custom label, that’s the base class for the form. It’s the User model that would need to create the label.)

You have other options at well, but the label_from_instance is the easiest.

1 Like