django: How to use ModelForm with dynamic 0-n possibilities? - django

The Quetion
Hello. I'm trying to figure out the best way to use one form to create one parent object and then create 0-n sub objects. I'm assuming ModelForms are what I need to do, but I'm having a hard time understanding how to structure the template and view. Would someone be willing to explain how to create n number of sub objects?
The Research
I've read several other articles and posts relating to this:
django model/modelForm - How to get dynamic choices in choiceField?
https://stackoverflow.com/questions/5575560/how-do-i-create-a-drop-down-menu-in-django-using-a-modelform-with-dynamic-values
http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/
Overriding the save method in Django ModelForm
To name a few.
The Details
I have two models like so:
// models.py
class DataItem(models.Model):
name = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
date_last_updated = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, blank=False)
def __unicode__(self):
return self.name
class DataItemSet(models.Model):
item = models.ForeignKey(DataItem, blank=False)
type_ind = models.IntegerField()
And I've created two ModelForms to correspond:
// forms.py
class CreateDataItemForm(forms.ModelForm):
class Meta:
model = DataItem
exclude = ('owner',)
def save(self, user, commit=True):
item = super(CreateDataItemForm,self).save(commit=False)
item.owner = user
if commit:
item.save()
return item
class CreateDataItemSetForm(forms.ModelForm):
class Meta:
model = DataItemSet
exclude = ('item',)
def save(self, parent, commit=True):
set = super(CreateDataItemSetForm,self).save(commit=False)
set.item = parent
if commit:
set.save()
return set
And in my view, I'm trying to have one form submit the creation of a new DataItem and 1-n DataItemSets which are owned by the DataItem. Here is an example of the output of the form:
And the template:
<form action="." method="post">
{% csrf_token %}
<table>
{{ create_form.as_table }}
</table>
<table>
<tr>
<th>What to track:</th>
<td>
<select>
<option value="1">Number</option>
<option value="2">Currency ($)</option>
<option value="3">Date</option>
<option value="4">Day</option>
<option value="5">Time</option>
</select>
</td>
<td>
</td>
</tr>
<tr>
<th>What to track:</th>
<td>
<select>
<option value="1">Number</option>
<option value="2">Currency ($)</option>
<option value="3">Date</option>
<option value="4">Day</option>
<option value="5">Time</option>
</select>
</td>
<td>
<button type="button">+</button>
</td>
</tr>
</table>
<p>
<button type="submit">Create</button>
</p>
</form>
And lastly, the view:
// views.py
#login_required
#csrf_protect
def create_data_item(request):
create_form = CreateDataItemForm()
c = {'create_form':create_form}
c.update(csrf(request))
if request.method == 'POST':
data = request.POST.copy()
form = CreateDataItemForm(data, instance=DataItem())
item_sets = [CreateDataItemSetForm(request.POST, prefix=str(x), instance=DataItemSet()) for x in range(0,9)]
if form.is_valid():
# create new data item
new_item = form.save(request.user)
#create new set of stuff to track
for item_set in item_sets:
new_item_set = item_set.save(new_item)
# return to the add entry page
return redirect('/add')
else:
return render_to_response('internal/create_data_item.html',c)
I'm missing how to allow for dynamic extraction of 0-n DataItemSets from the form. I can't figure it out with ModelForm.
Thanks for any help!

This is what model formsets - specifically, inline formsets - are for.

Related

Data cannot saved in django formview

I'm going to receive data and save it using form and save it. But I can't get any result. Let me know what I'm doing wrong.
I set up a model. And I wrote a form to get the input. Forms.Form was used. At first, I used modelform, but I wrote it like this because there seemed to be no difference.
Is label important in the form? You can't get the data value because you can't connect the label?
heeelp!
models.py
class PayHistory(models.Model):
branch = models.ForeignKey(Branch, on_delete=models.CASCADE, null=True)
package_recommandation_date = models.DateField(null=True)
package_payment_date = models.DateField(null=True)
forms.py
class PackageForm(forms.Form):
package_recommandation_date = forms.CharField(label='package_recommandation_date')
package_payment_date = forms.CharField(label='package_payment_date')
...
## i changed like this(1)
class PackageForm(forms.ModelForm):
class Meta:
model = PayHistory
fields = ['package_recommandation_date', 'package_payment_date']
views.py
class PackageView(FormView):
model = PayHistory
template_name = 'branches/package-create.html'
success_url = reverse_lazy('/')
form_class = PackageForm
def form_valid(self, form):
form = form.save(commit=False)
form.save()
return super().form_valid(form)
### I realize what you mean. I changed it like this(2) and it was saved in DB.
def form_valid(self, form):
data = PayHistory()
data.package_recommandation_date = form.cleaned_data['package_recommandation_date']
data.package_payment_date = form.cleaned_data['package_payment_date']
data.save()
return super().form_valid(form)
# HTML
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="table-content">
<!-- 검색 -->
<table border="0">
<tr class="input-tr">
<td><input type="date" class="input1" name="package_recommandation_date"
value="{{ form.package_recommandation_date.value|default_if_none:'' }}" required>
</td>
<td><input type="date" class="input2" name="package_payment_date"
value="{{ form.package_payment_date.value|default_if_none:'' }}">
</td>
...
<td><button type="submit" class="input-button input16">적용</button></td>
# static/js
const package_recommandation_date = document.querySelector("package_recommandation_date");
const package_payment_date = document.querySelector("package_payment_date");
console.info(package_recommandation_date, package_payment_date)
#output
-> null null

Need to extract data matching child model and also the parent data linked to it

I am new to world of Django and Python. I have two models: Testing (Parent) & Heat_Status (Child):
Models:
class Testing(models.Model):
Testing_Date = models.DateField(auto_now=False)
IE_Testing = models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return '%s %s' %(self.Testing_Date, self.Testing_Shift)
class Heat_Status(models.Model):
Heat_Number=models.CharField(max_length=6)
Testing_Detail=models.ForeignKey(Testing,null=True,on_delete=models.CASCADE)
def __str__(self):
return '%s %s' %(self.Heat_Number, self.Testing_Detail.Testing_Result)
Now I want to search a Heat_Number saved in database along with the details linked to its Parent in Testing model. I got a way to search the data but unable to access the foreign key. I am posting my code here.
Views:
def status (request):
if request.method=="POST":
srch=request.POST['srh']
if srch:
match= Heat_Status.objects.filter(Q(Heat_Number__iexact=srch))
if match:
return render (request,'status.html',{'sr':match})
else:
messages.error(request,'NO Heat')
else:
return reverse('status')
return render(request,'status.html')
Template
<form method="POST" action="/crinsp/status">
{%csrf_token%}
input type="text" name="srh" classs="form-control">
<button class="submit" class="btn btn-warning">Search</button>
</form>
{%if sr %}
{%for k in sr%}
<table>
<tr>
<td>Heat Number</td>
<td>{{k.Heat_Number}}</td>
</tr>
<tr>
<td>Rolled in</td>
<td> {{**k.Heat_Status.Testing.Testing_Date**}} </td> # I need Help here
</tr>
</table>
{%endfor%}
{%endif%}
Code is correct and answer for the problem is just a slight change in the HTML code.
While calling the information of parent field we must use the name assigned to foreign key in child model.
{{ k. Testing_Detail. Testing_Date}}

unable to save multiple values via Django Form

The post request is sending multiple values with same key e.g. values=foo&values=bar
I am seeing only one value in request object in both Django view and form. Not sure what I need to do to get multiple values in Django request object.
// model
class AttributeInstance(models.Model):
somefilter = models.CharField(max_length=255, blank=True)
values = models.TextField()
//form
class ABCModelForm(forms.ModelForm):
class Meta:
model = ABCModel
fields = ('somefilter', 'value')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.data:
self.fields['values'] = forms.MultipleChoiceField(())
// view
class ABCModelView(FormView):
def get(self, request):
form = ABCModelForm()
return render(self.request, 'core/abc_model_edit.html', {'form': form})
def post(self, request):
try:
form = ABCModelForm(request.POST)
form.save()
form = ABCModelForm()
return render(self.request, 'core/abc_model_edit.html', {'form': form})
except Exception as e:
return HttpResponse(status='400')
<!-- HTML -->
<!-- fills the multiple choice field on runtime based on somefilter -->
<!-- the multiple choice UI element looks like below after rendering -->
<form method="post" id="abcModelForm" novalidate="">
<input type="hidden" name="csrfmiddlewaretoken" value="abcdcdcd">
<table>
<tbody>
<tr>
<th><label for="id_somefilter">Description:</label></th>
<td><input type="text" name="somefilter" maxlength="255" id="id_somefilter"></td>
</tr>
<tr>
<th>
<label for="id_values">Values:</label>
</th>
<td>
<select name="values" required="" id="id_values" multiple="multiple">
<option value="dodo">dodo</option>
<option value="bobo">bobo</option>
<option value="foo">foo</option>
<option value="bar">bar</option>
</select>
</td>
</tr>
</tbody>
</table>
<button type="submit">Save</button>
</form>
Its frustrating to know that Django doesn't seem to handle multiple values in form data gracefully.
I used Intellij debugger to inspect request.POST. I could see multiple values in a list for key values but QueryDict as the name suggest seems to understand that there can be only one value for a key and drops the remaining values. For now I have added following hack but not very happy with it. Still looking for a cleaner solution.
payload = dict(request.POST)
payload = {
key: (value if key == 'values' else value[0])
for key, value in payload.items()
}
queryDict = QueryDict(json.dumps(payload))
form = AttributeInstanceForm(queryDict)
// this is how payload looks after conversion from QueryDict
{
'description': ['hmm'],
'values': ['foo', 'bar', 'gogo']
}
I am using Content-Type: application/x-www-form-urlencoded

Creating multiple Django model forms to change/edit multiple existing objects on the same page without passing object-id via URL

I'm trying to build an Object detection service where the user can choose multiple objects he would like to detect. The Object Model looks like the following:
#models.py
class Object(models.Model):
name = models.CharField(max_length=50)
image = models.ImageField()
detect = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('Objectdetector:objectdetail', kwargs={'pk': self.pk})
def __str__(self):
return self.name
The page is showing an overview of all the objects a user has created and there is a checkbox next to each object linked to the "detect" attribute. The model form looks like this:
#forms.py
class DetectForm(forms.ModelForm):
class Meta:
model = Object
fields = ['detect']
So in the views.py I'm trying to stick to the Django documentation and pass the instance to the form like such:
form = DetectForm(instance = objectinstance)
but i can't figure out how I am supposed to do it correctly. This is the view, you can ignore the post method for now:
class DetectView(generic.ListView):
template_name = 'Objectdetector/detect.html'
def get(self,request):
objectinstance = #Please help
form = DetectForm(instance = objectinstance)
objs = Object.objects.all()
args = {'form':form, 'all_obj': objs}
return render(request, self.template_name, args)
def post(self, request):
form = DetectForm(request.POST)
if form.is_valid():
form.save()
return redirect('Objectdetector:detectobjects')
args = {'form':form}
return render(request, self.template_name, args)
I then want to loop through all objects and display the images and names linked to them (already working) and display the checkbox next to each object. When the user checks the box of an object and hits submit, the detect attribute should be updated to true. Unfortunately I can only loop through the objects and display a checkbox, but when I hit submit, a new object is created. The table in the detect.html file looks like this:
#detect.html
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Image</th>
<th>Select</th>
</tr>
</thead>
<tbody>
{% for object in all_obj %}
<tr>
<td>
{{ object.name }}
</td>
<td>
<img src="{{ object.image.url }}" height="100">
</td>
<td>
<form method = "post" >
{% csrf_token %}
{{ object.id }}
{{form.as_p}}
<button type="submit">CONTINUE</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>
Everything else is working fine, I just don't know how to create and show the checkboxes with the right instance.
I'm pretty sure that I'm missing something very small here due to my lack of Django-knowledge. I would be so happy if someone could help me out!
Thank You so much.

Django forms field not appearing on webpage

Fields I have added in django forms are not visible on webpage.
Attached model, view and html for the reference below.
This is an additional filed which I intent to add to the forms, I am new to Django and learning by enhancing the current project.
"estimated_headcount" is the new filed I have added in the forms.
Thanks
Model
class EstimatedHeadcount(models.Model):
count = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Meta:
default_permissions = []
#staticmethod
def __gotoadmin__():
return True
forms.py
class ClientProfileForm(forms.ModelForm):
class Meta:
model = ClientProfile
fields = ('full_name', 'short_name', 'account_payable',
'require_job_number', 'currency', 'segment', 'market', 'estimated_headcount', 'is_technicolor',
'address')
views.py
def client_profile(request):
all_profiles = ClientProfile.objects.filter(status='active')
profile = None
pid = request.GET.get('pid')
client_profile_form = ClientProfileForm()
if pid:
profile = ClientProfile.objects.get(id=pid)
client_profile_form = ClientProfileForm(instance=profile)
if request.method == 'POST':
client_profile_form = ClientProfileForm(request.POST, instance=profile)
if client_profile_form.is_valid():
profile = client_profile_form.save()
profile.csv_mapping = profile.full_name
profile.save()
if profile:
for task_type in TaskType.objects.all():
if not profile.task_costs.filter(task_type=task_type):
task_cost = TaskCost(task_type=task_type)
task_cost.save()
profile.task_costs.add(task_cost)
return render(request, "prod/client_profile.html", {'all_profiles': all_profiles,
'profile': profile,
'client_profile_form': client_profile_form})
clientprofile.html
<div class="content">
<form id='add_new_client_form' method="post" action="">
{% csrf_token %}
<table class="table">
<tbody>
{{ client_profile_form.as_table }}
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button class="lock" type="button"
onclick="unlock(this, '#add_new_client_form')">Unlock
</button>
<button type="submit">SAVE</button>
</td>
</tr>
</tfoot>
</table>
</form>
</div>
As far as I can tell from your code, there is no relation between the ClientProfile model and the EstimatedHeadcount model.
estimated_headcount should be a field on the ClientProfile model.
class ClientProfile(models.Model):
...
estimated_headcount = models.CharField(max_length=100)
Side note: I would expect the estimated headcount to be a numeric value, so an IntegerField or PositiveIntegerField might be a better choice.