Saving forms with ManyToMany relationships

668 views
Skip to first unread message

Jason S

unread,
Apr 6, 2014, 2:42:36 AM4/6/14
to django...@googlegroups.com
Hi,
I've seen quite a few examples showing various ways to create a form using manytomany relationships and save the data, but i'm still missing a key piece of the puzzle.
I've created a form and it "works", meaning the form creates a new Item object and associates the user.
However i'm not sure how to go about saving the item category and item components selected in the form.

I've tried quite a few things over the last week and i know its quite easy, but just haven't quite gotten there yet and would appreciate some guidance.

FORM:
class NewItemForm(ModelForm):

    class Meta:
        model = Item
        fields = ('name', 'desc', 'img','category','components')

    def save(self, user, component commit = True):
        """Append the component list to the item"""
        new_item = super(NewItemForm, self).save(commit = Fals

        if commit:
                new_item.save()
#                new_item.save_m2m()
                new_item.user.add(user)
                if component != "none":
                    new_item.category.add(new_item)
                    new_item.components.add(new_item)

        return new_item

VIEW:
def create_item(request):

    if request.method == "POST":
        form = NewItemForm(request.POST, instance=item())
        if form.is_valid():
                form.save(request.user, "none")
        return HttpResponseRedirect(reverse('nav.views.dashboard'))
    else:
        form = CategoryForm(instance=item())
        return render_to_response('objects/create_item.html', {'item_Form': form}, context_instance=RequestContext(request))

MODEL:
class Item(models.Model):
    # 
    def __unicode__(self):  # Python 3: def __str__(self):
        return self.name

    name  = models.CharField(max_length=32)
    desc  = models.CharField(max_length=254)
    img   = models.CharField(max_length=32)
    user  = models.ManyToManyField(User) 
    components = models.ManyToManyField(component)
    category  = models.ManyToManyField(Category)

Camilo Torres

unread,
Apr 6, 2014, 10:58:21 PM4/6/14
to django...@googlegroups.com
Hello,

1. This code does not compiles, only to note in case you already didn't tested

2. In your view: why do you instantiate a different form for POST request and any other kind of requests: NewItemForm in one case, CategoryForm in the rest.

3. In your view: You are using the string "none" instead the built-in None. I recommend you to use Python built-in None.

4. ModelForm's save method actually saves the related many to many data. Why are you doing these?
new_item.category.add(new_item)
new_item.components.add(new_item)

You are adding a Item where a component and a Category is expected. This does not make sense. You add(new_item) where you should be add(component()) or something.

Jason S

unread,
Apr 8, 2014, 12:21:30 AM4/8/14
to django...@googlegroups.com
Hi Camilo,
I really appreciate your response.
I had another go at this last night, particularly trying to use the add(component()) snippet.

To respond to your notes:
1. I simplified and used a find-replace to replace the model names which is likely why it dosn't compile, sorry about that. Mine compiles and i'm happy to PM you the code if you'd like.
2. I have very limited programming experience at this point and my project is the cumulation of experimentation, examples and documentation i've read... I believe your saying I don't need to use the "instance"s?
3. Good point, thank you.
4. I've added the lines which try to ensure the m2m relationships are added, such as  
new_item.category.add(new_item)
new_item.components.add(new_item)
during my attempts to get the form to save the many to many data.

What i'm trying to do is create a basic form which allows a user to fill in the name, desc, img, category and component fields
When they save this form, i'd like the "item" to be saved (which is happening) as well as the user, category and component data to be associated to the item.
Currently only the user is being associated to the item when I complete the form and save it.

Once I can do that, i'll move on to using widgets to make the category field a drop down and the component field/s tick boxes and continue adding form validation etc but i've been stuck on this saving issue for some time.

Thanks again for your time and help. 

Camilo Torres

unread,
Apr 8, 2014, 2:40:13 AM4/8/14
to django...@googlegroups.com
Hello,

This works for me, saving the related objects in the many to many relationship. You can start from there to build yours:

models.py:

class Student(models.Model):
    name = models.TextField()


class Course(models.Model):
    name = models.TextField()
    students = models.ManyToManyField(Student, related_name='courses')

forms.py.

class NewItemForm(ModelForm):
    
    class Meta:
        model = Course
        fields = ('name', 'students')

view.py:

def create_item(request):
    if request.method == 'POST':
        form = NewItemForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponse('saved')
        return HttpResponse('Form not valid')
    else:
        form = NewItemForm()
        return render_to_response('testapp/create_item.html', {'form': form}, context_instance=RequestContext(request))


create_item.html:

<form method="POST" action="">{% csrf_token %}
{{ form.as_p }}
<p><input type="submit"></p>
</form>



Regards,
Camilo

Jason S

unread,
Apr 8, 2014, 10:17:46 AM4/8/14
to django...@googlegroups.com
Hi,
Thanks very much, I can now save the item.component and item.category many to many fields.
However I can't save the user as request.user and it still boils down to not quite understanding how to specify a field value to be saved.

I've tried variations of my original code and suggestions online including setting the field using cleaned_data, without success.
If you could please let me know how to do that then I should be fine form there.

Thanks again,
J

Camilo Torres

unread,
Apr 9, 2014, 4:16:47 PM4/9/14
to django...@googlegroups.com
Hello,

update your models.py:

class Course(models.Model):
    name = models.TextField()
    students = models.ManyToManyField(Student, related_name='courses')
    users = models.ManyToManyField(User)


Update your views.py:

def create_item(request):
    if request.method == 'POST':
        form = NewItemForm(request.POST)
        if form.is_valid():
            item = form.save()
            item.users.add(request.user)
            return HttpResponse('saved')
        return HttpResponse('Form not valid')
    else:
        form = NewItemForm()
        return render_to_response('testapp/create_item.html', {'form': form}, context_instance=RequestContext(request))

Note that ModelForm.save() returns the saved model instance, so you can associate the user.

Regards,
Camilo.
Reply all
Reply to author
Forward
0 new messages