Django autopopulate entire form with model data based of single choice field - django

I am trying to make a modelform that populates all fields based on a single select field. Basically when the user selects a value from a dropdown it would then populate the rest of the fields based on data from a database.
models.py:
class Commands(models.Model):
command_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
command_prefix = models.TextField()
command = models.TextField()
args = models.TextField()
shell = models.TextField()
role = models.ForeignKey('Roles', models.DO_NOTHING)
os = models.ForeignKey('Operatingsystems', models.DO_NOTHING)
job_type = models.ForeignKey('Jobtypes', models.DO_NOTHING)
active = models.IntegerField()
views.py:
#verified_email_required
def jobs(request):
return render(request, 'backend/jobs.html', {'form': CommandsForm()})
forms.py:
class CommandsForm(Form):
name = ModelChoiceField(queryset=Commands.objects.filter(active=1).values('name'))
os = CharField(required=True, disabled=True)
command_prefix = CharField(required=True, disabled=True)
target = CharField(required=True)
command = CharField(required=True, disabled=True)
args = CharField(required=True, disabled=True)
shell = CharField(required=True, disabled=True)
urls.py
urlpatterns = [
url(r'^$', profile, name='profile'),
url(r'^jobs/$', jobs, name='jobs'),
url(r'^patchreport/$', patchreport, name='patchreport'),
url(r'^prtbl/$', PatchReportTable.as_view(), name='patchreptbl')
]
jobs.html
{% extends "backend/base.html" %}
{% load staticfiles %}
{% load widget_tweaks %}
{% block title %}
{{block.super}}Jobs
{% endblock %}
{% block content %}
<form id="jobs_form" class="form-horizontal text-center" method="post" action="{% url 'jobs' %}">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="form-group">
{{ form.name.errors }}
<label for="{{ form.name.id_for_label }}" class="col-sm-2 control-label">Name</label>
<div class="col-sm-8">
{{ form.name|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<label for="{{ form.os.id_for_label }}" class="col-sm-2 control-label">Os</label>
<div class="col-sm-8">
{{ form.os|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<label for="{{ form.command_prefix.id_for_label }}" class="col-sm-2 control-label">Command prefix</label>
<div class="col-sm-8">
{{ form.command_prefix|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<label for="{{ form.target.id_for_label }}" class="col-sm-2 control-label">Target</label>
<div class="col-sm-8">
{{ form.target|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<label for="{{ form.command.id_for_label }}" class="col-sm-2 control-label">Command</label>
<div class="col-sm-8">
{{ form.command|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<label for="{{ form.args.id_for_label }}" class="col-sm-2 control-label">Args</label>
<div class="col-sm-8">
{{ form.args|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<label for="{{ form.shell.id_for_label }}" class="col-sm-2 control-label">Shell</label>
<div class="col-sm-8">
{{ form.shell|add_class:"form-control" }}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-8">
<button type="submit" class="btn btn-default" name="submit">Submit</button>
</div>
</div>
</form>
<script type="text/javascript" src="{% static "js/jobs.js" %}"></script>
{% endblock %}
To be honest, I am unsure of how to accomplish this with Django's ModelForms. Currently I took Michael Platt's advice and am auto-populating the fields with javascript on change event in a javascript file called jobs.js. I have to believe there is a way to accomplish the same thing by populating the entire form from the database directly, or via something like a RESTful api generated by TastyPie with an ajax call.

If you aren't too turned off by the concept of using javascript, you could use a .change() event for your particular select field. So it would look something like this:
$(document).ready(function(){
$("#id_name").change(function() {
// Find your different fields you want to populate and set the values here.
// Example would be
if ($("#id_name").val() == "Some value") {
$("#id_command_prefix").val("Whatever you want to populate with.")
}
});
});

Related

How to render error or validation messages to ModelForm in 2022

I've spent several hours researching on the internet, especially the official Django documentation, but still it is not clear to me which is the best option in 2022 (since almost all questions I read on SO are > 6 yo)
and there are diverse opinions on whether crispy forms is better or not.
Is still crispy forms a recommended option?
How can I (and what is the most recommended way to) get the typical validation error messages?
Like: "this field is mandatory" or "this input accepts numbers only"? I've seen some Django pages using those default messages but I don't know how to show them in my ModelForm fields.
Lets say I have the following model:
class Project(models.Model):
project_name = models.CharField(max_length=250, null=False, blank=False)
status = models.CharField(
max_length=250,
null=True,
blank=True,
default=PROJECT_STATUS_DEFAULT,
choices=PROJECT_STATUS,
)
creation_date = models.DateField(max_length=250, null=False, blank=False)
project_code = models.IntegerField(null=True, blank=True)
notes = models.CharField(max_length=250, null=True, blank=True)
And for the Project model I have the following ModelForm:
class CreateNewProjectForm(ModelForm):
creation_date = forms.DateField(widget=forms.DateInput(format = '%d/%m/%Y'), input_formats=settings.DATE_INPUT_FORMATS) #UK Date format
class Meta:
model = Project
fields = '__all__'
The view, when I try to create a new object Project:
def add_new_project(request):
context = {}
if request.method == 'POST':
form = CreateNewProjectForm(request.POST)
if form.is_valid():
form.save()
return redirect('project_page')
else:
print (form.errors)
form = CreateNewProjectForm()
context['form'] = form
return render(request, 'new_project.html', context)
HTML part:
<div class="card h-100">
<div class="card-header project-page-header">
<h3>Create A New Project</h3>
</div>
<div class="card-body px-0 new-project-card-body">
<div class="cardItem">
<div class="row">
<div class="col">
<div class="row">
<div class="tab-pane fade show active" id="general">
<form id="newProjectForm" method="POST" action="new_project">
{% csrf_token %}
<div class="accordion accordion-flush" id="accordionGeneral">
<div class="accordion-item">
<h2 class="accordion-header" id="general-headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#general-collapseOne" aria-expanded="false" aria-controls="general-collapseOne">
Details
</button>
</h2>
<div id="general-collapseOne" class="accordion-collapse collapse show" aria-labelledby="general-headingOne" data-bs-parent="#accordionGeneral">
<div class="accordion-body">
<div class="row">
<div class="col-5">
<ul class="list-unstyled">
<li class="mt-3">
<div class="row">
<div class="col-sm ph-tabs-field-label">
Project Name
</div>
<div class="col-sm">
<input type="text" name="project_name" class="form-control" aria-label="Project Name">
</div>
</div>
</li>
<li class="mt-3">
<div class="row">
<div class="col-sm ph-tabs-field-label">
Status
</div>
<div class="col-sm">
<select name="status" class="selectpicker show-tick w-100" aria-label="Status">
{% for status in project_status %}
{% if forloop.first %}
<option value="{{ status.id }}" selected>{{ status.text }}</option>
{% else %}
<option value="{{ status.id }}">{{ status.text }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
</li>
<li class="mt-3">
<div class="row">
<div class="col-sm ph-tabs-field-label">
Creation Date
</div>
<div class="col-sm">
<input type="text" name="creation_date" class="form-control">
</div>
</div>
</li>
<li class="mt-3">
<div class="row">
<div class="col-sm ph-tabs-field-label">
Project Code
</div>
<div class="col-sm">
<input type="text" name="project_code" class="form-control">
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="general-headingThree">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#general-collapseThree" aria-expanded="false" aria-controls="general-collapseThree">
Note
</button>
</h2>
<div id="general-collapseThree" class="accordion-collapse collapse" aria-labelledby="general-headingThree" data-bs-parent="#accordionGeneral">
<div class="accordion-body"><textarea name="notes" class="form-control" rows="7"></textarea></div>
</div>
</div>
<button type="submit" id="projectEditBtn" form="newProjectForm" class="btn btn-info rounded-0">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
I saw solutions like this, but the problem is that my form fields are spread over different accordions, I can't use something like {% if form.errors %}, I need something more specific for each field.
First update your views like this
def add_new_project(request):
context = {}
if request.method == 'POST':
form = CreateNewProjectForm(request.POST)
if form.is_valid():
form.save()
return redirect('project_page')
else:
print (form.errors)
context['form'] = form
return render(request, 'new_project.html', context)
context['form'] = CreateNewProjectForm()
return render(request, 'new_project.html', context)
You can specify error for each field like this
{% if form.field_name.errors %}
{{ form.field_name.errors }}
{% endif %}

My for loop does not work as expected - Data does not show up in my django template

I am trying to use a for loop in my Django template to show the data stored in the models of a table but for some reason , the data does not show up in the template.
Views.py
def add_part(request):
parts = Parts.objects.all()
context = {
"parts": parts
}
return render(request, 'admintemplate/add_parts_template.html', context)
def add_part_save(request):
if request.method != "POST":
messages.error(request, "Method Not Allowed!")
return redirect('add_part')
else:
part_name = request.POST.get('part_name')
part_type = request.POST.get('part_type')
supplier_id = request.POST.get('suppliers')
suppliers = Suppliers.objects.get(id=supplier_id)
try:
part = Parts(part_name=part_name, part_type=part_type, supplier_id=supplier)
part.save()
messages.success(request, "Part Added Successfully!")
return redirect('add_part')
except:
messages.error(request, "Failed to Add Part!")
return redirect('add_part')
models.py
The parts and the services model are exactly the same with different column names, so I think the functionality for both should be the same.
Suppliers models
class Suppliers(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20)
Parts model
class Parts(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20)
part_type = models.CharField(max_length=20)
supplier_id = models.ForeignKey(Suppliers, on_delete=models.CASCADE)
Services model
class Services(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20)
service_type = models.CharField(max_length=20)
supplier_id = models.ForeignKey(Suppliers, on_delete=models.CASCADE)
Part template
{% extends 'admintemplate/base_template.html' %}
{% block page_title %}
Add Parts
{% endblock page_title %}
{% block main_content %}
{% load static %}
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<!-- general form elements -->
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Add Parts</h3>
</div>
<!-- /.card-header -->
<!-- form start -->
<form role="form" method="POST" action="{% url 'add_part_save' %}">
{% csrf_token %}
{% comment %} Display Messages {% endcomment %}
{% if messages %}
<div class="form-group">
<div class="col-12">
{% for message in messages %}
{% if message.tags == "error" %}
<div class="alert alert-danger alert-dismissible fade show" role="alert" style="margin-top: 10px;">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% elif message.tags == "success" %}
<div class="alert alert-success alert-dismissible fade show" role="alert" style="margin-top: 10px;">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
<div class="card-body">
<div class="form-group">
<label>Part Name </label>
<input type="text" class="form-control" name="part_name" placeholder="Part Name">
</div>
<div class="form-group">
<label>Part Type </label>
<input type="text" class="form-control" name="part_type" placeholder="Part Type">
</div>
<div class="form-group">
<label>Supplier Name</label>
<select class="form-control" name="suppliers">
{% for supplier in suppliers %}
<option value="{{ supplier.id }}">{{ supplier.name }}</option>
{% endfor %}
</select>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary">Add Part</button>
</div>
</form>
</div>
<!-- /.card -->
</div>
</div>
</div><!-- /.container-fluid -->
</section>
{% endblock main_content %}
Now the services in parts template does not show up at all. There is no choices on the form. But, for the add services template, it does populate. I have no idea why this happens because I have used the exact same code for both templates.
Changing the view to this solved the issue
def add_part(request):
parts = Parts.objects.all()
context = {
"suppliers": suppliers
}
return render(request, 'admintemplate/add_parts_template.html', context)

How to combine any fields in one in Django filters

I wonder how you combine search from multiple fields into one. The fields would be textoQuestao, perguntaQuestao, aOpcao, bOpcao, cOpcao, eOpcao, eOpcao.
I would like all of these fields to be combined into one called texto and to search all selected fields.
filters.py
class FiltroQuestoes(django_filters.FilterSet):
texto =
class Meta:
model = Questao
fields = ['textoQuestao','perguntaQuestao','aOpcao','bOpcao','cOpcao','dOpcao','eOpcao','idProva','idQuestao','idCategoria']
views.py
def FiltroDeQuestoesView(request):
qs = filter(request)
context = {
'queryset': qs,
'categorias': Categoria.objects.all(),
'provas': Prova.objects.all()
}
return render(request, "polls/pesquisa.html", context)
def filter(request):
qs = Questao.objects.all()
categorias = Categoria.objects.all()
prova = request.GET.get('prova')
provas = Prova.objects.all()
questao = request.GET.get('questao')
categoria = request.GET.get('categoria')
return qs
search.html
{% block content %}
<form method="get">
<div class="well">
<h4 style="margin-top: 0">Filter</h4>
<div class="row">
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.texto.label_tag }}
{% render_field filter.form.texto class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.idProva.label_tag }}
{% render_field filter.form.idProva class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.idQuestao.label_tag }}
{% render_field filter.form.idQuestao class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.idCategoria.label_tag }}
{% render_field filter.form.idCategoria class="form-control" %}
</div>
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span> Search
</button>
</div>
</form>
{% endblock %}
I would suggest you should go with elasticsearch for this.
But you can use django Q objects to do OR query
qs = Questao.objects.filter(Q(textoQuestao__icontains=query_string)| Q(perguntaQuestao__icontains=query_string)|...

Is it correct use for Django ModelForm

My question; i'm used ModelForm instead of forms.Form and i want to organize form in html file instead of forms.py file (i don't want to use attr tag because my form.html file is very complicated) Is this usage correct?
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model=Comment
fields=[
'name',
'email',
'comment',
]
html file
<form class="post-comment-form" method="POST">
{% csrf_token %}
{% if form.errors %}
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
{% endif %}
<div class="form-row">
<div class="form-item half blue">
<label for="id_name" class="rl-label" >Name</label>
{{ form.name}}
</div>
<div class="form-item half blue">
<label for="id_email" class="rl-label" >Email</label>
{{ form.email}}
</div>
</div>
<div class="form-row">
<div class="form-item blue">
<label for="id_comment" class="rl-label">Comment</label>
{{ form.comment }}
</div>
</div>
<button type="submit" class="save-button">Submit</button>
If you don't want to use the {{form}} then you have to give the input field for each model's fields like <input type="text" name = "name"> .For example
<div class="form-row">
<div class="form-item half blue">
<label for="id_name" class="rl-label" >Name</label>
**<input type="text" name = "name">**
</div>
<div class="form-item half blue">
<label for="id_email" class="rl-label" >Email</label>
<input type="text" name = "email">
</div>
</div>
<div class="form-row">
<div class="form-item blue">
<label for="id_comment" class="rl-label">Comment</label>
<input type="text" name = "comment">
</div>
</div>
<button type="submit" class="save-button">Submit</button>
On the html you can have something like this
{% csrf_token %}
{{ form.as_p }}
and then proceed with styling or better yet you can use {% crispy %} which personal I find it great based on what you want to archive, your forms.py looks ok.

Sort by ascending and descending using django-filter

I have the following code for few filterings:
from .models import ProductLaptop
import django_filters
class ProductLaptopFilter(django_filters.FilterSet):
laptops_name = django_filters.CharFilter(lookup_expr='icontains')
laptops_price = django_filters.NumberFilter()
laptops_price__gt = django_filters.NumberFilter(field_name='laptops_price', lookup_expr='gt')
laptops_price__lt = django_filters.NumberFilter(field_name='laptops_price', lookup_expr='lt')
class Meta:
model = ProductLaptop
fields = ['laptops_name', 'laptops_price', 'brand_name']
The html codes for this:
{% load widget_tweaks %}
{% block content %}
<form method="get">
<div class="well">
<h4 style="margin-top: 0">Filter</h4>
<div class="row">
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.laptops_name.label_tag }}
{% render_field filter.form.laptops_name class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.laptops_price.label_tag }}
{% render_field filter.form.laptops_price class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.brand_name.label_tag }}
{% render_field filter.form.brand_name class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.laptops_price__gt.label_tag }}
{% render_field filter.form.laptops_price__gt class="form-control" %}
</div>
<div class="form-group col-sm-4 col-md-3">
{{ filter.form.laptops_price__lt.label_tag }}
{% render_field filter.form.laptops_price__lt class="form-control" %}
</div>
</div>
<button type="submit" class="btn btn-primary">
<span class="glyphicon glyphicon-search"></span> Search
</button>
</div>
</form>
Which gives me a view like below:
Here I want to add an option where people can sort the items in ascending and descending order.
Can anyone give me some suggestions how can I implement this?
For that, you can use OrderingFilter from django-filter. Create this filter in your FilterSet class and provide all fields that should be enabled for ordering.