Django ModelForm 无法使用 ModelForm 和 save_m2m 保存 "many to many" 记录

Django ModelForm fails to save "many to many" records using ModelForm and save_m2m

我是 Django 的新手,正在使用这个项目来学习它。我能够保存日志记录,但多对多关系不起作用。

这个 'create' 视图显示了正确的形式,包括带有所有列出的密码的 multi-select 框(来自密码模型)。提交表单时,多对多记录不保存,但日志保存正常。

我找到了很多不同的答案,有些是针对 python 2.7 的,但这是基于 [Django 文档][1] 的最简单的方法。非常感谢任何帮助。

此外,这种关系在管理部分工作得很好,所以我认为它与表单 and/or 查看和保存有关。

models.py

from django.db import models
from crypto.models import Crypto as CryptoModel

class Journal(models.Model):
    title = models.CharField(max_length=200, help_text='Journal Title', blank=False, null=False)
    content = models.TextField(max_length=2000, help_text='Journal Content (HTML OK)', blank=False, null=False)
    crypto_id = models.ManyToManyField(CryptoModel, blank=True)    
    created = models.DateTimeField(help_text='Created', auto_now_add=True, null=True)

    def __str__(self):
        return self.title  ## String for representing the Model object, usually name field or title

forms.py

from django.forms import ModelForm, ModelMultipleChoiceField, widgets
from journal.models import Journal as JournalModel
from crypto.models import Crypto as CryptoModel

class JournalForm(ModelForm):
    # select multiple items box
    cryptos = ModelMultipleChoiceField(widget=widgets.SelectMultiple(attrs={'size': 30}), queryset=CryptoModel.objects.all())

    class Meta:
        model = JournalModel
        fields = [
            "title",
            "content",
        ]
        labels = {
            'title': 'Journal Title',
        }
        required = [
            "title", # same as model
            "content",  # same as model
        ]

views.py

from journal.forms import JournalForm
from django.utils import timezone
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from journal.models import Journal as JournalModel

    def Create(request):

        if request.method == "POST":
            form = JournalForm(request.POST) # form instance
            context = {'form': form} # if errors, keep the form data on next page load

            journal = form.save(commit=False)  # False needed for many-to-many
            journal.title = form.cleaned_data["title"]
            journal.content = form.cleaned_data["content"]
            journal.created = timezone.now()
            journal.save()  # save the form journal data, now we have a PK
            form.save_m2m()  # save the 'form' using ManytoMany method

            return HttpResponseRedirect('/journal/')

        form = JournalForm()
        context = {'form': form}

        return render(request, 'journal/create.html', context)

models.py 2

from django.db import models
from crypto.models import Crypto

class Journal(models.Model):
    title = models.CharField(max_length=200, help_text='Journal Title', blank=False, null=False)
    content = models.TextField(max_length=2000, help_text='Journal Content (HTML OK)', blank=False, null=False)
    crypto_id = models.ManyToManyField(Crypto, blank=True)    
    created = models.DateTimeField(help_text='Created', auto_now_add=True, null=True)

    def __str__(self):
        return self.title  ## String for representing the Model object, usually name field or title

forms.py 2

from django.forms import ModelForm, ModelMultipleChoiceField, widgets
from journal.models import Journal
from crypto.models import Crypto

class JournalForm(ModelForm):
    # select multiple items box
cryptos = ModelMultipleChoiceField(widget=widgets.SelectMultiple(attrs={'size': 30}), queryset=Crypto.objects.all())

class Meta:
    model = JournalModel
    fields = [
        "title",
        "content",
        "cryptos",
    ]

views.py 2

from journal.forms import JournalForm
from django.utils import timezone
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from journal.models import Journal

def Create(request):

    if request.method == "POST":
        form = JournalForm(request.POST) # form instance
        context = {'form': form} # if errors, keep the form data on next page load

        journal = form.save(commit=False)  # False needed for many-to-many
        journal.created = timezone.now()
        journal.save()  # save the form journal data, now we have a PK

        journal.crypto_id.set(form.cleaned_data.get("cryptos")) # must be after "save"
        form.save_m2m()  # save the 'form' using ManytoMany method

        return HttpResponseRedirect('/journal/')

    form = JournalForm()
    context = {'form': form}

    return render(request, 'journal/create.html', context)

您将模型和表单字段称为不同的东西; Django 无法知道它们与同一领域相关。表单名称 - crypos - 是正确的,您应该将模型字段重命名为该名称。

此外,您还没有在 fields 列表中指定字段,因此 Django 甚至不会尝试在模型上设置它。

请注意,在您看来,您不需要设置标题或内容,form.save 已经为您完成了这些设置。

希望这能解决您的问题,但是在保存您的日志实例后这一行 journal.crypto_id.set(form.cleaned_data.get("cryptos"))

谢谢阿卜杜拉,将 "journal.crypto_id.set(form.cleaned_data.get("cryptos"))" 添加到 VIEW 中解决了这个问题。 额外注意的是,这必须在保存 'journal' 表单之后但在保存多对多之前。

我更新了上面的 "models.py 2"、"forms.py 2" 和 "views.py 2" 部分。这是工作代码。

随时欢迎您。 是的,但在 journal.save() 之后。 并且设置不需要从表单调用 save() 。