Django form with multi input from loop save only last record to database

Question:

I have a problem with saving data from a form in django. Only the last record is saved. I generate a list of dates (days of the month) in the view and display it in the form in templates along with the fields next to the type. Everything is displayed correctly in templates, but when I submit to, only the last record from the form appears in the save view. What am I doing wrong, can someone help?

forms.py


class DoctorsSchedule(forms.ModelForm):
    # work_hours = models.CharField(max_length=50, blank=True, null=True, default='8:00-21:00')
    # official_hours = models.CharField(max_length=50, blank=True, null=True, default='8:00-19:00')
    class Meta:
        model = DoctorSchedule

        fields = ['date', 'day_type', 'work_hours', 'scheme', 'official_hours']

model.py


class DoctorSchedule(models.Model):

    id = models.AutoField(primary_key=True, unique=True)
    date = models.DateField(blank=True, null=True)
    day_type = models.CharField(max_length=255, blank=True, null=True, default='Pracujący')
    work_hours = models.CharField(max_length=50, blank=True, null=True, default='8:00-21:00')
    scheme = models.CharField(max_length=255, blank=True, null=True, default='20')
    official_hours = models.CharField(max_length=50, blank=True, null=True, default='8:00-19:00')

    def __str__(self):
        return self.date

view.py

 
def terminarz(request):

    today = datetime.now()
    now = date.today()
    locale.setlocale(locale.LC_TIME, 'pl_PL')

    def months():

        months = {'1': 'Styczeń', '2': 'Luty', '3': 'Marzec', '4': 'Kwiecień', '5': 'Maj', '6': 'Czerwiec',
                  '7': 'Lipiec',
                  '8': 'Sierpień', '9': 'Wrzesień', '10': 'Październik', '11': 'Listopad', '12': 'Grudzień'}
        return months

    ##################### days of month list ######################################
    def days_of_month_list():
        if request.GET.get('year') and request.GET.get('month'):

            y = int(request.GET.get('year'))
            m = int(request.GET.get('month'))
            btn_y = int(request.GET.get('year'))

        else:

            y = today.year
            m = today.month
            btn_y = today.year

        date_list = {}
        for d in range(1, monthrange(y, m)[1] + 1):
            x = '{:04d}-{:02d}-{:02d}'.format(y, m, d)
            dayName = datetime.strptime(x, '%Y-%m-%d').weekday()
            date_list[x] = calendar.day_name[dayName].capitalize()

        ################### end days of month list #################################

        return date_list

    months = months()
    date_list = days_of_month_list()

    btn_today = today.year
    btn_today_1 = today.year + 1
    btn_today_2 = today.year + 2

    if request.GET.get('year') and request.GET.get('month'):
        btn_y = int(request.GET.get('year'))

    else:
        btn_y = today.year

    if request.method == 'POST':
        form = DoctorsSchedule(request.POST)
        if form.is_valid():
              
              form.save()

        else:
            print(form.is_valid())  # form contains data and errors
            print(form.errors)
            form = DoctorsSchedule()
    else:
        form = DoctorsSchedule

    context = {
        'form': form,
        'today': today,
        'now': now,
        'months': months,
        'date_list': date_list,
        'btn_today': btn_today,
        'btn_today_1': btn_today_1,
        'btn_today_2': btn_today_2
    }
    return render(request, "vita/panel/terminarz.html", context)

templates.html

<div class="card-body">

     <form  method="POST" enctype="multipart/form-data">
         {% csrf_token %}
         {{ form.as_p }}
        <div class="row p-3 text-center">
          {% include 'vita/messages.html' %}
            <div class="text-center p-2">
              <a role="button" class="btn btn-info" href='terminarz?month={{today.month}}&year={{btn_today}}'>{{ btn_today }}</a>
              <a role="button" class="btn btn-info" href='terminarz?month={{today.month}}&year={{ btn_today_1 }}'>{{ btn_today_1 }}</a>
              <a role="button" class="btn btn-info" href='terminarz?month={{today.month}}&year={{ btn_today_2 }}'>{{ btn_today_2 }}</a>
            </div>
                  {% for nr, month in months.items %}

                   <div class="col text-center">
                   {% if btn_y == btn_today_1 %}
                     <a role="button" class="btn btn-primary p-2" href="terminarz?month={{nr}}&year={{btn_today_1}}">{{month|upper}}</a>
                   {% elif btn_y == btn_today_2 %}
                      <a role="button" class="btn btn-primary p-2" href="terminarz?month={{nr}}&year={{btn_today_2}}">{{month|upper}}</a>
                   {% else %}
                      <a role="button" class="btn btn-primary p-2" href="terminarz?month={{nr}}&year={{btn_today}}">{{month|upper}}</a>
                   {% endif %}
                   </div>
                  {% endfor %}
                  </div>
                  <table class="table table-striped table-sm table-responsive">
                    <thead class="text-light" style="background: #26396F;">
                       <tr>
                         <th>Data</th>
                         <th class="text-center">Dzień pracy</th>
                         <th class="text-center">Godziny oficjalne</th>
                         <th class="text-center">Godziny pracy</th>
                         <th class="text-center">Przedział</th>
                         <th class="text-center">Ilość wizyt</th>
                       </tr>
                     </thead>
                     <tbody>
                        {% for date, day in date_list.items %}
                          <tr>
                           <td class="p-1">
                             <a href="/panel/{{ date }}">
                              <b>{{ date }}</b> -
                              {% if day == 'Sobota' or day == 'Niedziela'  %}
                              <span class="text-danger">{{ day }}</span>
                              {% else %}
                              <span class="text-success">{{ day }}</span>
                              {% endif %}
                            </a>
                            <input type="hidden" name="data" value="{{date}}" />
                         </td>
                         <td class="p-1">
                          <select name="day_type">
                           {% if day == 'Sobota' or day == 'Niedziela'  %}
                            <option value="Wolny" selected>Wolny</option>
                            <option value="Pracujący">Pracujący</option>
                           {% else %}
                            <option value="Pracujący" selected>Pracujący</option>
                            <option value="Wolny" >Wolny</option>
                           {% endif %}

                           </select>
                          </td>
                          {% if day == 'Sobota' or day == 'Niedziela'  %}
                             <td></td>
                             <td></td>
                             <td></td>
                             <td></td>
                          {% else %}
                            <td class="p-1 text-center"><input name="official_hours_start" type="time" value="08:00" />-<input name="official_hours_end" type="time" value="19:00" /></td>
                            <td class="p-1 text-center"><input name="work_hours_start" type="time" value="08:00" />-<input name="work_hours_end" type="time" value="21:00" /></td>
                            <td class="p-1 text-center">
                               <select name="scheme">
                                <option value="10">10 min</option>
                                <option value="15">15 min</option>
                                <option value="20">20 min</option>
                                <option value="25">25 min</option>
                                <option value="30" selected>30 min</option>
                               </select>
                            </td>
                            <td class="p-1 text-center">0</td>
                          {% endif %}

                        </tr>

                       {% endfor %}
                      </tbody>
                  </table>
              <div class="text-center"><input class="btn btn-success" type="submit" name="update_schedule" value="Uaktualnij terminarz" /></div>
              </form>
 </div>
Asked By: Erni

||

Answers:

You need to pass the form to the template. Now this is OK in your code. But I thing the problem come with the way you manage your form lifecycle.

If I understand your code the workflow is the following:

  1. A GET REQUEST Initialize to form with the current states
  2. You use several GET requests (buttons) to update states/values
  3. Then A POST request to save the form

And you end up with some bad values ?

It’s quite Normal …

let me explain if I’m correct :

  1. First GET You initialize the page data and the form with the an initial state (but maybe not I don’t see it)
  2. In other intermediate steps, you use GET request where you modify the page data with the current state but not the form
  3. Finally you save the form with ??? …

How to fix it:

  1. At each intermediate step update the form data but don’t save it. Therefore at the last step, The POST request will normally save correct data

change this:

...
else:
   form = DoctorsSchedule()
...
# into

...
else:
   form = DoctorsSchedule({
      'date': <place here the current correct value for this field>, 
      'day_type': <place here the current correct value for this field>, 
      etc...
   })

and please replace

         form = DoctorsSchedule  # this is a Form class not an instance
# with 
         form = DoctorsSchedule()
# or better
         form = DoctorsSchedule(initial_data={<your data>})
# or also better
         form = DoctorsSchedule(<some DoctorsSchedule (the Model one) instance>)

you should use a form instance not a form class in your template
and you should rename one of DoctorsSchedule Model or DoctorsSchedule Form in order to avoid confusion

  1. You can also Write some javascript code in order to propagate page changes into the form input fields data directly in the page
Answered By: franckfournier

Update:

I print(request.POST) and this is the result. When I use form.is_valid it’s not show errors and form.save() save only last record from QueryDict

<QueryDict: {'csrfmiddlewaretoken': ['mzIuVQEY1a6s15UEInWD5xZOm6HapMyOAikLItkMTvIGOizxIU9NErfh4SUkfiR9'], 'data': ['2022-12-01', '2022-12-02', '2022-12-03', '2022-12-04', '2022-12-05', '2022-12-06', '2022-12-07', '2022-12-08', '2022
-12-09', '2022-12-10', '2022-12-11', '2022-12-12', '2022-12-13', '2022-12-14', '2022-12-15', '2022-12-16', '2022-12-17', '2022-12-18', '2022-12-19', '2022-12-20', '2022-12-21', '2022-12-22', '2022-12-23', '2022-12-24', '2022-12-25',
 '2022-12-26', '2022-12-27', '2022-12-28', '2022-12-29', '2022-12-30', '2022-12-31'], 'day_type': ['Pracujący', 'Pracujący', 'Wolny', 'Wolny', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Wolny', 'Wolny', 'Pracu
jący', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Wolny', 'Wolny', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Wolny', 'Wolny', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Pracujący', 'Wolny'
], 'official_hours_start': ['08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19
:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00', '08:00-19:00'], 'work_hours_start': ['08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21
:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00', '08:00-21:00'], 'scheme': ['30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30', '30'], 'update_schedule': ['Uaktualnij terminarz']}>
Answered By: Erni

This is resolve my problem


        if request.method == "POST":
            if form.is_valid():
                  x1 = request.POST #get data from request and getlist from QueryDict
                  data_l = x1.getlist('data')
                  day_type_l = x1.getlist('day_type')
                  work_hours_l = x1.getlist('work_hours_start')
                  scheme_l = x1.getlist('scheme')
                  official_hours_l = x1.getlist('official_hours_start')

                  for date, day_type, work_hours, official_hours, scheme in zip(data_l,day_type_l,work_hours_l,official_hours_l,scheme_l):

                      post_dict = {'date': date, 'day_type': day_type, 'work_hours': work_hours, 'official_hours': official_hours, 'scheme': scheme}
                      
                      form = DoctorsScheduleForm(post_dict)
                      form.save()

            else:
                form = DoctorsScheduleForm()
Answered By: Erni
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.