modelformset_factory tidak menyimpan instance ke database

Saya mencoba membuat formset dengan instance di halaman saya, tetapi formset tidak menyimpan instance sama sekali. Penghematan bidang ekstra berfungsi dengan baik, tetapi saya tidak mengerti apa yang salah dengan instance. Saya memiliki banyak formulir di halaman, dan ini yang terakhir, yang tidak disimpan: Pandangan saya:

def details(request, username, slug):
    sm = Social_media.objects.all()
    profile = get_object_or_404(Profiles, user__username=username)
    site = get_object_or_404(MySites, slug=slug, user__username=username)
    ftp = get_object_or_404(FTP, site=site)
    FtpPathFormSet = forms.modelformset_factory(FtpPath,form=FtpPathForm,extra=1)
    sjim_ops = SjimOperations.objects.filter(user=request.user).order_by('-date')
    sjim_det = SjimDetails.objects.all()

    if request.method == 'POST':

    // other forms and logic 

       if 'change3' in request.POST:
            newsite = NewSiteForm(instance=site)
            newftp = NewFtpForm(instance=ftp)
            ftppath = FtpPathFormSet(request.POST)
            if ftppath.is_valid:
                print('here')
                i = 0
                instances = ftppath.save(commit=False)
                for instance in instances:
                    print('here2')
                    instance.ftp = ftp
                    instance.recursive = request.POST.get('form-'+str(i)+'-recursive')

                    if instance.recursive == 'on':
                        instance.recursive = True
                    else:
                        instance.recursive = False

                    instance.period = request.POST.get('form-'+str(i)+'-period')
                    try:
                        testconnect = ftplibr(ftp.host)
                        testconnect.login(user=ftp.ftpuser, passwd=ftp.password)
                        testconnect.cwd(instance.path)
                        instance.find = True
                        testconnect.quit()
                    except:
                        instance.find = False

                    ftppath.save()
                    i =+ 1
                return redirect('main:details', username=username, slug=site.slug)

model:

class FtpPath(models.Model):
    period = (
        ('Раз в сутки','Раз в сутки'),
        ('Раз в неделю','Раз в неделю'),
        ('Раз в 2 недели','Раз в 2 недели')
        )

    ftp = models.ForeignKey(FTP, on_delete=models.CASCADE)
    path = models.CharField(max_length=200, blank=True)
    period = models.CharField(choices=period, max_length=20, null=True, blank=True)
    find = models.BooleanField(null=True, default=False)
    recursive = models.BooleanField(null=True, default=False)

    class Meta:
        verbose_name = 'FTP path'
        verbose_name_plural = 'FTP paths'

Membentuk:

class FtpPathForm(forms.ModelForm):
    path = forms.CharField(widget=forms.TextInput(attrs={'type':'text','class':'effect-16'}), required=False)
    recursive = forms.BooleanField(widget=forms.CheckboxInput(attrs={}), required=False)
    period = forms.CharField(widget=forms.TextInput(attrs={'type':'text','style':'display:none','class':'period-input'}), required=False, label='')

    class Meta:
        model = FtpPath
        fields = ('path', 'recursive','period', 'find')

templat:

<form method="POST" id="path-form">
       {% csrf_token %}
       {{ ftppath.management_form }}
       <div class="info_tab_3col">
        <div class="form_title">Выбрать папки</div>
        <div class="form_folders">
        {% for ftppath in ftppath %}
        {% if ftppath.find.value == True %}
         <div class="form_folder">
         {% else %}
        <div class="form_folder form_folder_error">
        {% endif %}
        <div class="ftp_form_item">
        <div class="ftp_f_long ftp_f_long_i">
        <div class="input-effect">
        {{ ftppath.path }}
        <label>Путь на сервере</label>
        <span class="focus-border"></span>
        </div>
        </div>
        <div class="ftp_form_s ftp_form_s_i">
        <div class="checkbox_box">
        {{ ftppath.recursive }}
        <label for="id_form-{{ forloop.counter0 }}-recursive">
        <span><!-- This span is needed to create the "checkbox" element --></span>
        Рекурсивно
        </label>
        </div>
        </div>
        </div>
        <div class="ftp_form_item ftp_form_item_type">
        <div class="select_wrapper">
        {{ ftppath.period }}
        <div class="select_wrapper_val"></div>
        <span class="select_wrapper_label">Период</span>
        <span class="input_error error-ftppath"></span>
        <div class="select_list">
        <div class="select_list_item">Раз в сутки</div>
        <div class="select_list_item">Раз в неделю</div>
        <div class="select_list_item">Раз в 2 недели</div>
        </div>
        </div>
        </div>
           </div>
       {% endfor %}
       </div>
        <div class="form_button form_button_ftp">
               <button class="btn" type="submit" name="change3">
                   <span>Изменить</span>
                </button>
         </div>
      </div>
         </form>

Jadi formset dirender dengan baik di halaman: saya punya 1 instance dan satu kolom tambahan. Jika saya mengklik kirim kolom yang sudah ada contoh yang tidak berubah di database dan di halaman. Jika saya mengubah tampilan saya is_valid: ke is_valid():, sepertinya formulir disimpan di halaman, tetapi tidak di database, jadi jika saya pergi ke halaman lain, dan kembali lagi, tidak ada yang disimpan. Jika saya menghapus semua logika dalam pandangan saya, itu bekerja dengan cara yang sama, dan saya tidak tahu apa yang saya lakukan salah, tolong bantu!


person Andrej Vilenskij    schedule 11.12.2018    source sumber


Jawaban (1)


Masalahnya adalah Anda harus memanggil instance.save() alih-alih ftppath.save() di akhir perulangan Anda.

Perhatikan juga bahwa is_valid adalah sebuah metode, Anda perlu memanggilnya: if ftppath.is_valid():

Dan juga perhatikan, sangat tidak Pythonis untuk menyimpan penghitung seperti itu dan menambahnya secara manual. Sebagai gantinya, gunakan enumerate.

Namun sebenarnya, Anda tidak ingin menggunakan keduanya; daripada mendapatkan data dari POST dengan menggabungkan ID formulir, Anda harus mendapatkannya dari formulir yang dibersihkan_data itu sendiri. Jika Anda melakukannya, maka Anda tidak perlu membandingkannya secara manual dengan "on"; itulah yang sudah dilakukan oleh proses pembersihan formulir untuk Anda.

Jadi, Anda harus mengulangi formulir tersebut, lalu menyimpan masing-masing formulir:

if ftppath.is_valid():
    for form in ftppath.forms:
        instance = form.save(commit=False)
        instance.ftp = ftp
        instance.recursive = form.cleaned_data['recursive']
        instance.period = form.cleaned_data['period']
        try:
           ...
        except ftplib.all_errors:   # always use a specific error
           ...
        instance.save()

Namun yang terakhir, apakah Anda yakin perlu menyetel kedua nilai tersebut secara manual? Itu adalah bidang pada model dan ditentukan dalam atribut bidang formulir, jadi Django seharusnya sudah menyetelnya untuk Anda.

person Daniel Roseman    schedule 11.12.2018
comment
Terima kasih banyak atas saran Anda tentang cara membuat kode saya lebih baik, dan jawaban Anda. Masalahnya sepenuhnya ada di bidang id, yang diperlukan pada halaman untuk setiap formulir di formset. Itu sebabnya formset tidak bisa lolos validasi. - person Andrej Vilenskij; 11.12.2018