Mormoran
Mormoran

Reputation: 791

Django: Adding an instance of object to all ForeignKey related models

I have model Chamber:

class Chamber(models.Model):
    chamber_name    = models.CharField(max_length=100)

And related model ChamberProperty:

class ChamberProperty(models.Model):
    chamber         = models.ForeignKey(Chamber, on_delete=models.CASCADE)
    property_name   = models.CharField(max_length=50)
    property_value  = models.CharField(max_length=100, default=None)

Right now a Chamber can have many ChamberProperty. The requirements were just changed to make it so that if a ChamberProperty is added to a Chamber, it gets added to all future Chamber instances as well, with an empty property_value (default=None), so that the next time the form for creating a new Chamber instance is loaded, all previous ChamberProperty.property_name are loaded in the form, and empty.

Example forms:

First time a Chamber instance is created:

Chamber.chamber_name: Foo
ChamberProperty.property_name: Bar
ChamberProperty.property_value: Baz

Second time a Chamber instance is created:

Chamber.chamber_name: Qux
ChamberProperty.property_name: Bar # Old property, retain the name
ChamberProperty.property_value: None # Old property, value empty
ChamberProperty.property_name: Quux # New property, added by a button
ChamberProperty.property_value: 700 # New property, value filled with input box

Third time a Chamber instance is created:

Chamber.chamber_name: Corge
ChamberProperty.property_name: Bar # Old property, retain the name
ChamberProperty.property_value: None # Old property, value empty
ChamberProperty.property_name: Quux # Old property, retain the name
ChamberProperty.property_value: None # Old property, value empty
ChamberProperty.property_name: Ouier # New property, added by a button
ChamberProperty.property_value: Grault # New property, value filled with input box

So on and so forth.

For clarification: If you create Chamber A, with "ChamberProperty Temperature", then the next time you create a Chamber, Chamber B, it will already have "ChamberProperty Temperature". You can then add a new ChamberProperty to Chamber B, say, "Degrees" with whatever value. Then if you create a new Chamber, Chamber C, it will already have "ChamberProperty Temperature AND Degrees".

Now I realise that I would then have to add ChamberProperty instances recursively to all other Chamber instances.

New properties and values are added in the form with a jQuery script, I have that already, as such:

<div class="card loading-card" id="card-loaded">
    <h3 class="card-header bg-primary text-white">
        Add Chamber
    </h3>
    <div class="card-body">
        <form class="form-horizontal" action="" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {{ properties_formset.management_form }}
        <!-----------Chamber------------>
            <table>
                <tr>
                    <td style="vertical-align:top">
                        <table>
                            <tr>
                                {% for field in chamber_form %}
                                    <td>{{ field|as_crispy_field }}</td>
                                {% endfor %}
                            </tr>
                        </table>
                    </td>
                    <br />
                    <!-----------Chamber Properties------------>
                    <td><p>&nbsp &nbsp &nbsp</p></td>
                    <td>
                        <table class="new-chamber-properties" id="propertiestable">
                        <script type="text/html" id="property-template">
                            <div id="property-__prefix__">
                                <table>
                                    <tr>
                                        {% for field in properties_formset.empty_form %}
                                            <td>{{ field|as_crispy_field }}</td>
                                        {% endfor %}
                                    </tr>
                                </table>
                            </div>
                        </script>

                        <div id="property-form">
                            <tr>
                                {% for properties_form in properties_formset %}
                                    <div id="property-{{ forloop.counter0 }}"></div>
                                {% endfor %}
                            </tr>
                        </div>
                        <tr>
                            <td>
                                <a href="#" id="add-property-button" class="btn btn-outline-primary add-item">Add Properties</a>
                            </td>
                            <td>
                                <a href="#" id="remove-property-button" class="btn btn-outline-danger remove-item">Remove Properties</a>
                            </td>
                            <td><button class="btn btn-primary" type="submit">Submit</button></td>
                        </tr>
                        </table>
                    </td>
                </tr>
            </table>
        </form>
    </div>
</div>

And the jQuery:

<script>
    $(document).ready(function () {
        $('.add-item').click(function (ev) {
            ev.preventDefault();
            var count = $('#property-form').children().length;
            console.log(count)
            var tmplMarkup = $('#property-template').html();
            var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
            $('div#property-form').append(compiledTmpl);

            // update form count
            $('#id_form-TOTAL_FORMS').attr('value', count + 1);

            // some animate to scroll to view our new form
            $('html, body').animate({ scrollTop: $("#add-property-button").position().top - 200 }, 800);
        });

        for (var i = 0; i < 4; i++) {
            var tmplMarkup = $('#property-template').html();
            var compiledTmpl = tmplMarkup.replace(/__prefix__/g, i);
            $('div#property-form').append(compiledTmpl);
            // update form count
            $('#id_form-TOTAL_FORMS').attr('value', i + 1);
        }
        document.getElementById("id_form-0-property_name").value = "Process";
        document.getElementById("id_form-1-property_name").value = "Type";
        document.getElementById("id_form-2-property_name").value = "Model";
        document.getElementById("id_form-3-property_name").value = "Electrode Diameter";
        document.getElementById("card-loaded").style.visibility = "visible";

        $('.remove-item').click(function (ev) {
            ev.preventDefault();
            var count = $('#property-form').children().length;
            console.log(count);
            if (count > 0) {
                var successCount = count - 1;
                console.log(successCount);
                var formlist = $('#property-form')[0];
                while (count > successCount) {
                    var childNode = formlist.lastChild;
                    formlist.removeChild(childNode);
                    count = $('#property-form').children().length;
                }
                // update form count
                $('#id_form-TOTAL_FORMS').attr('value', count);

                // some animate to scroll to view our new form
                $('html, body').animate({ scrollTop: $("#remove-property-button").position().top - 200 }, 800);
            }
        });
    });
</script>

I'm not sure how to even go about doing this.

I was thinking perhaps something like, when the form loads, retrieve all ChamberProperty instances for that customer, but then if I set those values to nothing, it would change the values on all the other Chamber instances as well.

Any help with this?

Upvotes: 1

Views: 101

Answers (1)

ascripter
ascripter

Reputation: 6223

The problem is that you current model doesn't reflect your requirements. What constitutes a property? Obviously it's only the name, and not its value. The value should be kept in a separate model instead that's associated with a name and a chamber:

class ChamberProperty(models.Model):
    name = models.CharField(max_length=50)

class ChamberPropertyValue(models.Model):
    chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)
    property = models.ForeignKey(ChamberProperty, on_delete=models.CASCADE)
    value = models.CharField(max_length=100, default=None)

When a new chamber is created, you will immediately add one ChamberPropertyValue for every ChamberProperty that's present in the database. This way old chambers will keep their state not having any newer names / properties associated with them. This code snippet should go in your form's validation after creating a new chamber:

for prop in ChamberProperty.objects.all():
    ChamberPropertyValue.objects.get_or_create(property=prop, chamber=chamber)

Upvotes: 1

Related Questions