1

I used to have a Multi select box in my form filled with a JSON list

forms.py

FRUIT_CHOICES= [
    ('Oranges', 'Oranges'),
    ('Cantaloupes', 'Cantaloupes'),
    ('Mangoes', 'Mangoes'),
    ('Honeydews', 'Honeydews')
    ]

class AddRecordForm(forms.ModelForm):
    testrever = forms.MultipleChoiceField(choices=FRUIT_CHOICES)

And so I used a JSON field in my models.py like this

models.py

class Record(models.Model):
    testrever = models.JSONField(max_length=500)

It worked like a charm and I was able to save the selection and load it back when the form was recalled.

But now in forms.py I need to modify the forms.MultipleChoiceField to forms.ModelMultipleChoiceField which is populated by a queryset like this

class AddRecordForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        self.storage_pk = kwargs.pop('pk')
        super(AddRecordForm, self).__init__(*args, **kwargs)
        self.fields['testrever'].queryset = BDC.objects.filter(ref=self.storage_pk).values_list('model', flat=True) 


    testrever = forms.ModelMultipleChoiceField(widget=forms.widgets.SelectMultiple, queryset=None)

class Meta:
    model = Record
    exclude = ("user",)
    fields = ['id','first_name','last_name','email', 'phone' ,'address', 'city' ,'state' ,'zipcode' ,'regarde' ,'testrever']

So now my models.JSONField in models.py is no longer working since I'm not rendering a JSON list format anymore.

What kind of field should I use in models.py to make it work again?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Nico44044
  • 125
  • 8

1 Answers1

1

If you pick zero, one or multiple items of another model, you use a ManyToManyField [Djangp-doc]:

class Fruit(models.Model):
    name = models.CharField(max_length=128, unique=True)

    def __str__(self):
        return name


class Record(models.Model):
    testrever = models.ManyToManyField(Fruit, blank=True)

This also uses a ModelMultipleChoice field as default form field. So you only have to plug in a different widget:

class RecordForm(forms.ModelForm):
    class Meta:
        model = Record
        fields = '__all__'
        widgets = {'testrever': forms.SelectMultiple}

while most databases offer a JSON field nowadays, which is useful for unstructered data, for structured data, using a JSONField is not a good idea as it violates first normal form [wiki].

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Thanks for your help. Still don't get it though (sorry) what is the class Fruit for ? since I want to populate the widget with a queryset in class AddRecordForm? Should i get rid of the forms.ModelMultipleChoiceField in forms.py ? i don't get the widgets = {'testrever': forms.SelectMultiple} part either as i specify it in the forms.ModelMultipleChoiceField(widget=forms.widgets.SelectMultiple. already – Nico44044 May 17 '23 at 19:45
  • And thanks for the advice about JSONField, I didn't know that. – Nico44044 May 17 '23 at 19:47
  • @Nico44044: since you used `FRUIT_CHOICES` I used the model `Fruit`, but likely here it is `BDC`, you can indeed still set the queryset in the constructor as you did. – Willem Van Onsem May 17 '23 at 20:33
  • I made the changes as you told but i have an error : (1146, "Table 'data.website_record_testrever' doesn't exist") It adds "_testrever" at the end and I don't know why. It appens because of this line in models.py in the class Record: testrever = models.ManyToManyField(BDC, blank=True) – Nico44044 May 17 '23 at 21:33
  • @`Nico44044: because an m2m is implemented as a junction table in a relational database. You need to make migrations and migrate the database. – Willem Van Onsem May 17 '23 at 21:39
  • Ok. My database is Mysql do you think it’s a problem ? Is there something special to do as create a new table ? Or do i just have to make django migration from manage.py ? Sorry it’s my first django app … – Nico44044 May 17 '23 at 21:56
  • @Nico44044: you use `manage.py makemigrations` and `manage.py migrate` to create a migration, that migration will construct the junction table. – Willem Van Onsem May 17 '23 at 21:58
  • I did the migration but I have this message error : "TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use testrever.set() instead." – Nico44044 May 18 '23 at 08:31
  • @Nico44044: that is by assigning it with the model form? You can indeed not make a `Record(testrever=...)` for example, or `my_record.testrever = ...` but that is than likely in a view. since a `ModelForm` can handle that properly. – Willem Van Onsem May 18 '23 at 08:46