4

I am new to Django and using this project to learn it. I am able to save the Journal record but the many to many relationship does not work.

This 'create' view displays the correct form including the multi-select box with all of the cryptos listed (from Crypto model). When submitting the form the many-to-many records do not save but the Journal saves fine.

I have found a bunch of different answers to this, some are for python 2.7, but this is the simplest method based on the [Django documentation][1]. Any help is greatly appreciated.

Also, the relationship works fine in the Admin section, so I am thinking it has something to do with the Forms and/or the View & saving.

models.py

from django.db import models
from crypto.models import Crypto as CryptoModel

class Journal(models.Model):
    title = models.CharField(max_length=200, help_text='Journal Title', blank=False, null=False)
    content = models.TextField(max_length=2000, help_text='Journal Content (HTML OK)', blank=False, null=False)
    crypto_id = models.ManyToManyField(CryptoModel, blank=True)    
    created = models.DateTimeField(help_text='Created', auto_now_add=True, null=True)

    def __str__(self):
        return self.title  ## String for representing the Model object, usually name field or title

forms.py

from django.forms import ModelForm, ModelMultipleChoiceField, widgets
from journal.models import Journal as JournalModel
from crypto.models import Crypto as CryptoModel

class JournalForm(ModelForm):
    # select multiple items box
    cryptos = ModelMultipleChoiceField(widget=widgets.SelectMultiple(attrs={'size': 30}), queryset=CryptoModel.objects.all())

    class Meta:
        model = JournalModel
        fields = [
            "title",
            "content",
        ]
        labels = {
            'title': 'Journal Title',
        }
        required = [
            "title", # same as model
            "content",  # same as model
        ]

views.py

from journal.forms import JournalForm
from django.utils import timezone
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from journal.models import Journal as JournalModel

    def Create(request):

        if request.method == "POST":
            form = JournalForm(request.POST) # form instance
            context = {'form': form} # if errors, keep the form data on next page load

            journal = form.save(commit=False)  # False needed for many-to-many
            journal.title = form.cleaned_data["title"]
            journal.content = form.cleaned_data["content"]
            journal.created = timezone.now()
            journal.save()  # save the form journal data, now we have a PK
            form.save_m2m()  # save the 'form' using ManytoMany method

            return HttpResponseRedirect('/journal/')

        form = JournalForm()
        context = {'form': form}

        return render(request, 'journal/create.html', context)

models.py 2

from django.db import models
from crypto.models import Crypto

class Journal(models.Model):
    title = models.CharField(max_length=200, help_text='Journal Title', blank=False, null=False)
    content = models.TextField(max_length=2000, help_text='Journal Content (HTML OK)', blank=False, null=False)
    crypto_id = models.ManyToManyField(Crypto, blank=True)    
    created = models.DateTimeField(help_text='Created', auto_now_add=True, null=True)

    def __str__(self):
        return self.title  ## String for representing the Model object, usually name field or title

forms.py 2

from django.forms import ModelForm, ModelMultipleChoiceField, widgets
from journal.models import Journal
from crypto.models import Crypto

class JournalForm(ModelForm):
    # select multiple items box
cryptos = ModelMultipleChoiceField(widget=widgets.SelectMultiple(attrs={'size': 30}), queryset=Crypto.objects.all())

class Meta:
    model = JournalModel
    fields = [
        "title",
        "content",
        "cryptos",
    ]

views.py 2

from journal.forms import JournalForm
from django.utils import timezone
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
from journal.models import Journal

def Create(request):

    if request.method == "POST":
        form = JournalForm(request.POST) # form instance
        context = {'form': form} # if errors, keep the form data on next page load

        journal = form.save(commit=False)  # False needed for many-to-many
        journal.created = timezone.now()
        journal.save()  # save the form journal data, now we have a PK

        journal.crypto_id.set(form.cleaned_data.get("cryptos")) # must be after "save"
        form.save_m2m()  # save the 'form' using ManytoMany method

        return HttpResponseRedirect('/journal/')

    form = JournalForm()
    context = {'form': form}

    return render(request, 'journal/create.html', context)
DBTales
  • 91
  • 8

4 Answers4

1

hope this solve your problem just but this line after save your journal instance journal.crypto_id.set(form.cleaned_data.get("cryptos"))

0

You've called your model and form fields different things; Django can't know they relate to the same field. The form name - crypos - is the correct one, you should rename your model field to that.

Also, you haven't specified the field in the fields list, so Django won't even try to set it on the model.

Note that in your view you don't need to set title or content, that's what form.save does for you already.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Thank you for the response. I made the changes you suggested and posted the new code as '****.py 2'. Unfortunately this did not change the result, the Many to Many does not save but it works fine in the Admin section. Any other ideas? – DBTales Oct 27 '18 at 19:40
0

Thank you abdullah, adding "journal.crypto_id.set(form.cleaned_data.get("cryptos"))" to the VIEW fixed the issue. An additional note is that this must be places after the 'journal' form is saved but before the many to many is saved.

I updated the "models.py 2", "forms.py 2" and "views.py 2" section above. This is the working code.

DBTales
  • 91
  • 8
0

you are always welcome. yes but it after journal.save(). and set don't need to call save() from form.