Django Model With ManyToMany Not Saving

Viewed 2514

I have a list of menus--with items listed on the menu.

models.py:

class Menu(models.Model):
    season = models.CharField(max_length=20)
    items = models.ManyToManyField('Item', related_name='items')
    created_date = models.DateTimeField(default=timezone.now)
    expiration_date = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        return self.season

class Item(models.Model):

    name = models.CharField(max_length=200)
    description = models.TextField()
    chef = models.ForeignKey('auth.User')
    created_date = models.DateTimeField(
            default=timezone.now)
    standard = models.BooleanField(default=False)
    ingredients = models.ManyToManyField(
        'Ingredient', related_name='ingredients'
    )

    def __str__(self):
        return self.name

class Ingredient(models.Model):
    name = models.CharField(max_length=200)

    def __str__(self):
        return self.name

For some reason--in the admin--i can edit the menu and it will save the items properly--but if i do so in the edit view for the menu it does not save the items (it does however save the season and expiration_date). That's why i thought maybe it was the ManyToMany relationship? Any ideas?

views.py:

def edit_menu(request, pk):
    menu = get_object_or_404(Menu, pk=pk)
    items = Item.objects.all()
    admin = User.objects.get(username="admin")
    if request.method == "POST":
        ingredient = Ingredient(name="bug")
        ingredient.save()
        items = []

        for i in request.POST.getlist('items'):
            item = Item(name=i, chef=admin, description = "your description here", standard=False)
            item.save()
            items.append(item)
            item.ingredients.add(ingredient)

        if not Menu.objects.filter(pk=pk).exists():
            menu=Menu(season=request.POST.get('season', ''),
                      expiration_date=datetime.strptime(request.POST.get('expiration_date', ''), '%m/%d/%Y'))

        else:
            menu=Menu.objects.get(pk=pk)
            menu.items.delete()

        menu.save()

        for item in items:
            menu.items.add(item)
            return redirect('menu_detail', pk=menu.pk)

    return render(request, 'menu/change_menu.html', {
    'menu': menu,
    'items': items,
})

and menu detail page,

template

{% extends "layout.html" %}
{% block content %}
        <div class="content container">
            <div class="row">
                <div class="col-md-8">
                    <div class="post">
                        <h1>
                            {% if user.is_authenticated %}
                                <a class="btn btn-default" href="{% url 'menu_edit' pk=menu.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
                            {% endif %}
                            {{ menu.season }}
                        </h1>
                        <h2>On the menu this season:</h2>
                        <ul>
                            {% for item in menu.items.all %}
                                <li><a href="{% url 'item_detail' pk=item.pk %}">{{ item }}</a></li>
                            {% endfor %}
                        </ul>
                        {% if menu.expiration_date %}
                            <div class="date">
                                Menu expires on {{ menu.expiration_date|date:"F j, Y" }}
                            </div>
                        {% endif %}
                    </div>
                </div>
            </div>
        </div>
{% endblock %}

UPDATE --changed edit view to this... now getting many to many error

def edit_menu(request, pk):
    menu = get_object_or_404(Menu, pk=pk)
    items = Item.objects.all()
    admin = User.objects.get(username="admin")
    if request.method == "POST":
        ingredient = Ingredient(name="bug")
        ingredient.save()
        item = Item(name=request.POST.get('items', ''), chef=admin, description = "your description here", standard=False)
        item.save()
        item.ingredients.add(ingredient)
        menu = Menu(season=request.POST.get('season', ''),
                    expiration_date=datetime.strptime(request.POST.get('expiration_date', ''), '%m/%d/%Y'))
        menu.save()
        menu.items.add(item)
        return redirect('menu_detail', pk=menu.pk)

    return render(request, 'menu/change_menu.html', {
    'menu': menu,
    'items': items,
})

UPDATE --adding change_menu template below

{% extends "layout.html" %}
{% block content %}
        <div class="content container">
            <div class="row">
                <div class="col-md-8">
                    <h1>Change menu</h1>
                    <form action="" method="POST">{% csrf_token %}
                        <label for="season">Season:</label>
                        <input type="text" name="season" value="{{ menu.season }}">
                        <br />
                        <label for="items">Items:</label>
                        <select multiple>
                        {% for item in items %}
                            <option value="{{ item }}">{{ item}}</option>
                        {% endfor %}
                        </select>
                        <br />
                        <label for="expiration_date">Expiration Date:</label>
                         <input type="text" name="expiration_date" value="{{ menu.expiration_date|date:"m/d/Y" }}">
                        <br />
                        <input type="submit" value="Submit" /> 
                    </form>
                </div>
            </div>
        </div>
{% endblock %}

adding new_menu template below

{% extends "layout.html" %}
{% block content %}

   <div class="content container">
            <div class="row">
                <div class="col-md-8">
                    <h1>Create New Menu</h1>
                    <form method="POST" action="">
                        {% csrf_token %}
                        {{ form.as_p }}
                        <input type="submit" class="button" value="Save">
                    </form>
                </div>
                </div>
       </div>


{% endblock %}
2 Answers
Related