#ManyToManyField won't save to database.

48 messages · Page 1 of 1 (latest)

hollow jewel
#

I have a form which I am posting to my model, saved in a postgreSQL database. All of the fields are saving except for a ManyToMany field I have as part of my model for "opening_days". On the front end this form field is a ModelMultipleChoiceField, with CheckboxSelectMultiple. Within my view, I have verified that the checkbox selections are being picked up with a print statement, however, I cannot get them to save to my database. I would expect it to save as a list of strings, with each item being a day of the week. Days of the week are defined in their own model, and used in the model I am trying to save to via ManyToMany field. I cannot work out how to fix it and have been stuck for days. Further details with code snippets can be found here: https://stackoverflow.com/questions/75932404/checkboxselectmultiple-values-not-saving-to-django-database

gloomy copper
#
booking_management.opening_days.set(form.cleaned_data['opening_days'])
...
form.save_m2m()

Aren't those two lines doing the same thing?

#
form.instance.user = self.request.user
form.instance.listing = get_object_or_404(Listing, slug=self.kwargs['slug'])
booking_management = form.save()
return redirect(self.get_next_url())

Could you use that instead of commit=False and save_m2m?

hollow jewel
#

Hi @gloomy copper , thanks for getting back to me. I've commented out the save_m2m, however I dont think this was the problem. I've tried so many different approaches to try and get it to save a value to the opening_days field and nothing seems to work. I've tried loooping through the data from the form and using .add() for each value too but this still doesn't save anything in the field for the model instance:

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            form.instance.user = self.request.user
            booking_management = form.save(commit=False)
            listing = get_object_or_404(Listing, slug=self.kwargs['slug'])
            booking_management.listing = listing
            booking_management.save()

            instance = get_object_or_404(BookingManagement, id=booking_management.id)
            print(instance.opening_days)
            day_list = request.POST.getlist('opening_days')
            print(day_list)
            for day in day_list:
                instance.opening_days.add(day)
                instance.save()
            next_path = self.get_next_url()
            return redirect(next_path)

Feel like I am slowly losing hope and really can't work out the issue here

gloomy copper
#

Did you try what I suggested?

hollow jewel
#

@gloomy copper do you mean like this:

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            form.instance.user = self.request.user
            form.instance.listing = get_object_or_404(Listing, slug=self.kwargs['slug'])
            booking_management = form.save()
            booking_management.opening_days.set(form.cleaned_data['opening_days'])
            next_path = self.get_next_url()
            return redirect(next_path)

?

#

In which case, the 'opening_days' field for that instance is still null

#

upon creation of a new instance

gloomy copper
#

No, I meant ```python
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
form.instance.user = self.request.user
form.instance.listing = get_object_or_404(Listing, slug=self.kwargs['slug'])
booking_management = form.save()
return redirect(self.get_next_url())

#

If that doesn't work, then you need to confirm that opening_days is being specified properly and Django is converting that to the correct instances.

#

Use print statements or a debugger to confirm what the server is receiving. And use the browser's developer tools' network panel to confirm that the browser is sending.

hollow jewel
# gloomy copper No, I meant ```python def post(self, request, *args, **kwargs): form...

Yeah I tried that initially and have just tried that again 😦 still no luck

Have been using print statements, and they are printing the expected ouputs when getting the data from the 'opening_days' form field. It is literally just when it comes to saving it to the database for that model instance that it's not working.

(Thanks so much for your help btw!)

#

That's why I am so confused, it's sending the data and I can see that when using print statements, but it just wont write to the database and I've tried so many different methods to do so and none of them have worked.

gloomy copper
hollow jewel
# gloomy copper Is anything else manipulating the database? Have you confirmed that after you sa...

Okay thanks, I've printed that as I've added a new instance to my database, and this was the result:

[{'sql': 'SELECT "listings_daysofweek"."id", "listings_daysofweek"."day" FROM "listings_daysofweek" WHERE "listings_daysofweek"."id" IN (1, 2)', 'time': '0.001'}, {'sql': 'SELECT "listings_listing"."id", "listings_listing"."business_id", "listings_listing"."title", "listings_listing"."slug", "listings_listing"."activity_id", "listings_listing"."listing_image", "listings_listing"."price", "listings_listing"."accessible", "listings_listing"."description", "listings_listing"."date_added", "listings_listing"."active", "listings_listing"."address", "listings_listing"."town", "listings_listing"."county", "listings_listing"."post_code", "listings_listing"."country", "listings_listing"."longitude", "listings_listing"."latitude" FROM "listings_listing" WHERE "listings_listing"."slug" = \'gsretbstrebtr-4z7jlf\' LIMIT 21', 'time': '0.002'}, {'sql': 'INSERT INTO "listings_bookingmanagement" ("listing_id", "opening_time", "closing_time", "booking_slots_per_day", "max_bookings_per_time_slot", "standard_booking_duration", "min_booking_duration", "max_booking_duration", "booking_duration_increment", "min_people_per_booking", "max_people_per_booking", "turnaround_time", "time_slot_frequency", "max_people_per_time_slot", "max_people_per_day", "min_adults_per_booking", "max_children_per_adult", "min_age", "max_age", "child_max_age") VALUES (167, \'09:00:00\'::time, \'17:00:00\'::time, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) RETURNING "listings_bookingmanagement"."id"', 'time': '0.007'}, 
#
{'sql': 'SELECT "listings_daysofweek"."id" FROM "listings_daysofweek" INNER JOIN "listings_bookingmanagement_opening_days" ON ("listings_daysofweek"."id" = "listings_bookingmanagement_opening_days"."daysofweek_id") WHERE "listings_bookingmanagement_opening_days"."bookingmanagement_id" = 108', 'time': '0.001'}, {'sql': 'INSERT INTO "listings_bookingmanagement_opening_days" ("bookingmanagement_id", "daysofweek_id") VALUES (108, 1), (108, 2) ON CONFLICT DO NOTHING', 'time': '0.001'}]
#

Would I be right and thinking the issue could be here:

ON CONFLICT DO NOTHING', 'time': '0.001'}] ? But the problem is, I'm not sure what the conflict is and how to resolve this?

#

I wonder if it's something to do with the fact that I am trying to add multiple items to one instance, i.e. openings days could be Monday and Tuesday (as I selected in the above instance) which tries to write 1 & 2 (the pk values within my DaysOfWeek model for Monday and Tuesday) both to the opening_days field of this instance

#

But ultimately, that's what I need it to do as it should be possible to be open every day of the week if applicable

gloomy copper
#

There's no insert into the many2many's through table...

#

What was day_list = request.POST.getlist('opening_days') print(day_list) printing exactly?

hollow jewel
#

For the above instance, that would print:
['1', '2']

#

However, if I were to print:

print(form.cleaned_data['opening_days'])

I would get:
<QuerySet [<DaysOfWeek: Monday>, <DaysOfWeek: Tuesday>]>

gloomy copper
#

Okay, good. That's right.

#

Ah, never mind. I see the insert into the intermediate table now.

hollow jewel
#

That's the INNER JOIN right?

gloomy copper
#

No, this ```
{'sql': 'INSERT INTO "listings_bookingmanagement_opening_days" ("bookingmanagement_id", "daysofweek_id") VALUES (108, 1), (108, 2) ON CONFLICT DO NOTHING', 'time': '0.001'}

#

Did you check the database for a row matching those values?

hollow jewel
#

Yeah so I have a row with the 108 id value, but the opening_days field on that row is null

gloomy copper
#

Show me the query and result exactly as it is. opening_days shouldn't be a column.

hollow jewel
#

Just been looking at the entire table in pg_admin; with SELECT * FROM listings_bookingmanagement

gloomy copper
#

That's not the right table though.

#

listings_bookingmanagement_opening_days is the table you need to check. It's the intermediate / through relationship between the other two objects.

hollow jewel
#

oh my

#

I am so stupid

#

I've been checking the wrong table all this time

#

So sorry for wasting your time; is there anything I can do to show my appreciation

#

Just for my understanding then, the opening_days field will always be null because the opening_days are stored in a seperate table which acts as an intermediary between the two models

gloomy copper
#

What do you mean by "opening_days field"? Are you talking about the ORM, the column on a table, a form field?

hollow jewel
#

So as in within my BookingManagement model I have: opening_days = models.ManyToManyField(DaysOfWeek, verbose_name="Opening Days")

#

And that field is appearing in my database as null under my BookingManagement model

gloomy copper
#

If you're seeing a column opening_days on the table for BookingManagement, your database is out of sync with your models. That column should not exist from the models that you've shared with me.

#

models.ManyToManyField() does not create any columns on the table. It creates an intermediate/through table that allows you to map the two models together.

hollow jewel
#

Ahhh I see, that's a reminent from an older migration where I tried something different. That makes sense so I just need to remove that now

#

Thank you so much honestly I've been pulling my hair out over that

gloomy copper
#

Nope, I do this because I enjoy our community and want to help folks level up. If you insist on compensating me, you'll need to pay it forward to someone else you can help out.

hollow jewel
#

That's amazing to hear, I'll make sure I do! Thanks so much once again and hope you had a great Easter weekend!