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:
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:
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))
I would suggest referring to how the Django Admin app works in situations like this. Typically, this would be a two stage operation; First you'd create several Labels, then you'd create a Document, pick the labels you want associated from a multi-select list, then save it. Django would then automatically associate the labels selected in the list via the many-to-many table between Documents and Labels.
If you're hoping to do this all in one step, there is the possibility of using inline formsets. The admin app uses these mainly for foreign keys (Poll and Questions, for example), but they can be used to a limited degree with many-to-many relationships as well.
Be warned, inline formsets can be tricky. If you can split the operation up into two separate views, it would be much easier. Simply create one view for creating Labels and another for creating Documents, which will automatically have a list for picking which labels to associate with the document.
I think this is easy to resolve if you understande the forms and foms-factory of django
Maybe this doc can help you:
https://docs.djangoproject.com/en/1.3/topics/forms/modelforms/#inline-formsets
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With