0

I have a form where user can add multiple items which will be shown in table and then if user hits the save button, all those items will be saved to database but I have no idea on how should i do this. Can anyone give me an idea, please?

This is the form I have to process

enter image description here

In screenshot, there is one add button which will add in the below table and then there is save button which will post to the database.

I have only created a model and forms for now

class OpeningStock(models.Model):
    office = models.ForeignKey(OfficeSetup, blank=True, null=True, on_delete=models.CASCADE)
    miti = models.DateTimeField(null=True)
    item_group = models.ForeignKey(ItemGroup, blank=True, null=True)
    item = models.ForeignKey(Item, blank=True, null=True)
    department = models.ForeignKey(DepartmentSetup, blank=True, null=True)
    employee = models.ForeignKey(Employee, blank=True, null=True)
    quantity = models.PositiveIntegerField(default=0)
    value = models.DecimalField(default=0.0, max_digits=100, decimal_places=2)
    specification = models.CharField(blank=True, null=True, max_length=600)
    remarks = models.TextField(blank=True, null=True)

def stock(request):
    form = StockForm(request.POST or None)
    if request.method == "POST" and form.is_valid():
        office_instance = OfficeSetup.objects.get(user=request.user)
        new_form = form.save(commit=False)
        new_form.office = office_instance
        new_form.save()
        messages.success(request, 'Thank you')
        return redirect('stock')
    messages.warning(request, "Correct the errors below")
    stocks = OpeningStock.objects.all()
    context = {
        "form": form,
        "stocks": stocks
    }
    return render(request, 'dashboard/stocks/stock.html', context)

{% block content %}
<div class="right_col" role="main">
    <h1 class="text-center">Stock</h1>
    <div class="space"></div>
    <form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
  </form>
</div>
<div class="stock-list">
  {% include 'dashboard/stocks/stock_adjustment_list.html' %}
</div>
{% endblock content %}

Can anyone show me a simple example/demo to solve such, please?

Serenity
  • 3,884
  • 6
  • 44
  • 87
  • Check out https://www.youtube.com/watch?v=D6esTdOLXh4 – Glenn Nov 30 '17 at 01:49
  • Use JQuery to update your table each time the user clicks add, and also store the new row in a JS object, then when they click save, send the data to the back end using a AJAX request, I can create a more detailed answer if you still need help – kujosHeist Nov 30 '17 at 02:17
  • @Alan That does not answer my question. I have shown what I have done. If its about just posting the data to database, its working but what I want is first create some list by filling the form and hitting the add button and then when hitting save button should save those added items in the tabe to database. – Serenity Nov 30 '17 at 02:17
  • @kujosHeist I did not understand the "new row in a JS object" part – Serenity Nov 30 '17 at 02:31
  • Ok so when the add button is clicked, have a JavaScript function which checks the values in the form, such as Employee/quantity/item etc, and then add these values to the table, as well as store these values in an JavaScript object. Then when you click save, have a JavaScript function send these values to the backend, using a JQuery $.post request – kujosHeist Nov 30 '17 at 02:40
  • The reason you need to use JQuery to post the data to the backend, instead of the standard form, is that the way you have it setup now will only send the most recently selected values to the back end, in order to send more than this, you need to create your own custom $.post request using JQuery, there's an example of this in this question https://stackoverflow.com/questions/7753073/jquery-ajax-post-to-django-view – kujosHeist Nov 30 '17 at 02:43
  • @kujosHeist I implemented submit function in the form and wrapped all those provided values in the form in a object called object variable. Now how should i show those data to the table(stock_adjustment.html). I think I need your guid on this step by step if you have time . I have created a gist https://gist.github.com/MilanRgm/337b581be88ddffc4285fe554ad361e8 – Serenity Dec 01 '17 at 03:08

1 Answers1

1

Okay so in response to your comments, I've created a simplified Django app to demonstrate a solution,

Note: I've removed all but one foreign key and corresponding values from the table, and I've also merged all the templates into one (without any styling).

The source code can be found here: https://github.com/kujosHeist/stack-overflow-django

There's surely better ways to do this, but my solution is that every time the Add button is clicked, the table gets updated with the values, and the values are stored in an object list.
Then when Submit is clicked, it loops through the object list and posts the data to the backend to be stored.

There is a way to send the whole object list together, but django/ajax treats lists awkwardly, so sending them separately is easier for now.

Also, I am manually creating the objects in the backend, rather than using the form, but there should be a way to store the JavaScript objects in a FormData object before posting, so that you can use form.is_valid() and form.save(), but again for simplicity I avoided that.

index.html

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">


<div class="right_col" role="main">
    <h1 class="text-center">Stock</h1>
    <div class="space"></div>
    <form class="stock-form form-group">
        {% csrf_token %}
        {{ form.as_p }}

        <input type="submit" name="Submit">
    </form>

    <button class="btn btn-primary add-stock" id="add-button">Add</button>  
</div>

<div class="stock-list">
  <table class="table table-striped stock-table">
  <thead>
    <tr>
      <th>#</th>
      <th>Item</th>
      <th>Miti</th>
      <th>Quantity</th>
      <th>Value</th>
      <th>Specification</th>
      <th>Remarks</th>
    </tr>
  </thead>
  <tbody id="table-body">
    <!--  here how should i show all those added items  -->
  </tbody>
</table>
</div>

<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>

<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

<script>
    var itemCount = 0;
    var objList = [];

    $(document).ready(function() {

        $("#add-button").on("click", function(){

            // store values in object and add them to list
            var obj = {};
            obj['date'] =  $('#id_miti').val();
            obj['item_id'] =  $('#id_item').val();  
            // (adding itemCount to values so all they all aren't the same)
            obj['quantity'] = $('#id_quantity').val() + itemCount; 
            obj['value'] = $('#id_value').val() + itemCount;
            obj['specification'] = $('#id_specification').val();
            obj['remarks'] = $('#id_remarks').val();
            objList.push(obj);


            // get item name for displaying in table
            var e = document.getElementById("id_item");
            var itemName = e.options[e.selectedIndex].innerText;            


            // add object to table
            $("#table-body").append("<tr>");              
            $("#table-body").append("<td>" + itemName + "</td>");
            $("#table-body").append("<td>" + (itemCount++) + "</td>");
            $("#table-body").append("<td>" + obj['date'] + "</td>");
            $("#table-body").append("<td>" + obj['quantity'] + "</td>");
            $("#table-body").append("<td>" + obj['value'] + "</td>");
            $("#table-body").append("<td>" + obj['specification'] + "</td>");
            $("#table-body").append("<td>" + obj['remarks'] + "</td>");
            $("#table-body").append("</tr>");

            console.log("Added object to table:")
            console.log(obj);   
        });


        $('.stock-form').submit(function(e) {
            e.preventDefault();

            if(objList.length > 0){
                var token = "{{csrf_token}}";

                // loop through object list and post to back end
                // could send them all together, but ajax/django handles list of objects
                // awkwardly

                for(var i = 0; i < objList.length; i++){
                    var data = objList[i];
                    data['csrfmiddlewaretoken'] = token

                    $.ajax({
                        type: 'post',
                        url: '',
                        data: data,
                        success: function(data) {
                            console.log("Success!")
                            console.log('data', data);
                        }
                    });                     
                }
            }
        });
    });
</script>

views.py

from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse

import json
from .models import OpeningStock, Item
from django.http import HttpResponse
from .forms import  OpeningStockForm
from dateutil import parser

def index(request):

    form = OpeningStockForm(request.POST or None)
    if request.method == "POST":

        # manually retrieve fields and create object
        data = request.POST
        date = parser.parse(data['date'])

        # get item from db, make sure you add some sample items to the db
        item = Item.objects.get(pk=data["item_id"])

        s = OpeningStock(item=item, miti=date, quantity=data['quantity'], value=data['value'], specification=data['specification'], remarks=data['remarks'])
        s.save()        

        return HttpResponse(json.dumps({'result': 'success', 'data': request.POST}))
    else:
        context = {}
        context['form'] = form      
        return render(request, 'question1/index.html', context)

models.py

from django.db import models
from django.utils import timezone

class Item(models.Model):
    name = models.CharField(max_length=32)

class OpeningStock(models.Model):
    item = models.ForeignKey(Item, blank=True, null=True)
    miti = models.DateTimeField(null=True, default=timezone.now)
    quantity = models.PositiveIntegerField(default=34)
    value = models.DecimalField(default=2.5, max_digits=100, decimal_places=2)
    specification = models.CharField(blank=True, null=True, max_length=600, default="Sample Spec")
    remarks = models.TextField(blank=True, null=True, default="Sample remarks")

forms.py

from django.forms import ModelForm
from django import forms
from .models import Item

from question1.models import OpeningStock

class OpeningStockForm(ModelForm):

    item = forms.CharField(
        widget=forms.Select(
            choices=Item.objects.all().values_list('id', 'name')
        )
    )

    class Meta:
        model = OpeningStock
        fields = ["item","miti","quantity","value","specification","remarks"]

urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.index, name="index"),
]
kujosHeist
  • 810
  • 1
  • 11
  • 25
  • wow I appreciate your kindness and effort. Can you give me a hint on foreignkey part, please? Right now I am getting an error of Cannot assign "''": "OpeningStock.item_group" must be a "ItemGroup" instance. because item_group will need the instance. Do i have to query in the views by doing something like ItemGroup.objects.get(name=data['item_group'])? – Serenity Dec 01 '17 at 14:36
  • I think i have to use formdata like you have mentioned because the user may provide item value and its not compulsory. Also i need to check the errors etc . What you say? – Serenity Dec 01 '17 at 14:42
  • I've updated my answer to include the "Item" model as a foreign key in the OpeningStock model, which meant updating each of the files to make use of it, this should give you a good idea of how to include the rest of the foreign keys which you need for your model. I've also updated the repo on github. FYI make sure to create some sample "Items" in your database when testing – kujosHeist Dec 01 '17 at 17:18
  • 1
    Did that answer your question? – kujosHeist Dec 02 '17 at 22:40
  • 1
    sure it answered my question. Sorry I was working with FormData(the idea you gave me). Thanks a lot for your effort and help. I appreciate it. – Serenity Dec 03 '17 at 01:23