Deleting multiple items from JSON data in flask form - flask

I have a WTForm that submits its data in JSON format. Say the database is one recipe to many recipe steps.
{
"stepset-0": {
"recipe_step_id": "5",
"recipe_step_name": "step 1",
"recipe_step_temp": "666.0"
},
"stepset-1": {
"recipe_step_id": "6",
"recipe_step_name": "Step 2",
"recipe_step_temp": "57.0"
},
"stepset-2": {
"recipe_step_id": "7",
"recipe_step_name": "Step 3",
"recipe_step_temp": "68.0"
},
"stepset-3": {
"recipe_step_id": "8",
"recipe_step_name": "Step 4",
"recipe_step_temp": "73.0"
}
}
I'm using JS to remove elements from the table, but currently trying to get my head round how to update the data in the recipe_steps table.
The logic should be basically:
Find the steps in the step table that match the recipe.
See if those steps are in the JSON data (match using the id)
Delete anything that isn't in the submitted JSON.
So if I remove the row with recipe_step_id '8', this gets submitted to the route, and the route works out that it isn't in the data, removes it from the db, then processes the rest of the data as per the route.
My current route (showing add/update functions) is thus:
#app.route('/recipe/recipesteps/<int:recipe_id>/update', methods=['GET', 'POST'])
#login_required
def updaterecipesteps(recipe_id):
recipe = Recipe.query.get_or_404(recipe_id)
data = request.form
nested_data = nest_once(data)
for key, val in nested_data.items():
recipe_step_id = val['recipe_step_id']
recipe_step_temp = val['recipe_step_temp']
recipe_step_name = val['recipe_step_name']
recipe_id = recipe
if recipe_step_id:
recipe_step = ReciperecipeSteps.query.get_or_404(recipe_step_id)
recipe_step.name = recipe_step_name
recipe_step.step_temp = recipe_step_temp
db.session.commit()
else:
recipe_step = ReciperecipeSteps(name=recipe_step_name,
step_temp=recipe_step_temp,
recipe_id=recipe_step.recipe_id)
db.session.add(recipe_step)
db.session.commit()
return redirect(url_for('recipe', recipe_id=recipe.id))
The closest I've got to this is this query:
mash_steps = RecipeSteps.query.filter(RecipeSteps.id.not_in([nested_data.recipe_step_id for val in nested_data.items]), RecipeSteps.recipe_id == recipe_id).all()
...but Python won't iterate over the object.

Probably far from the best way of doing this (I'd imagine I could have parsed the JSON data in the query), but this is what I got working:
# Build a list from the step ids
step_id = []
for key, value in nested_data.items():
recipe_step_id = value['recipe_step_id']
step_id.append(recipe_step_id)
# Query matching items that aren't on the list
recipe_steps = RecipeSteps.query.filter(RecipeSteps.id.not_in(step_id), RecipeSteps.recipe_id == recipe.id).all()
# Loop through the results and delete
for m in recipe_steps:
db.session.delete(m)
db.session.commit()

Related

I want to get data from JavaScript with Django

There is price filtering written in JavaScript in the template. I want to take the price range given in this filter with dajngo and write a filtering function. I couldn't because I don't know JavaScript. How will I do?
So, i want to write a django function that takes the given start and end values and sorts the products accordingly.
main.js
// PRICE SLIDER
var slider = document.getElementById('price-slider');
if (slider) {
noUiSlider.create(slider, {
start: [1, 100],
connect: true,
tooltips: [true, true],
format: {
to: function(value) {
return value.toFixed(2) + '₼';
},
from: function(value) {
return value
}
},
range: {
'min': 1,
'max': 100
}
});
}
I'm not familiar with noUiSlider but you would need to get the from and to values into Django - you can do that either by submitting a form when clicking FILTER or by sending an AJAX request. I presume you would just submit the form in a standard page submission as you aren't familiar with JS (and therefore AJAX).
def your_view(request)
filter_from = request.POST.get('slider_from')
filter_to = request.POST.get('slider_to')
YourModel.objects.filter(value__gte=filter_from, value__lte=filter_to)
...
You will need to replace slider_from and slider_to with the key values that are sent by the slider input in request.POST - this will be the name of the inputs themselves. You can wrap request.POST in a print statement to easily see what these are. It's just a matter of getting the values and passing them into the filter() function of your model.

Generating a data table to iterate through in a Django Template

I have this function that uses PrettyTables to gather information about the Virtual Machines owned by a user. Right now, it only shows information and it works well. I have a new idea where I want to add a button to a new column which allows the user to reboot the virutal machine. I already know how to restart the virtual machines but what I'm struggling to figure out is the best way to create a dataset which i can iterate through and then create a HTML table. I've done similar stuff with PHP/SQL in the past and it was straight forward. I don't think I can iterate through PrettyTables so I'm wondering what is my best option? Pretty tables does a very good job of making it simple to create the table (as you can see below). I'm hoping to use another method, but also keep it very simple. Basically, making it relational and easy to iterate through. Any other suggestions are welcome. Thanks!
Here is my current code:
x = PrettyTable()
x.field_names = ["VM Name", "OS", "IP", "Power State"]
for uuid in virtual_machines:
vm = search_index.FindByUuid(None, uuid, True, False)
if vm.summary.guest.ipAddress == None:
ip = "Unavailable"
else:
ip = vm.summary.guest.ipAddress
if vm.summary.runtime.powerState == "poweredOff":
power_state = "OFF"
else:
power_state = "ON"
if vm.summary.guest.guestFullName == None:
os = "Unavailable"
else:
os = vm.summary.guest.guestFullName
x.add_row([vm.summary.config.name, os, ip, power_state])
table = x.get_html_string(attributes = {"class":"table table-striped"})
return table
Here is a sample of what it looks like and also what I plan to do with the button. http://prntscr.com/nki3ci
Figured out how to query the prettytable. It was a minor addition without having to redo it all.
html = '<table class="table"><tr><th>VM Name</th><th>OS</th><th>IP</th><th>Power
State</th></tr>'
htmlend = '</tr></table>'
body = ''
for vmm in x:
vmm.border = False
vmm.header = False
vm_name = (vmm.get_string(fields=["VM Name"]))
operating_system = (vmm.get_string(fields=["OS"]))
ip_addr = ((vmm.get_string(fields=["IP"])))
body += '<tr><td>'+ vm_name + '</td><td>' + operating_system + '</td> <td>'+ ip_addr +'</td> <td>ON</td></tr>'
html += body
html += htmlend
print(html)

odoo: printing qweb report using wizard

i have two reports cout_materiel_facture_detail_report.xml (detailed report) and cout_materiel_facture_report.xml (simple report) and have a wizard where the user inputs some data and chooses whether he/she wants to print the simple report or the detailed one. Here is the wizard's class:
class CoutMaterielFactureWizard(models.TransientModel):
_name = 'gc.cout_materiel_facture_wizard'
directeur_parc_id = fields.Many2one('hr.employee', string='Directeur Parc')
procedure = fields.Char(string='Procedure')
version = fields.Char(string='Verion')
date_realisation = fields.Date(string='Date realisation')
# is_landscape = fields.Boolean(string='Mode paysage?')
is_detail = fields.Boolean(string='Version simplifiee?')
#api.multi
def do_toggle_print(self):
cout_materiel = self.env['gc.cout_materiel'].browse(self._context.get('active_id', False))
cout_materiel.write({
'directeur_parc_id': self.directeur_parc_id.id
})
# Print the simple report
if not self.is_detail:
return {
'type': 'ir.actions.report.xml',
'name': 'gestion_cout.cout_materiel_facture_report_template',
'report_name': 'gestion_cout.cout_materiel_facture_report_template',
}
# Print the detailed report
else:
sql = "SELECT SUM(h_sup)+SUM(h_exp),SUM(h_im),count(*),SUM(total), famille FROM gc_cout_materiel_line where " \
"cout_materiel_id =%s group by famille "
self.env.cr.execute(sql, (cout_materiel.id,))
results = self.env.cr.fetchall()
if len(results) > 0:
line_ids = []
for nbht, nbhim, qte, prix_total, famille in results:
line_ids.append((0, 0, {
'famille': famille,
'type': 'VA',
'qte': qte,
'nbr_heures': nbht,
'nbr_heures_im': nbhim,
'nbr_jours': 28,
'prix_unitaire': 'VA',
'prix_total': prix_total,
}))
self.env['gc.cout_materiel_facture_temp'].create({
'chantier_name': cout_materiel.chantier_id.name,
'mois_name': cout_materiel.mois_id.name,
'num_annexe': cout_materiel.num_annexe,
'expediteur': cout_materiel.expediteur,
'destinateur': cout_materiel.destinateur,
'application_date': cout_materiel.application_date,
'date_realisation': self.date_realisation,
'directeur_parc_name': self.directeur_parc_id.name,
'procedure': self.procedure,
'version': self.version,
'prix_total_global': cout_materiel.total_global,
'line_ids': line_ids,
})
return {
'type': 'ir.actions.report.xml',
'name': 'gestion_cout.gc_cout_materiel_facture_detail_report_template',
'report_name': 'gestion_cout.gc_cout_materiel_facture_detail_report_template',
}
But i get this error after i hit the print button
I checked out the database and found both reports are present there.
Any help? please!!
Finally i managed to solve my problem!!
Here is what i did:
I created a method in the wizard model which returns a list of objects that i whant to print and linked the wizard to the qweb report.
Then i called the method from the qweb report using object.my_mehtod() in a t-foreach loop, where object represents the wizard.
With this way i am able to create complex reports and print them easily. One can use this method to get data from several tables and organize the data and retur them as a list.
I hope that it will help someone.
Best regards!!

How to POST to server with null Related Resource in TastyPie?

I've got the following two models. They are linked by a OneToOneField relation 'mission' going from the TextMission table to the Mission table.
#Models.py
class Mission(models.Model):
name = models.CharField(max_length = 40)
class TextMission(models.Model):
mission = models.OneToOneField(Mission, related_name="text_mission", primary_key = True)
Sometimes missions will have a corresponding TextMission, but not always. I'm able to get everything working when I'm creating/updating objects in the shell, but TastyPie only handles requests correctly when there IS a TextMission (PUTing to /mission/{id}/). When text_mission=null, it craps out.
My tastypie config:
class MissionResource(ModelResource):
text_mission = fields.ToOneField('path.to.TextMissionResource', attribute='text_mission', related_name='mission', full = True, null = True)
class TextMissionResource(ModelResource):
mission = fields.ToOneField(MissionResource, attribute='mission', related_name='text_mission')
When PUTing the following JSON back to the server (the exact same JSON I received), I get a ValueError:
FAILS
{ "name": "TEST", "id": "1", "text_mission": null, "resource_uri":
"/api/web/mission/1/", }
*** ValueError: Cannot assign None: "Mission.text_mission" does not allow null values.
SUCCEEDS
{ "name": "TEST", "id": "1", "text_mission": {"id": "1", "resource_uri": "/api/web/text_mission/1/"}, "resource_uri":
"/api/web/mission/1/", }
Is there something I'm doing wrong in my code or it just not supposed to work this way. I'm at a loss here.
I'm running TastyPie 0.9.11.
I ended up figuring it out and it was a pretty dumb error. I just needed to add blank = True in addition to null = True:
class MissionResource(ModelResource):
text_mission = fields.ToOneField('path.to.TextMissionResource', attribute='text_mission', related_name='mission', full = True, blank = True, null = True)
That got everything working properly.

Filter in multiple parameters in query string

I've got a django app that has a filter as one of it's features.
The filter values are decided from a checkbox which is sent to the django backend using ajax as follows:
$('input.type-check').on('click', function(){
var a = $(this).prop('checked');
if(a == true){
elem.push($(this).attr('data-role'));
}else{
elem.splice($(this).attr('data-role'));
}
var cats = '';
$.each(elem, function(i){
cats += elem[i];
});
var xurl ='/filter?category='+cats;
$.ajax({
type: 'GET',
url: xurl,
success: function(data){
$('div.products').html(data);
}
})
});
The /filter$' url is mapped to thefitlered` view which is:
def filtered(request):
if 'category' in request.GET and request.GET['category']:
cat = request.GET['category']
ct = Product.objects.filter(category__in=cat)
diction = {'prods': ct}
return render(request, 'filter.html', diction)
It works when only one category is sent as parameter. However, when I send multiple, it gives no results.
Eg:
filter?category=Dairy will return the product that's associated with that category. However, filter?category=Dairy,Plastics or filter?category=DairyPlastics (which is from the above mentioned Javascript snippet) returns no result.
I've tried putting the category inside brackets in the view as follows [cat] but that doesn't help either. What should I do to make it return results?
The issue is, you are neither specifying a delimiter to demarcate the categories, not are you separating the categories in the view.
Try this:
In JQuery,
var cats = elem.join(', ')
var xurl ='/filter?category='+cats;
And in the view:
def filtered(request):
if request.GET.get('category'):
cat = request.GET.get'category')
cat_list = [c.strip() for c in cat.split(',')]
ct = Product.objects.filter(category__in=cat_list).distinct()
#You might need a distinct clause too, to remove duplicates
diction = {'prods': ct}
return render(request, 'filter.html', diction)
This would work for a single category v/s a list of categories