I'm building an app where the user enters data and then gets redirected to a page that shows results based on their input with some simple equations. However, every time I refresh the results page, a new model instance is saved on the database.
Is there another (more efficient and effective) way of passing the data from this view to another view where I have access to the instance of that model submitted through the form view? What's the Django way of passing form data to a view?
The only limitation is I don't want user authentication so using self.request.user is not an option unless it can be implemented in a way that doesn't require users to sign up and sign in.
I'm still somewhat new to Django so any pointers to obvious solutions that I'm overlooking would be greatly appreciated.
This is the view that processes the model form:
def createcalculation(request):
form = CalcForm()
if request.method == 'POST':
form = CalcForm(request.POST)
if form.is_valid():
item = form.save()
m_data = get_object_or_404(Calculate, id=item.id)
context = {'c_data': form.cleaned_data, 'm_data': m_data}
return render(request, 'calc/res_ca.html', context)
context = {'c_form': form}
return render(request, 'calc/calc.html', context)
It is advisable to always do a redirect after a successful POST. Your code should look something like this:
from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse
...
def createcalculation(request):
form = CalcForm()
if request.method == 'POST':
form = CalcForm(request.POST)
if form.is_valid():
item = form.save()
m_data = get_object_or_404(Calculate, id=item.id)
context = {
'c_data': form.cleaned_data,
'm_data': m_data
}
return redirect(reverse('app_name:view_name', kwargs=context))
context = {'c_form': form}
return render(request, 'calc/calc.html', context)
You can pass the newly created item object in the context as well. Also, you should change app_name and view_name text to match your situation.
Related
So I'm trying to make a calculator of sorts where the user enters some data and then they are presented with a view that gives them the results they need (printer-friendly format so they can print it).
What I've done so far:
Created a model and a form which they work as intended.
**class CalcModel**(models.Model):
income = models.DecimalField...
civil_status = models.CharField...
have_children = models.CharField..
**class CalcForm**(ModelForm):
class Meta:
**model = Calculate**
fields = ['income', 'civil...]
The view that processes the form and redirects to another view if submitted data is valid:
data_d = {}
def createcalculation(request):
form = CalcForm()
if request.method == 'POST':
form = CalcForm(request.POST)
if form.is_valid():
**data_d['cleaned_data'] = form.cleaned_data**
form.save()
return redirect('res-view')
context = {'c_form': form}
return render(request, 'calc/calc.html', context)
I think there should be a way to pass the model instance data to the view where the user is redirected but I can't figure it out. So, help is highly appreciated. Right now, I'm 'manually' passing a dictionary containing the data from the form but it doesn't work:
def res_ca(request):
context = data_d
return render(request, 'calc/res_ca.html', context)
I can't seem to figure out how to pass the data for the current session to the res_ca view.
The urls if that helps:
path('calc', createcalculation, name='calculate'),
path('res/', res_ca, name='res-view'),
As suggested by #vinkomlacic, I found a way to include the id of the model instance by switching to the render method instead of redirect and it worked like a charm.
if form.is_valid():
messages.success(request, f'Successful!')
item = form.save()
m_data = Calculate.objects.get(id=item.id)
context = {'c_data': form.cleaned_data, 'm_data': m_data}
return render(request, 'calc/res_ca.html', context)
This way, the form is saved, I can pass that instance of the model to the next view and it also allows me to add additional context to the template directly from model methods.
I have a home in which I have a form that I get some info from students to suggest them some programs to apply to. The home view is as below:
def home(request):
template_name = 'home.html'
home_context = {}
if request.POST:
my_form = MyModelForm(request.POST)
if my_form.is_valid():
# do some stuff
return programs(request)
else:
my_form = MyModelForm()
home_context.update({'my_form': my_form, })
return render(request, template_name, home_context)
In the second view, I have the same form and I want this form to be pre-occupied with the information I entered in the home page. That is why in the above, I passed my POST request to programs view that is as below:
def programs(request):
template_name = 'programs.html'
programs_context = {}
if request.POST:
my_form = MyModelForm(request.POST)
if my_form.is_valid():
# do some other stuff
else:
my_form = MyModelForm()
programs_context.update({'my_form': my_form, })
return render(request, template_name, programs_context)
The drawback of this strategy (passing the POST request in the home view to the programs_view) is that the url in url bar does not change to 'example.com/programs' and stays as 'example.com' . I will have some problems including problems in pagination of the programs.
The alternative is that I do this:
def home(request):
template_name = 'home.html'
home_context = {}
if request.POST:
my_form = MyModelForm(request.POST)
if my_form.is_valid():
# do some stuff
return redirct(programs)
else:
my_form = MyModelForm()
home_context.update({'my_form': my_form, })
return render(request, template_name, home_context)
The problem with this altenative strategy is that I do not see my_form in programs view pre-occupied with the data submitted in the home page.
I want to get a mixed result. Have the form pre-occupied and see the url changed to 'example.com/programs'.
Note: I have not shown what is written in the url patterns and in the template and I expect these are implied.
What I expect is a solution without adding info to sessions. I just want to pass POST request to programs view.
If there is no way without adding data to the session, the best method of adding data to session is appreciated.
Please note that when push the form submit button in the home page, I want to be referred to request.POST of the programs view. This is a positive point of alternative 1 that I am currently using.
In the first method, If I enter 'example.com/programs', the get part of the programs view is triggered and if I submit the form on the home page, the get part of the home view and then the POST part of the programs view is triggered. This is the behavior I want.
If you search more for that issue, you'll find that either redirect of POST request or passing variables/context via redirect is not possible.
Either your proposed solution with storing POST data in the request.session is not optimal.
You can return redirect(reverse('urlname') + query_string) with adding GET querystring to the url and prefill the form from that querystring in the programs view. It would be much cleaner solution to this.
I am trying to create a simple one item product store, in which customers would go to a product page and choose the quantity they would like to purchase in a form. After completing the form, I would then like for it to redirect to the checkout page and render the quantity they chose. Is there a simple way to do this? At the moment, I am posting the form data to the product page url and then redirecting the user to the checkout page, however I am unsure how to access that data.
def proxy_detail(request, proxy_slug):
if request.method == 'POST':
form = forms.AddProxyAmountForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
number_of_proxies = int(cd['number_of_proxies'])
return redirect('payment:checkout')
else:
add_proxy_form = forms.AddProxyAmountForm()
proxy_product = get_object_or_404(models.ProxyProduct, slug = proxy_slug)
return render(request, 'shop/product/proxy_detail.html', {'proxy_product' : proxy_product, 'add_proxy_form' : add_proxy_form })
Its not an exact answer but I am new here so I can't really comment :p
Anyways, I think in your view, what you can do is, get the data in a variable and pass it as a context to render another page.
def viewname(request):
if request.method == "POST":
#your logic to save here
context = {variableOfInterest : request.POST.valueOfInterest} #considering you are getting it from the form
return render(request, "redirected.html", context)
I am trying to use same view for creating form and updating any object.
My code is as below, I tried in many ways nothing is working, since I am excluding the shof from form and adding it after form.is_valid() it makes lot of confusion. If I update it creates new object. I have two urls one without ql (create new) and one with ql (update existing), I have a class vdview which provides v.shof which needs to applied in the f.shop in form. please help fix this,
#csrf_protect
#login_required
def addmenu(request, qs, ql=None):
v = vdview(request, qs)
ctgobj = get_object_or_404(v.shopcategs, pk=ql) if ql else None # ctgobj = ShopCtg(shop=v.shof)
if ql:
form = ShopCtgForm(instance=ctgobj) # Tried ShopCtgForm(instance=ctgobj, data=request.POST)
else:
form = ShopCtgForm(data= request.POST)
if request.method == 'POST':
if form.is_valid():
f=form.save(commit=False)
f.shop = v.shof
f.save()
#form.save_m2m()
return redirect('vendor-shop', qs) #thing='%s added' %f.name)
else:
pass
#else:
# form = ShopCtgForm()
return render(request,'vendorshop.html', {'shop':v.shof, 'shopcategs':v.shopcategs, 'form': form,
'heading':'Create New Category', 'createcateg': 'createcateg', 'pkaddmenupk':'y' } )
Use try blocks to handle both scenarios. The simplified example below will look for a given model instance pk and if it doesn't find it, will assume you want to create it. try will prevent django from throwing an error if the model instance doesn't exist. Rather, it will just return the empty model form.
It does this first to render the correct form in the template (the first try block) then again in the second try block after request.method == 'POST': to submit new data or update existing data.
Views.py
from .models import Books
from .forms import BookForm
def create_and_update_book_view(request, pk):
books = Books.objects.get(id=pk)
try: # get pre-populated form with model instance data (for update)
form = BookForm(instance=books.id)
except: # If it doesn't exist, show an empty form (for create)
form = BookForm(request.POST or None)
if request.method == 'POST':
try: # Do the same as above
form = BookForm(instance=books.id)
except: # Same as above
form = BookForm(request.POST or None)
if form.is_valid():
form.save()
return render(request, "create_and_update_book_page.html", {'form':form})
I am storing some form values, which is a list filter, from a post method in the request.session in order to use it in another view function to render the filtered results. The problem is any user that I log in keep the results stored, if they access the results page directly they will see other users filter results.
I use pagination (digg without AJAX), I am using django-el-pagination.
the views.py
def search(request):
if request.method == 'POST':
form = ComprarBuscaForm(request.POST)
if form.is_valid():
cidade = form.cleaned_data['cidade']
request.session['cidade'] = form.cleaned_data['cidade']
request.session['quartos'] = form.cleaned_data['quartos']
request.session['tipo_imovel'] = form.cleaned_data['tipo_imovel']
request.session['preco_minimo'] = form.cleaned_data['preco_minimo']
request.session['preco_maximo'] = form.cleaned_data['preco_maximo']
request.session['area_minima'] = form.cleaned_data['area_minima']
request.session['area_maxima'] = form.cleaned_data['area_maxima']
return HttpResponseRedirect(reverse('imoveis:resultado_busca'))
else:
form = ComprarBuscaForm()
return render (request, 'imoveis/busca_comprar.html', {'form':form})
def search_result(request):
anuncios = Anuncio.objects.filter(quartos=request.session['quartos'],
cidade=request.session['cidade'],
tipo_imovel=request.session['tipo_imovel'],
preco_venda__gte=request.session['preco_minimo'],
preco_venda__lte=request.session['preco_maximo'],
area_construida__gte=request.session['area_minima'],
area_construida__lte=request.session['area_maxima'],
tipo_anuncio='Venda')
return render(request, 'imoveis/resultado_busca_comprar.html', {'anuncios': anuncios})
Everything is working fine although the fact I mentioned before. I am wondering if what I am doing is the right approach for this kind of situation.
Is it really necessary to use two views for this? I would filter and render in the same form view if I were you.
def search(request):
if request.method == 'POST':
form = ComprarBuscaForm(request.POST)
if form.is_valid():
anuncios = Anuncio.objects.filter(quartos=request.session['quartos'],
cidade=request.session['cidade'],
tipo_imovel=form.cleaned_data['tipo_imovel'],
preco_venda__gte=form.cleaned_data['preco_minimo'],
preco_venda__lte=form.cleaned_data['preco_maximo'],
area_construida__gte=form.cleaned_data['area_minima'],
area_construida__lte=form.cleaned_data['area_maxima'],
tipo_anuncio='Venda')
return render(request, 'imoveis/resultado_busca_comprar.html', {'anuncios': anuncios})
else:
form = ComprarBuscaForm()
return render (request, 'imoveis/busca_comprar.html', {'form':form})