保存模型时何时以及如何创建多对多关系?

时间:2011-10-07 04:56:06

标签: python django django-models

我有一组文档对象和标签对象,我想要链接这两个对象。这是一种典型的多对多关系。我有以下代码:

Models.py:

class Document(models.Model):
    title = models.CharField(max_length=50, unique=True)
    title_slug = models.SlugField(max_length=50, unique=True, editable=False)
    labels = models.ManyToManyField('Label')

    def save(self, *args, **kwargs):
        self.title_slug = slugify(self.title)
        super(Document, self).save(*args, **kwargs)

class Label(models.Model):
    name = models.CharField(max_length=40, unique=True)
    slug = models.SlugField(max_length=40, unique=True, editable=False)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(Document, self).save(*args, **kwargs)

Views.py:

class DocumentForm(ModelForm):
    class Meta:
        model = Document
        fields = ["title","labels"]

def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            new_document = form.save()
            return HttpResponseRedirect("/thanks/")

    else:
        form = DocumentForm()

    return render_to_response('upload_page.html', {'form':form}, context_instance=RequestContext(request))

当我上传文档时,它会被添加到数据库中,但是没有创建标签或与文档关联。我是否需要在Document的save()函数中明确添加一些内容才能实现?或者在Views.py文件中的某个地方?我想它会像:

  • 检查已添加的标签是否已存在
  • 如果没有,则创建新标签
  • 抓住当前的document_id和新的/现有的label_id
  • 将记录添加到document_labels表(为多对多关系自动创建)

我觉得这是非常标准的功能,我认为它将内置于django中的多对多关系中,但到目前为止它似乎并不适合我。我试图避免在这里重新发明轮子。 django有点新鲜。

提前致谢!

5 个答案:

答案 0 :(得分:4)

正如其他人所说,你不能保存在一次性Document对象及其ManyToMany字段中,因为django创建“intermediatary join table”,这需要Document对象的ID,这在那时没有定义。

ModelForm中有一个save_m2m函数,应该由Form本身调用,如the doc

中所述

但是,如果它不起作用,也许一个技巧是在视图函数中调用save_m2m,如下所示:

def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            new_document = form.save()
            form.save_m2m()
            return HttpResponseRedirect("/thanks/")

    else:
        form = DocumentForm()

    return render_to_response('upload_page.html', {'form':form}, context_instance=RequestContext(request))

希望它有所帮助, 斯特凡

答案 1 :(得分:2)

我建议您参考Django Admin应用程序在这种情况下的工作原理。通常,这将是一个两阶段的操作;首先,您要创建多个标签,然后创建一个文档,从多选列表中选择您想要关联的标签,然后保存。然后,Django会通过文档和标签之间的多对多表自动关联列表中选择的标签。

如果您希望一步到位,可以使用inline formsets。管理员应用程序主要用于外键(例如,轮询和问题),但它们也可以在有限的程度上使用多对多关系。

请注意,内联表单可能很棘手。如果您可以将操作拆分为两个单独的视图,则会更容易。只需创建一个用于创建标签的视图和另一个用于创建文档的视图,该视图将自动具有用于选择与文档关联的标签的列表。

答案 2 :(得分:2)

您在Document模型中的标签是一个M2M字段,因此最终会在呈现的表单中呈现多选(显示系统中可用的所有标签)。

假设你想要什么,

views.py

中的

def upload_document(request):
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            labels = request.POST.getlist('labels')
            new_document = form.save()
            for label_id in labels: # we're only going to add currently defined labels
                label = Label.objects.get(id=int(label_id))
                new_document.labels.add(label)
            new_document.save()
            return HttpResponseRedirect("/thanks/")
    else:
        form = DocumentForm()

    return render_to_response('doc_form.html', {'form':form}, context_instance=RequestContext(request))

我在 models.py

中更新了Label的模型
class Label(models.Model):
    name = models.CharField(max_length=40, unique=True)
    slug = models.SlugField(max_length=40, unique=True, editable=False)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(Label, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.name

如果您考虑让用户同时创建标签,则需要使用其他内容覆盖表单中的标签字段,例如输入字段。例如,如果您指示用户输入用逗号分隔的标签,那么您将拥有更新的views.py,

for label in labels: # labels entered by user
    try:
        lbl = Label.objects.get(name='label')
    except Label.DoesNotExist:
        lbl = None

    if not lbl:
        lbl = Label()
        lbl.name = label
        lbl.save()

    newDoc.labels.add(lbl)

newDoc.save()

希望这可以解决您的问题或为您提供一些工作。

答案 3 :(得分:2)

如果您了解django

的表格和表格,我认为这很容易解决

也许这个文档可以帮到你:

https://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#inline-formsets

答案 4 :(得分:0)

在save()上不会自动创建链接对象。 您应该为标签创建另一个表单,并明确保存它们。