Michael.Hughes1
Michael.Hughes1

Reputation: 62

Django model extra fields added by the user

The model will ask for personnels names and qualifications which are both text fields but the amount of personnel will vary from instance to instance. How can I let the user add a field to the model?

Here's my model currently:

class ShiftReport(models.Model):
    work = models.ForeignKey(Work, null=True, on_delete=models.CASCADE)
    work_description = models.TextField(max_length=500)
    handover = models.BooleanField(default=False)
    weather_conditions = models.TextField(max_length=100)
    start_time = models.DateTimeField()
    end_time = models.DateTimeField()
    noms_number = models.TextField(max_length=40)
    noms_phase_addition_number = models.TextField(max_length=5)

    
    personnel_name = models.TextField(max_length=100)
    personnel_qualifications = models.TextField(max_length=100)

    personnel_name_2 = models.TextField(max_length=100)
    personnel_qualifications_2 = models.TextField(max_length=100)

I would like to be able to let the user add in as many personnel names and qualifications as they want to.

class ShiftReport(models.Model):
    work = models.ForeignKey(Work, null=True, on_delete=models.CASCADE)
    work_description = models.TextField(max_length=500)
    handover = models.BooleanField(default=False)
    weather_conditions = models.TextField(max_length=100)
    start_time = models.DateTimeField()
    end_time = models.DateTimeField()
    noms_number = models.TextField(max_length=40)
    noms_phase_addition_number = models.TextField(max_length=5)

    
    personnel_name = models.TextField(max_length=100)
    personnel_qualifications = models.TextField(max_length=100)
    
    personnel_count = 1

    def add_personnel(self):
        self.personnel_count += 1
        ADDFIELD(personnel_name_{self.personnel_count})
        ADDFIELD(personnel_qualifications_{self.personnel_count}

Just not too sure on how to implement this. Help!

Upvotes: 0

Views: 274

Answers (2)

Jaydeep
Jaydeep

Reputation: 803

one way would be, by making personnel names and qualifications fields as ForeignKey instance of ShiftReport and do the dynamic addition using javascript.

as you had not shared any other data. so, to give you an idea, suppose the models are like following:

class ShiftReport(models.Model):
    work_description = models.TextField(max_length=500)

class PersonalInfo(models.Model):
    shift_report = models.ForeignKey(ShiftReport, on_delete=models.CASCADE)
    personnel_name = models.TextField(max_length=100)
    personnel_qualifications = models.TextField(max_length=100)

then views would be:

def shift_report_view(request):
    if request.method == "POST":
        work_description = request.POST.get("work_description")
        personnel_name_list = request.POST.getlist("personnel_name")
        personnel_qualifications_list = request.POST.getlist("personnel_qualifications")

        personnel_info = zip(personnel_name_list, personnel_qualifications_list)

        shift_report_data = ShiftReport(work_description=work_description)
        shift_report_data.save()

        for i in personnel_info:
            if not len(i[0]) == len(i[1]) == 0:
                personal_info_data = PersonalInfo(shift_report=shift_report_data, personnel_name=i[0], personnel_qualifications=i[1])
                personal_info_data.save()
        
    return render(request, "shift_report.html")

and in template:

<form method="POST" action="">
    {% csrf_token %}
    <input type="text" name="work_description" placeholder="Work Description"><br><br>

    <div id="personalInfo">
        <input type="text" name="personnel_name" placeholder="Personnel Name"><br><br>
        <input type="text" name="personnel_qualifications" placeholder="Personnel Qualifications"><br><br>
    </div>

    <input type="button" id="addPersonalInfo" value="Add Personal Info">
    <input type="submit" value="submit">
</form>


<script type="text/javascript">
    document.getElementById("addPersonalInfo").onclick = function() {
        
        var personnelName = document.createElement("input");
        personnelName.type = "text";
        personnelName.name = "personnel_name";
        personnelName.placeholder = "Personnel Name";

        var personnelQualifications = document.createElement("input");
        personnelQualifications.type = "text";
        personnelQualifications.name = "personnel_qualifications";
        personnelQualifications.placeholder = "Personnel Qualifications";

        var personnelDiv = document.getElementById("personalInfo");
        personnelDiv.appendChild(personnelName);
        personnelDiv.appendChild(document.createElement("br"));
        personnelDiv.appendChild(document.createElement("br"));
        personnelDiv.appendChild(personnelQualifications);
        personnelDiv.appendChild(document.createElement("br"));
        personnelDiv.appendChild(document.createElement("br"));

    }
</script>

Upvotes: 1

CodexLink
CodexLink

Reputation: 188

It's impossible to make the user add a field to the models dynamically. But what you can do is by, making a container model and the content-you-want-to-store model approach.

For instance, (excerpt from my code). I want to store an nth number of documents that the user could send to us. Instead of statically defining fields 10 times. I would rather create a container of documents that could contain a set of documents.

Container Model = SupportDocsContainer
ContentToStoreToContainer Model = SupportDocs

# ! ContentToStoreToContainer Model
class SupportDocs(models.Model):
    document = models.FileField(
        upload_to=<...>,
        null=False,
        blank=False,
        max_length=2048,
        verbose_name="Case-Supporting Documents / Report Proofs",
        help_text="Images or documents that can be submitted inside Review Support Case.",
        validators=[
            FileExtensionValidator(
                allowed_extensions=(["pdf", "docx", "doc", "jpg", "jpeg", "png", "gif"])
            )
        ],
    )

# ! Container Model
class SupportDocsContainer(models.Model):
    documents = models.ManyToManyField(
        "SupportDocs",
        editable=True,
        verbose_name="List of Documents Provided by the Message Sender / Request Initiator",
        help_text="A many-to-many field that involves documents.",
    )
    dtLastModified = models.DateTimeField(
        auto_now=True,
        editable=False,
        verbose_name="Date and Time Document Container was Modified",
        help_text="The date and time from where the document container has been modified.",
    )
    dtCreated = models.DateTimeField(
        auto_now_add=True,
        editable=False,
        verbose_name="Date and Time Document Container was Created.",
        help_text="The date and time from where the document container has been created.",
    )

With this approach, you could make ShiftReport Object create an object (automatically via Signals, Clean, or Save Method. (But on your own implementation...)) of the container from where the user could save a particular data.

Upvotes: 1

Related Questions