Django using template input data to create new objects - django-views

Due to my lack of skills I think I overcomplicate and would like to get input for a simple solution. I'm creating kind of a web shop template. I have a service work order that I would like to add parts to. I have a decent looking template where I can list all parts that fits a machine model and then filter through them.
One Workorder many Parts (So I have one separate class for all parts objects for workorder). SO to avoid to have to update part number by part number, using create view. I have tried to use a listview where I try to select all parts at the same time.
In my CBV I define the filtering and get a context "Partslist" with all parts to show in table. In the template I have created an input fields qty. I.e. all part in the "Partslist" gets a "qty" inputfields.
In my post or save definition can I simply catch all input fields data with a self.request.GET statement somehow, or could I use the context "Partslist" to iterate through the fields?
models.py
class WorkOrderParts(models.Model):
wo_num = models.CharField(max_length=30, verbose_name='Order Number')
wo_pa_number = models.CharField(max_length=30, verbose_name='Part Number')
wo_pa_qty_def = models.DecimalField(max_digits=3,decimal_places=0,verbose_name='Qty Planned for')
class Parts(models.Model):
pa_number = models.CharField(max_length=30,verbose_name='Part Number')
pa_group1 = models.CharField(max_length=30,verbose_name='Group of Parts')
pa_group2 = models.CharField(max_length=30,verbose_name='2:nd Group of Parts')
pa_fits = ArrayField(models.CharField(max_length=20,verbose_name='models that part fit to (BRAND-MODEL)'),default=list, null=True, blank=True)
views.py
class WO_PartCreate(LoginRequiredMixin,ListView):
login_url = '/login'
template_name = 'AddPart.html'
model = WorkOrderParts
def get_context_data(self, **kwargs):
model = 'my machine model'
qs=Parts.objects.filter(pa_fits__icontains=model)
context = super().get_context_data(**kwargs)
context['Partslist'] = qs
return context
def post(self):
??? This is where I want to take all parts with input qty>0 and update new objects into WorkOrderParts.
AddPart.html
<table>
<thead>
<tr>
<th class="text-left">Part Number</th>
<th class="text-left">Quantity</th>
</tr>
</thead>
{% for item in Partslist %}
<tbody>
<tr>
<td class="text-left">
{% autoescape off %}{{item.pa_number}}{% endautoescape %}<br>
</td>
<td class="text-left">
<input type="number" method="GET" name="{{ item.pa_number }}-Qty"/>
</td>
</tr>
</tbody>
I hope I have been able to explain enough for someone to understand me.The reason why I use a table is simply that I have much more data in there that I want to display.

OK I know there is most probably better ways to solve this. But since I at least was able to find out something that worked, I thought I would share it with others.
It at least solved my immediate need to grab input fields from a dynamic list and do something with it.
views.py
class AddPartTest(LoginRequiredMixin,ListView):
login_url = '/login'
template_name = 'AddPart.html'
model = Parts
form_class = Add_Parts2 ##including selected fields
def get_context_data(self, **kwargs):
order = self.kwargs.get('str')
wo = WorkOrder_main.objects.get(order=order)
model = wo.wo_machine_model
qs=Parts.objects.filter(pa_fits__icontains=model)
first_contains_query = self.request.GET.get('pa_group1')
if is_valid_queryparam(first_contains_query):
qs=qs.filter(pa_group1=first_contains_query)
second_contains_query = self.request.GET.get('pa_group2')
if is_valid_queryparam(second_contains_query):
qs=qs.filter(pa_group2= second_contains_query)
context = super().get_context_data(**kwargs)
context['Partslist'] = qs
return context
def post (self, request, *args, **kwargs):
order = self.kwargs.get('str')
context = self.get_context_data()
Partslist_x = context['Partslist']
Parts_num_list = Partslist_x.values_list('pa_number', flat=True)
for part in Parts_num_list:
qty = part + 'Qty'
Qty = self.request.POST.get(qty)
if int(Qty)==0:
pass
else:
WorkOrderParts.objects.create(
wo_num=order,
wo_pa_number=part,
wo_pa_qty_def=Qty,
)
path='where I want to end up'
return redirect (path)

Related

Django displaying CPU average for many VMs using ListView

I have a model that stores a list of VMs and their management IPs. I have another model that stores cpu consumption for all of the VMs tied to the foreign key of the VM. There is a cron job that will run every five minutes to populate the Cpu model with the cpu consumption of each VM.
What I want to do is create a table in a template that lists all VMs, with an average of the CPU consumption, and max CPU consumption.
'''
#models.py
from django.db import models
class VMs (models.Model):
mgmtIP = models.CharField(max_length=16)
hostName = models.CharField(max_length=128)
def __str__(self):
return self.hostName
class Cpu (models.Model):
hostName = models.ForeignKey(F5Model, on_delete=models.CASCADE)
cpuAverage = models.DecimalField(max_digits=3, decimal_places=2)
whenReported = models.DateTimeField(auto_now=False, auto_now_add=True)
#views.py
from .models import Cpu, VMs
class Report(ListView):
model = VMs
template_name = 'report.html'
def get_queryset(self):
queryset = Cpu.objects.filter(hostName= self.hostName).aggregate(Avg(cpuAverage))
return queryset
'''
I can get the basic ListView to show the VMs in my template but when I try to add the queryset it stops displaying anything.
!!!Update!!!
I am able to get it to sort of work with this
'''
class Report(ListView):
model = VMs
context_object_name = 'cpu_list'
template_name = 'report.html'
def get_context_data(self, **kwargs):
context = super(Report, self).get_context_data(**kwargs)
context.update({ 'cpuAverageReport':Cpu.objects.filter(hostName_id='1').aggregate(Avg('cpuAverage')),
})
return context
'''
The problem is I need the hostName id to be dynamic. It should be which ever host it is currently looping through. I put in '1' just to test the rest of my syntax... I've tried 'self.kwargs['id'], self.args['id'], just id, VMs.id, and probably a thousand other combinations...
The problem is aggregate only returns one line. I needed to change it for annotate.
'''
cpuAverageReport':Cpu.objects.values('hostName_id').annotate(Avg('cpuAverage'))
'''
Then I needed to add a for loop into my for loop and an if to match the records up with each other in my template. like so...
'''
<table style="width:100%">
<tr>
<td>Name</td><td>CPU Average</td>
</tr>
{% for x in report %}
<tr>
<td>{{ x.hostName }}</td>
{% for y in cpuAverageReport %}
{% if x.id == y.hostName_id %}
<td>{{ y.cpuAverage__avg }}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
'''

How to bind more than one model to a template

I'm trying to feed a select tag with options recovered from a database.
The problem is, I'm totally beginner with Django and don't even know how to search for this.
I'm using generic view and as far as I know, the template is fed by a model bound to a context_object, default named as object_list, but you can change it in the context_object_name variable.
But my companies_object is not feeding the template.
<tbody>
{% for project in projects %}
<tr>
<td>
{{ project.title }}
</td>
[...]
<select>
{% for company in companies %}
<option value="{{company.id}}">{{company.name}}</option>
{% endfor %}
</select>
class ProjectsView(LoginRequiredMixin, ListView):
model = Project
context_object_name = 'projects'
template_name = 'projects/projects.html'
def select_company(self):
companies = Company.objects.all()
return 1 #return selected company
def get_projects(self):
seek_in_database()
return projects
I expect to know how to show two different objects in the same template, the projects, which is already working, and the companies object.
I didn't figure it out yet how the template is getting the project's data, I suspect of model = Projects and context_object_name.
I know that it is begginer level, and I don't expect someone to write a complete guide, I'll be very happy with some instruction of what subject to look.
Here an example how I do it:
class CompanyListView(ListView):
model = Company
context_object_name = 'companies'
template_name = 'core/company/listCompanies.html'
queryset = Company.objects.get_main_companies()
def get_context_data(self, **kwargs):
context = super(CompanyListView, self).get_context_data(**kwargs)
context.update({
'company_abbr': self.request.session.get('company_abbr'),
'page_title': 'Manage Companies',
})
return context
So in get_context_data, you may add as many data as you need.

Editing FormPreview form data before preview in Django

I'd like to alter the data collected from the form after a user clicks Preview but before it is shown to them again.
class StoryForm(forms.Form):
title = forms.CharField()
story = forms.CharField(widget=forms.Textarea)
class StoryFormPreview(FormPreview):
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
return HttpResponseRedirect('/form/success')
Before the preview is shown I want to append to whatever the user entered for the story field and add "Brought to you by so and so." How would I go about that? I've played around a lot with the process_preview and preview_post methods but couldn't get anything to work.
As I understand, you can modify it with
def process_preview(self, request, form, context):
"""
Given a validated form, performs any extra processing before displaying
the preview page, and saves any extra data in context.
"""
pass
UPD:
I found only solution with form clean method:
class StoryForm(forms.Form):
...
def clean_story(self):
self.cleaned_data['story'] = ...
return self.cleaned_data['story']
class StoryFormPreview(FormPreview):
def process_preview(self, request, form, context):
preview_data = {}
for key, value in form.cleaned_data.iteritems():
preview_data[key] = value
context['preview_data'] = preview_data
formtools/preview.html
{% for label, data in preview_data.iteritems %}
<tr>
<th>{{ label }}:</th>
<td>{{ data }}</td>
</tr>
{% endfor %}

Generate choice field with existing ORM data in Django

I'm using Django 1.4 with Python 2.7 on Ubuntu 12.04.
I'm going to continue to update this question as I make progress.
UPDATE 2:
I'm trying to generate 2 choice fields in a form using information from an existing model.
class AssignProject(forms.Form):
def __init__(self, devs, *args, **kwargs):
"""
.. method:: __init__()
Class constructor
:param devs: Tuple with which developer
"""
super(AssignProject, self).__init__(*args, **kwargs)
self.dev = forms.ChoiceField(widget = forms.Select(), choices = devs, required = True)
self.designer = forms.ChoiceField(widget = forms.Select(), choices = devs, required = True)
At this point I can't seem to access dev and designer ChoiceField in my template yet.
Here is the view:
#login_required
def view_all_projects(request):
"""
.. function:: view_projects()
Show the projects
:param request: Django Request object
"""
data = { 'user' : request.user }
if (request.user.is_authenticated() and request.user.is_superuser):
all_projects = Projects.objects.filter(active = True)
dev_info = User.objects.filter(is_staff = True, is_superuser = False)
dev_dict = {}
for dev in dev_info:
dev_dict[dev.id] = '{0} {1}'.format(dev.first_name, dev.last_name)
devs = tuple(dev_dict.items())
form = AssignProject(devs)
data.update({ 'form' : form })
data.update({ 'projects' : all_projects })
data.update(csrf(request))
return render_to_response("view_all_projects.html", data)
return render_to_response("index.html", data)
I have verified that the developers/designers are properly getting set in the devs tuple.
...and the template from view_all_projects.html:
<form action="/assignProject/" method="post">{% csrf_token %}
<table>
<td>
<input type="hidden" name="project_id" value={{ project.id }}>
<tr>
<td align="right"><label class="formlabel">Assign Developer:<br /></label></td><td>{{ form.dev }}</td>
</tr>
<tr>
<td align="right"><label class="formlabel">Assign Designer:<br /></label></td><td>{{ form.designer }}</td>
</tr>
<tr>
<td align="right"><label class="formlabel"> </label></td><td><input type="submit" value="Submit ►"></td>
</tr>
</td>
</table>
</form>
I don't see any errors, but I do see a strange object reference in place of the ChoiceField in the template.
<django.forms.fields.ChoiceField object at 0x7ffdbc054190>
<django.forms.fields.ChoiceField object at 0x7ffdbc0542d0>
I see these instead. I know I'm close...just can't quite get what I'm going wrong.
Thoughts?
The issue was entirely in the form __init__.
I should have assigned the fields like follows:
class AssignProject(forms.Form):
def __init__(self, devs, *args, **kwargs):
"""
.. method:: __init__()
Class constructor
:param devs: Tuple with developers
"""
super(AssignProject, self).__init__(*args, **kwargs)
self.fields['dev'] = forms.ChoiceField(widget = forms.Select(), choices = devs, required = True)
self.fields['designer'] = forms.ChoiceField(widget = forms.Select(), choices = devs, required = True)
Note the self.fields['dev'] not self.dev. Fixed everything.

modelformset_factory does not honor extra parameter

Django: 1.4.1
Model:
class Hoja(models.Model):
nombre = models.CharField(max_length=200) # requerido
class Linea(models.Model):
hoja = models.ForeignKey(Hoja) # requerido
nombre = models.CharField(max_length=200) # requerido
padre = models.ForeignKey('self', null=True, blank=True, related_name='hijo')
View:
lineas = Linea.objects.filter(hoja=alt).order_by('id')
LineaHojaSet = modelformset_factory(Linea, can_delete=True, extra=1 if request.POST.has_key('siguiente') else 0)
formset = LineaHojaSet(request.POST or None, queryset=lineas)
if request.method=='POST':
# process formset
return render_to_response('template.html', {'formset':formset}, context_instance=RequestContext(request))
Template:
<table>
<thead>
<tr><th>Nombre</th><th>Borrar</th></tr>
</thead>
<tbody>
{% for fs in formset %}
<tr>
<td>{{ fs.nombre }}</td>
<td>{{ fs.id }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" name="siguiente" value="Añadir siguiente" />
When I submit the "siguiente" button, I can see than the formset is getting the correct extra field of 1, but in the webpage, the only rows showing are the database ones. It's this a bug, or I'm doing something wrong?
Formset factory finds number of forms either by max_num, extra parameters or form-TOTAL_FORMS parameter in request.POST (or data) from management form.
In your case, request.POST['form-TOTAL_FORMS'] has number which does not include extra form . So it does not add extra form when you create formset.
One solution would be to increment this number by one when your condition is met. e.g.
data = None
if request.POST:
data = request.POST.copy() #required as request.POST is immutable
if request.POST.has_key('siguiente'):
data['form-TOTAL_FORMS'] = int(data['form-TOTAL_FORMS']) + 1
#now use data instead of request.POST
formset = LineaHojaSet(data, queryset=lineas)
....
However, there are some drawbacks of manipulating formset this way. When you validate formset, the extra form will show errors if there are any required fields.
Better solution would be to create formset again before passing it template with one extra form and queryset. Most likely, when formset is valid, you would save any new objects, those will get added by queryset. So your page will show newly added objects and one extra form.
lineas = Linea.objects.filter(hoja=alt).order_by('id')
LineaHojaSet = modelformset_factory(Linea, can_delete=True,)
formset = LineaHojaSet(request.POST or None, queryset=lineas)
if request.method=='POST':
# process formset
if formset.is_valid:
#saved and done with formset.
if request.POST.has_key('siguiente'):
LineaHojaSet = modelformset_factory(Linea, can_delete=True, extra=1)
formset = LineaHojaSet(queryset=lineas)
...
return render_to_response('template.html', {'formset':formset}, context_instance=RequestContext(request))