Duplicate query for every form in the formset - python

In my django application I have users which can have multiple positions. ie: The Position model has a foreign key to the User model. By using a django modelformset_factory I output all the positions associated to the currently logged in user as follows:
Views.py:
class ABCUpdate(View):
def get(self, request):
user = request.user
PositionFormSet = modelformset_factory(Position)
formset = PositionFormSet(queryset=user.position_set.all().prefetch_related('symbol'))
return render(request,
'some_template.html',
{'formset': formset})
some_template.html
<form action="#" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for position in formset %}
<div class="row">
{{ position }}
</div>
{% endfor %}
<button type="submit">
Update
</button>
</form>
Position model:
class Position(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
symbol = models.OneToOneField(Symbol)
# more fields...
One of the fields on some_template.html is the symbol field (ie: the one for which I am using prefetch_related in the above snippets). Symbol is from a different model using a foreign key. This means that on the html template, the symbol field is a dropdown allowing the user to select the desired symbol.
This all works correctly, however the problem is that the database is being queried for every single form in the formset to retrieve the list of symbol's. For example if the user has 10 positions, then each position will have a dropdown for symbol,...which results in a separate database query to retrieve all the symbols.
How do I go about just doing one query for all the available symbol's and using that for the dropdown in every form in the formset?

I think you should point in prefetch_related all the fields of the model Position that have a link to another model, in your cases you must add field user:
PositionFormSet(queryset=user.position_set.all().prefetch_related('symbol', 'user')).
P.S. it worked for me.

Related

How to frequently update some particular field in database?

This is my model to store bus details. Here I have kept a field named bookedSeat to store which seat is booked ( input a-z or A-Z ).Every-time user book a seat a single character (inputted from user) should be added to bookedSeat field in database.
class busDetails(models.Model):
busID = models.AutoField(primary_key=True)
arrival = models.CharField(max_length=50)
destination = models.CharField(max_length=50)
rent = models.IntegerField()
distance = models.IntegerField()
bookedSeat = models.CharField(max_length=100)
def __str__(self):
return self.arrival+' --> '+self.destination
I am getting stuck at how to frequently update(add into existing) that particular database field(bookedSeat)? (without adding any new row)
How do you solve this problem?
Thank You :)
Create an updateview for that Model and specify the fields that you would like to update. You can use a custom form, But trust me these generic views will save you a lot of time .
views.py :
from django.views.generic.edit import UpdateView
class updimagesUpdateView(UpdateView):
model = updimages
fields = ['approval']
template_name_suffix = '_update_form'
Of all the attributes, you can mention the ones you need to be updated.
As shown in the last line above (template_name_suffix) , use the same naming suffix pattern for the template... [modelname_update_form.html] and display the form...
modelname_update_form.html :
{% extends "base.html" %}
{% load static %}
{% load bootstrap_tags %}
{% block title %} Update {% endblock %}
{% block body%}
<div class="jumbotron">
Are you sure you want to approve the Image ?
</div>
<br>
<form method="POST">
{% csrf_token %}
{{ form|as_bootstrap }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
make sure you get the corresponding element you want to update from the template :
<h3>update</h3>
clicking the above triggers the url. Add the urls ,
urls.py:
path('update/<int:pk>', views.updimagesUpdateView.as_view() , name = "update"),
PS: You can also update values from views using queries to select the desired object and then editing them like this ,
in views.py :
example = model.objects.filter(id=images.id).update(content=txt)
check out the documentation for more info :
https://docs.djangoproject.com/en/3.1/ref/class-based-views/generic-editing/

How to loop through a form and add same form in django if we click add more button and store that in django

What I really want to do is , if a user click on "ADD more" button then a same form repeat itself and the values should store in database, if he/she doesn't click of that button then only the values from first form should be stored.
I am not able to get this, I just created a form , and a table in database for those details but can't loop though the form neither in data.
please help.
This is the form and the button:
This is the model.py code:
from django.db import models
class experience(models.Model):
company_name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
startdate = models.Datefield(default = 01-01-2020)
lastdate = models.DateField(default = 01-01-2020)
profile = models.CharField(max_length=100)
description = models.TextField(max_length = 250)
This is the views.py code:
from django.shortcuts import render, redirect
import requests
from django.contrib.auth.models import User, auth
# Create your views here.
def profile(request):
return render(request, 'profile.html')
Unfortunately, there's no built-in way (as far as I know) in Django to do that without Javascript, but here's an approach:
HTML:
<div class="container" id="experiencesContainer">
<form method='POST' name='experienceForm'>
{{form.as_p}}
</form>
<form method='POST' name='experienceForm'>
{{form.as_p}}
</form>
<button type="button" id="addMoreButton">Add more</button>
<button type="submit">Save Changes</button>
</div>
Django POST method:
# Get a list of submitted forms
experiences = request.POST.getlist('experienceForm')
for experience in experiences:
# this is how you loop throuh every form
experience.get('company_name)
Your javascript something like:
// clonning his childs as well
let cloneForm = document.querySelector('form[name=experienceForm]').cloneNode(true);
document.querySelector('div#experiencesContainer').appendChild(cloneForm);
// see this https://www.w3schools.com/jsref/met_node_clonenode.asp
Of course this code is not tested but I've done this in several projects before, hope it works!
A simple way would be to request the same view from the "Add", just make sure your form view saves the data when request method is POST.
<form action="{% url 'your-form-url' %}" method="GET">
{% csrf_token %}
<input type="submit" value="Add">
</form>
one other way to repeat forms would be using formsets. Formsets allow you to repeat the same form 'extra' times. Check out the documentation for more about this.
def repeat_form(request):
ExpFormSet = formset_factory(ExperienceForm, extra=3)
#extra defines the no. of forms you want to display
if request.method == 'POST':
formset = ExpFormSet(request.POST, request.FILES)
if formset.is_valid():
# do something with the formset.cleaned_data
#loop through each form in the formser
for form in formset.cleaned_data:
obj = form.save()
else:
formset = ExpFormSet()
return render(request, 'exp_form.html', {'formset': formset})
The corresponding template should be:-
<form method="post">
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
</form>
Make sure you add form.management_form. Using the combination of the above might solve your problem of taking and saving several inputs.

Creating Dropdown from Model in Django

I am looking to create a dropdown in a template where the values of the dropdown come from a field (reference) within my Orders model in models.py. I understand creating a dropdown where the values are set statically, but since I am looking to populate with values stored in the DB, I'm unsure of where to start.
I've created the model and attempted playing around with views.py, forms.py and templates. I am able to get each of the order numbers to display but not in a dropdown and I am struggling with how to write my template.
models.py
from django.db import models
class Orders(models.Model):
reference = models.CharField(max_length=50, blank=False)
ultimate_consignee = models.CharField(max_length=500)
ship_to = models.CharField(max_length=500)
def _str_(self):
return self.reference
forms.py
from django import forms
from .models import *
def references():
list_of_references = []
querySet = Orders.objects.all()
for orders in querySet:
list_of_references.append(orders.reference)
return list_of_references
class DropDownMenuReferences(forms.Form):
reference = forms.ChoiceField(choices=[(x) for x in references()])
views.py
def reference_view(request):
if request.method == "POST":
form = references(request.POST)
if form.is_valid():
form.save()
return redirect('index')
else:
form = references()
return render(request, 'proforma_select.html', {'form': form})
proforma_select.html
{% extends 'base.html' %}
{% block body %}
<div class="container">
<form method="POST">
<br>
{% for field in form %}
<div class="form-group row">
<label for="id_{{ field.name }}" class="col-2 col-form-label"> {{ field.label }}</label>
<div class="col-10">
{{ field }}
</div>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary" name="button">Add Order</button>
</form>
</div>
{% endblock %}
All I get when I render the template is each of the reference #s listed out but NOT within a dropdown. This leads me to believe my problem is mainly with the template, but I'm unsure as I am new to using Django.
Are you using Materialize CSS? If yes, then Django forms renders dropdowns differently from how Materialize expects them. So you will want to override the form widget. You can do something like so:
forms.py:
class DropDownMenuReferences(forms.Form):
reference = forms.ChoiceField(choices=[(x) for x in references()],
widget=forms.Select(choices=[(x) for x in references()], attrs={'class':
'browser-default'}))
This overrides the parameters passed into html. You can also pass any name tags in the attrs too.
The issue:
https://github.com/Dogfalo/materialize/issues/4904

How to dynamically delete object using django formset

Django says, i should render inline formset this way:
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.field_1 }}
{{ form.field_2 }}
<button type="button"> delete </button>
{% endfor %}
<button type="submit"> submit </button>
Ok. But what if i want to delete some formset objects (form) dynamically? User press delete button - i remove form from the DOM, i use ajax to remove object, related to the form from the DATABASE. It works ok till this point. But when user clicks submit - my views.py trying to validate formset:
filled_formset = OrderItemFormSet(request.POST, instance=order)
if filled_formset.is_valid():
and raises error:
MultiValueDictKeyError at /order/cart/
"'orderitem_set-0-id'"
...\market\ordersys\views.py in show_cart
59. if filled_formset.is_valid():
I think it happend because form objects were displayed by django with some regularity (first form got id = orderitem_set-0-id, second = orderitem_set-1-id etc.) And when i deleted first form from the DOM, the regularity was broken - there is no more form with orderitem_set-0-id. But is_valid() still trying to get it dict["orderitem_set-0-id"].
I could use some black magic, substituting django's technical information, displayed in the DOM, restoring disrupted regularity, but is there a better way?
Could you tell me how to properly dynamically delete formset items, please ?
I got no answer for some time, so i did not find any better solution than below. Maybe someome will find it useful.
Ok, the trick is - to have {{form.DELETE}} for any form in your formset in template. It renders as a checkbox (i made it invisible) and i made JS to make it "checked" whenever user press "delete" button. After user press "submit" button, every form, marked for deletion, will NOT be validated by the view during filled_formset.is_valid(). This lets you delete object from database with ajax behind the scene.
The problem was that an ERROR was raised during formset validation. Caused by the form of an object, which was already deleted from database with ajax.
So there are all components:
views.py
def show_cart(request):
OrderItemFormSet = inlineformset_factory(Order, OrderItem, form=OrderItemForm, extra=0, can_delete=True)
order = Order.objects.get(pk=request.session['order'])
if request.method == 'GET':
formset = OrderItemFormSet(instance=order)
return render(request, 'ordersys/cart.html', {'formset': formset})
elif request.method == 'POST':
filled_formset = OrderItemFormSet(request.POST, instance=order)
if filled_formset.is_valid():
filled_formset.save()
return redirect('catalog:index')
else:
return render(request, 'ordersys/cart.html', {'formset': filled_formset})
cart.html
<form action="" method="post">
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
{{ form.DELETE|add_class:"not_displayed" }} # custom filter
<img src="{{ form.instance.spec_prod.product.picture.url }}">
{{ form.quantity.label_tag }}
{{ form.quantity }}
{{ form.errors }}
<button type="button">Delete</button>
{% endfor %}
<button type="submit">Submit</button>
</form>
Next, if user press 'DELETE' button, my JavaScript
1. hides form with $(item).css('display', 'none');
2. makes checked form.DELETE checkbox with ItemDelCheckbox.prop('checked', true);
3. sends ajax request to delete item from database (otherwise if user refreshes the page, the item still in the cart)
views.py
def delete_order_item(request): # meets the ajax request
item_id = int(request.POST['item_id'])
order = get_object_or_404(Order, pk=int(request.POST['order_id']))
order.remove_item(item_id)
if order.is_empty(): # if the last item is deleted
order.untie(request.session)
order.delete()
return HttpResponse()
Instead of manually creating the hidden field using {{ form.DELETE }}, you can probably use 'can_delete' while instantiating the formset which does the same. For eg,
ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
Refer can_delete

D3.js Directed Graph Django Integration

I have a simple Django application that queries a database and displays the fields of the objects returned. I was wondering what would be the best way to go about integrating this directed graph visualisation into my project; I've never worked with d3 until now.
Allow me to explain my application. At the moment, it's simply a form that allows users to query contents of the database regarding information on books, by entering the unique ID of a book. This works fine, the contents are displayed through the use of a template.
What I wish to do is used one of the fields of the queried objects to push data into the graph example above, and simply display this graph in a new window when a text-link is pressed.
Here's my application structure:
Code
myapp.models.py:
from django.db import models
class Book(models.Model):
uid = models.IntegerField(primary_key=True)
title = models.CharField(max_length=30)
related_books = models.CharField(max_length=2048)
class Meta:
db_table = u'books'
The field related_books contains the data I wish to graph. The data is stored in the format rel_book1 rel_book2 rel_book3 ... where each book is related to the queried Book object, there are n related books for each object, but there is at least always two.
myproject.templates.search.html:
<form action="" method="get">
<label for="id_q">Search:</label>
<input id="id_q" name="q" type="text">
<input type="submit" value="Submit">
{% if found_entries %}
<ul>
{% for i in found_entries %}
{{ i.uid }} {{ i.title }} {{ value|linebreaks }}
{% endfor %}
</ul>
{% endif %}
</form>
So this is where I'd like to display the graph; in a new window when a text-link is pressed.
myapp.views.py:
from django.shortcuts import render_to_response
from django.template import RequestContext
from myapp.search import get_query
from myapp.models import Book
def search(request):
query_string = ''
found_entries = None
if ('q' in request.GET) and request.GET['q']:
query_string = request.GET['q']
found_entries = Book.objects.filter(uid=query_string)
return render_to_response('search.html',
{ 'query_string': query_string, 'found_entries': found_entries },
context_instance=RequestContext(request))
So just to reiterate, a user enter a unique ID for a given book and contents of the database related to that book are displayed. At the moment the ID and title are displayed, I'd also like to display a link to the directed graph example.
So my question is, how would I go about extracting the information out of the field related_books when the Book model is queried, so that I can push this data into the graph example to generate graphs for each Book object generated?
Thanks for bearing with me!
It seems to me you already have enough information. Your view returns book objects that match the filter, and related_books is a member of the Book class.
You'd simply need to generate a link on the fly by iterating over the related_books attribute of each book, e.g.
{% for book in found_entries %}
# print out whatever results here
<a href="graphing_page.html?contents=
{% for content in book.related_books %}content{% endfor %}
">Graph this</a>
{% endfor %}
I'm not sure what parameters the d3 library takes, but a list of connected components seems reasonable.

Categories