3

对 Django 来说相对较新,并试图将处理 M2M 关系的标准实践拼凑到一个表单中。我已经有了模型和数据库。

对于这个例子,我在我的项目中为文章编写了一个应用程序,并且我正在尝试添加类别。为简单起见,文章具有标题、正文、时间戳(不包含在表单中)和类别。我更喜欢复选框来表示一篇文章可以属于的一个或多个类别。

到目前为止,我有:

模型.py

class Category(models.Model):
    category = models.CharField(max_length=100)

    def __unicode__(self):
        return self.category


class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    category = models.ManyToManyField(Category)

    def __unicode__(self):
        return self.title

视图.py

def article_index(request):
    return render_to_response('article_index.html', {'articles': Article.objects.all()})

def article_detail(request, article_id=1):
    return render_to_response('article_detail.html', {'article': Article.objects.get(id=article_id)} )

def article_create(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ArticleForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            article = Article.objects.create(
                title=form.cleaned_data['title'],
                body=form.cleaned_data['body'],
                category=form.cleaned_data['category']
            )
            return redirect('article_index') # Redirect after POST
    else:
        form = ArticleForm() # An unbound form

    return render(request, 'article_form.html', { 'form': form })

表格.py

class ArticleForm(forms.Form):
    title = forms.CharField(required=True)
    body = forms.CharField(required=True, widget=forms.Textarea)
    category = forms.MultipleChoiceField(Category.objects.all(), widget=forms.CheckboxSelectMultiple)

我目前坚持的两个项目是:

1)在视图“article_create”中,我不确定如何将类别创建为文章对象的一部分。在 shell 中,我必须通过调用 save() 创建文章,然后添加每个类别。我是否需要在这里做类似的事情,例如创建文章然后遍历每个类别?示例代码表示赞赏。

2) 尚未编写“article_edit”代码,假设它与创建非常相似,但我不确定是否需要或如何处理将先前选择的类别与当前提交进行比较的逻辑。或者,我应该删除正在编辑的文章的所有类别条目,然后根据当前提交的内容重新输入它们吗?这可能是最简单的。同样,这方面的示例代码会有所帮助。

谢谢!

4

3 回答 3

11

每个文件的评论...

模型.py

class Category(models.Model):
    category = models.CharField(max_length=100)

类别的名称应命名为name。一个名为categoryId Expect 的字段类似于models.ForeignKey("Category").

class Article(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    pub_date = models.DateTimeField(auto_now_add=True)
    category = models.ManyToManyField(Category)

正如亚当指出的那样,这应该被命名为categories. 此外,它的反向(Category链接回的字段Article)应命名为articles。所以我们得到:

    categories = models.ManyToManyField(Category, related_name="articles")

所以现在你可以得到一个查询集,其中包含一个类别中的所有文章,例如:

get_object_or_404(Category, id=int(cat_id, 10)).articles.all()

视图.py

def article_detail(request, article_id=1):

不要在这里使用默认值。ID 1 没什么特别的,如果有人忘记了 ID,应该是报错。

def article_create(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ArticleForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            article = Article.objects.create(
                title=form.cleaned_data['title'],
                body=form.cleaned_data['body'],
                category=form.cleaned_data['category']
            )

通过使用 a ModelForm,这被简化为:

def article_create(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ArticleForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            form.save()
        return redirect('article_index') # Redirect after POST
    else:
        form = ArticleForm() # An unbound form

    return render(request, 'article_form.html', {'form': form})

表格.py

class ArticleForm(forms.Form):

您真的应该ModelForm改用(此处的文档):

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ["title", "body", "category"]
        widgets = {
            'body': forms.Textarea(),
            'category': forms.CheckboxSelectMultiple()
        }

关于你的问题:

1)在视图“article_create”中,我不确定如何将类别创建为文章对象的一部分。在 shell 中,我必须通过调用 save() 创建文章,然后添加每个类别。我是否需要在这里做类似的事情,例如创建文章然后遍历每个类别?示例代码表示赞赏。

IIRC,ModelForm.save()将为您解决这个问题。

2) 尚未编写“article_edit”代码,假设它与创建非常相似,但我不确定是否需要或如何处理将先前选择的类别与当前提交进行比较的逻辑。或者,我应该删除正在编辑的文章的所有类别条目,然后根据当前提交的内容重新输入它们吗?这可能是最简单的。同样,这方面的示例代码会有所帮助。

编辑几乎就像创建一样。您所要做的就是将原始对象与表单相关联。(通常,您可以从 URL 中找出原始对象是什么。)例如:

def article_edit(request, article_id):
    article = get_object_or_404(Article, id=int(article_id, 10))

    if request.method == 'POST': # If the form has been submitted...
        form = ArticleForm(request.POST, instance=article)
        if form.is_valid(): # All validation rules pass
            form.save()
        return redirect('article_index') # Redirect after POST
    else:
        form = ArticleForm(instance=article)

    return render(request, 'article_form.html', {'form': form})

编辑:正如下面的 jheld 评论,您可以将article_createarticle_edit合并为一种视图方法:

def article_modify(request, article_id=None):
    if article_id is not None:
        article = get_object_or_404(Article, id=int(article_id, 10))
    else:
        article = None

    if request.method == 'POST': # If the form has been submitted...
        form = ArticleForm(request.POST, instance=article)
        if form.is_valid(): # All validation rules pass
            form.save()
        return redirect('article_index') # Redirect after POST
    else:
        form = ArticleForm(instance=article)

    return render(request, 'article_form.html', {'form': form})

然后 URL 很简单:

url(r"^/article/edit/(?P<article_id>[0-9]+)$", "app.views.article_modify", name="edit"),
url(r"^/article/new$", "app.views.article_modify", name="new"),
于 2013-08-04T23:55:22.847 回答
2

我首先category将模型重命名为categories,并相应地更新相关代码 - 单数命名将是一个持续令人头疼的问题。

在这一点上,你已经很接近了。在提交文章时,在您的成功分支中,将类别分配为单独的语句。

article = Article.objects.create(
    title=form.cleaned_data['title'],
    body=form.cleaned_data['body']
)
# note changed plural name on the m2m attr & form field
article.categories.add(*form.cleaned_data['categories'])
# alternately
# for cat in form.cleaned_data['categories']:
#     article.categories.add(cat)
return redirect('article_index') # Redirect after POST

哦,还有,避免ModelForm. 自己连接表单实例管道要容易得多,这个问题会更加复杂ModelForm

对于编辑视图,是的,清除和重新添加是最简单的。有更有效的方法,但在它真正成为问题之前,没有什么值得复杂的。清除方法调用为article.categories.clear(),重新添加同上。

于 2013-08-04T23:10:03.173 回答
0

例如,您可以这样做:

  if todo_list_form.is_valid():
                todo_list = todo_list_form.save(commit=False)
                todo_list.save()
                todo_list_form.save_m2m()
于 2014-11-21T03:31:33.157 回答