Django Forms - 多对多关系

Dav*_*vid 2 python forms django

对Django来说相对较新,并尝试将标准实践拼凑在一起以处理表单中的M2M关系.我已经有模型和数据库平方.

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

到目前为止,我有:

models.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
Run Code Online (Sandbox Code Playgroud)

views.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 })
Run Code Online (Sandbox Code Playgroud)

forms.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)
Run Code Online (Sandbox Code Playgroud)

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

1)在视图'article_create'中,我不确定如何创建类别(ies)作为Article对象的一部分.在shell中,我必须通过调用save()来创建文章,然后在之后添加每个类别.我是否需要在此处执行类似的操作,例如创建文章然后遍历每个类别?示例代码表示赞赏.

2)尚未编写'article_edit',假设它与create非常相似,但我不确定是否或如何处理逻辑以将先前选择的类别与当前提交进行比较.或者,我是否应该删除正在编辑的文章的所有类别条目,并根据当前提交重新输入?这可能是最简单的.再次,此示例代码将有所帮助.

谢谢!

Mik*_*one 11

每个文件的评论......

models.py

class Category(models.Model):
    category = models.CharField(max_length=100)
Run Code Online (Sandbox Code Playgroud)

应该命名类别的名称name.一个名为categoryI 的字段希望是这样的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)
Run Code Online (Sandbox Code Playgroud)

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

    categories = models.ManyToManyField(Category, related_name="articles")
Run Code Online (Sandbox Code Playgroud)

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

get_object_or_404(Category, id=int(cat_id, 10)).articles.all()
Run Code Online (Sandbox Code Playgroud)

views.py

def article_detail(request, article_id=1):
Run Code Online (Sandbox Code Playgroud)

这里不要使用默认值.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']
            )
Run Code Online (Sandbox Code Playgroud)

通过使用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})
Run Code Online (Sandbox Code Playgroud)

forms.py

class ArticleForm(forms.Form):
Run Code Online (Sandbox Code Playgroud)

你真的应该使用ModelForm(文档在这里):

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

关于你的问题:

1)在视图'article_create'中,我不确定如何创建类别(ies)作为Article对象的一部分.在shell中,我必须通过调用save()来创建文章,然后在之后添加每个类别.我是否需要在此处执行类似的操作,例如创建文章然后遍历每个类别?示例代码表示赞赏.

IIRC,ModelForm.save()将为您照顾这一点.

2)尚未编写'article_edit',假设它与create非常相似,但我不确定是否或如何处理逻辑以将先前选择的类别与当前提交进行比较.或者,我是否应该删除正在编辑的文章的所有类别条目,并根据当前提交重新输入?这可能是最简单的.再次,此示例代码将有所帮助.

编辑几乎就像创建一样.您所要做的就是将原始对象与表单相关联.(通常,您可以从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})
Run Code Online (Sandbox Code Playgroud)

编辑:作为下面的jheld评论,您可以组合article_create并使用article_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})
Run Code Online (Sandbox Code Playgroud)

然后网址很简单:

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"),
Run Code Online (Sandbox Code Playgroud)