2011-10-07 52 views
5

我有一組文檔對象和標籤對象,我希望鏈接這兩個對象。這是一種典型的多對多關係。我有以下代碼:何時以及如何在保存模型時創建多對多關係?

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)) 

當我上傳的文件,它就會被添加到數據庫中,但是沒有標籤正在創建或與文檔關聯。我是否需要向文檔的save()函數中明確添加某些內容以實現此目的?或者在Views.py文件的某個地方?我想像它會去是這樣的:

  • 檢查,看看如果這是要添加的標籤已經存在
  • 如果沒有,然後創建一個新的標籤
  • 既抓住當前DOCUMENT_ID和新的/現有label_id
  • 添加記錄到document_labels表(適用於許多一對多關係自動生成)

我覺得這是我認爲將內置於敢標準功能Django中的任何多對多的關係,但它似乎並沒有爲我工作到目前爲止。我試圖避免在這裏重新發明輪子。 django有點新鮮。

在此先感謝!

+0

您使用的形式?您是否通過其他方式添加「文檔」?你在說什麼save()方法?在'文檔','標籤'或表單?請提供一些保存對象的示例視圖/表單/其他代碼。 – andreaspelme

+0

我想我不確定應該使用哪種保存方法。因此,我的問題=]文檔全部通過網站上的表單添加。我已經更新了這個問題,以包含來自views.py –

回答

4

正如其他人所說,您不能保存在一次性Document對象及其ManyToMany字段中,因爲django創建了「intermediatary join table」,它需要Document對象的ID,該ID在此處未定義。

中有一個的ModelForm功能save_m2m,是應該由窗體本身被調用,如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)) 

希望它能幫助, 斯特凡

+0

啊,我發現了問題。爲了簡化我的例子中的代碼,我拿出了'new_document = form.save(commit = False)'這一行。我必須使用commit = False,因爲在將文檔保存到數據庫之前,我正在對文檔對象做更多的工作,但我不認爲這會影響m2m的保存。您鏈接到https://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#the-save-method正是我需要的,謝謝! –

2

我會建議參考Django Admin應用程序如何在這種情況下工作。通常情況下,這將是一個兩階段的操作;首先,您需要創建多個標籤,然後創建一個文檔,從多選列表中選擇您想要關聯的標籤,然後保存。然後,Django會通過文檔和標籤之間的多對多表來自動關聯列表中選定的標籤。

如果您希望一步完成此操作,則可以使用inline formsets。管理應用程序主要將這些用於外鍵(例如Poll和Questions),但它們也可以在有限程度上使用多對多關係。

被警告,內聯窗體可能會非常棘手。如果您可以將操作分成兩個單獨的視圖,則會更容易。只需創建一個用於創建標籤的視圖,另一個用於創建文檔,這將自動具有用於選擇要與文檔關聯的標籤的列表。

0

鏈接的對象不會在save()上自動創建。 您應該爲標籤創建另一個表單,並將其明確保存。

+0

的代碼我必須爲標籤製作另一個*表單*這是否意味着從「文檔」窗體中刪除「標籤」字段,然後同時保存它們? –

+0

是的,從「文檔」表單中刪除「標籤」字段,創建新的「標籤」表單並保存後保存文檔 –

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)) 

我更新的標籤模型中模型。PY

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() 

希望這能解決您的問題,或者給你的東西,從工作。

相關問題