Edit: Although this post is a duplicate of the Django ModelForm unique_together authentication , the accepted answer here about removing "exclude" from ModelForm is a much cleaner solution than the accepted answer in another question.
This is a continuation of this issue .
If I did not explicitly check the unique_together constraint in the clean_title () function, django throws an exception:
IntegrityError at / journal / journal / 4
duplicate key value violates unique journal_journal_owner_id_key constraint
Request Method: POST
Request URL: http: // localhost: 8000 / journal / journal / 4
Exception Type: IntegrityError
Exception value: a duplicate key value violates the unique journal_journal_owner_id_key constraint
Exception location: /Library/Python/2.6/site-packages/django/db/backends/util.py in execution, line 19
However, I got the impression that Django would effectively apply this restriction by raising a ValidationError, not the exception that I need to catch.
Below is my code with the optional clean_title () method, which I use to work. But I want to know what I'm doing wrong, so django does not apply the restriction in expected mode.
Thanks.
Model Code:
class Journal (models.Model): owner = models.ForeignKey(User, related_name='journals') title = models.CharField(null=False, max_length=256) published = models.BooleanField(default=False) class Meta: unique_together = ("owner", "title") def __unicode__(self): return self.title
Form Code:
class JournalForm (ModelForm): class Meta: model = models.Journal exclude = ('owner',) html_input = forms.CharField(label=u'Journal Content:', widget=TinyMCE(attrs={'cols':'85', 'rows':'40'}, ), ) def clean_title(self): title = self.cleaned_data['title'] if self.instance.id: if models.Journal.objects.filter(owner=self.instance.owner, title=title).exclude(id=self.instance.id).count() > 0: raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.') else: if models.Journal.objects.filter(owner=self.instance.owner, title=title).count() > 0: raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.') return title
View code:
def journal (request, id=''): if not request.user.is_active: return _handle_login(request) owner = request.user try: if request.method == 'GET': if '' == id: form = forms.JournalForm(instance=owner) return shortcuts.render_to_response('journal/Journal.html', { 'form':form, }) journal = models.Journal.objects.get(id=id) if request.user.id != journal.owner.id: return http.HttpResponseForbidden('<h1>Access denied</h1>') data = { 'title' : journal.title, 'html_input' : _journal_fields_to_HTML(journal.id), 'published' : journal.published } form = forms.JournalForm(data, instance=journal) return shortcuts.render_to_response('journal/Journal.html', { 'form':form, }) elif request.method == 'POST': if LOGIN_FORM_KEY in request.POST: return _handle_login(request) else: if '' == id: journal = models.Journal() journal.owner = owner else: journal = models.Journal.objects.get(id=id) form = forms.JournalForm(data=request.POST, instance=journal) if form.is_valid(): journal.owner = owner journal.title = form.cleaned_data['title'] journal.published = form.cleaned_data['published'] journal.save() if _HTML_to_journal_fields(journal, form.cleaned_data['html_input']): html_memo = "Save successful." else: html_memo = "Unable to save Journal." return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'saved':html_memo}) else: return shortcuts.render_to_response('journal/Journal.html', { 'form':form }) return http.HttpResponseNotAllowed(['GET', 'POST']) except models.Journal.DoesNotExist: return http.HttpResponseNotFound('<h1>Requested journal not found</h1>')
UPDATED WORK CODE: Thanks to Daniel Roseman.
The model code remains the same as above.
The code of the form is to remove the exclude statement and the clean_title function:
class JournalForm (ModelForm): class Meta: model = models.Journal html_input = forms.CharField(label=u'Journal Content:', widget=TinyMCE(attrs={'cols':'85', 'rows':'40'},),)
View code - add a unique uniqueness error message:
def journal (request, id=''): if not request.user.is_active: return _handle_login(request) try: if '' != id: journal = models.Journal.objects.get(id=id) if request.user.id != journal.owner.id: return http.HttpResponseForbidden('<h1>Access denied</h1>') if request.method == 'GET': if '' == id: form = forms.JournalForm() else: form = forms.JournalForm(initial={'html_input':_journal_fields_to_HTML(journal.id)},instance=journal) return shortcuts.render_to_response('journal/Journal.html', { 'form':form, }) elif request.method == 'POST': if LOGIN_FORM_KEY in request.POST: return _handle_login(request) data = request.POST.copy() data['owner'] = request.user.id if '' == id: form = forms.JournalForm(data) else: form = forms.JournalForm(data, instance=journal) if form.is_valid(): journal = form.save() if _HTML_to_journal_fields(journal, form.cleaned_data['html_input']): html_memo = "Save successful." else: html_memo = "Unable to save Journal." return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'saved':html_memo}) else: if form.unique_error_message: err_message = u'You already have a Lab Journal with that title. Please change your title so it is unique.' else: err_message = form.errors return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'error_message':err_message}) return http.HttpResponseNotAllowed(['GET', 'POST']) except models.Journal.DoesNotExist: return http.HttpResponseNotFound('<h1>Requested journal not found</h1>')