I have a set of document objects and label objects, and I want those two objects to be linked. It’s a typical many-to-many relationship. I have the following code:
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))
When I upload a document, it gets added to the database, however no labels are being created or associated with the document. Do I need to explicitly add something to the Document’s save() function to make this happen? Or somewhere in the Views.py file? I’d imagine it’d go something like:
- Check to see if the label that’s being added already exists
- If it doesn’t, then create a new label
- Grab both the current document_id and the new/existing label_id
- Add a record to the document_labels table (automatically created for the many-to-many relationship)
I feel like that’s pretty standard functionality that I assumed would be built in to a many-to-many relationship in django, but it doesn’t seem to be working for me so far. I’m trying to avoid reinventing the wheel here.
As other people said, you cannot save in one-shot Document object and its ManyToMany field, because django create "intermediatary join table", which need the Document object’s ID, which is not defined at that point.
There is a save_m2m function in ModelForm, that is supposed to be called by the Form itself, as described in the doc
However, if it doesn’t work, maybe a trick is to call save_m2m in the view function, like this: