(Django - Python) Hidden Input Request to views.py - python

I have to create a site with auctions and I have an homepage which shows all the auctions active...
I want to redirect users to auctions details clicking on relative button but I have some problems with the hidden input request because it doesn't report the hidden value to my function ( def bid (request, auction) ) but I see it on the url bar after the csrfmiddlewaretoken (id=1), can you help me? (I have tried also with POST request...)
These are my codes:
views.py
def home(request):
auctions = Auction.objects.filter(status=True)
form = detailForm(request.GET or None)
if request.method == "GET":
id = request.GET.get("id")
if form.is_valid():
[bid(request,auction) for auction in auctions if auction.id==id]
else:
form = detailForm()
return render(request, "index.html", {"auctions":auctions, "form":form})
def bid(request, auction):
user = request.user
form = bidForm(request.POST)
if request.method == "POST":
bid = request.POST.get("endPrice")
if form.is_valid():
if bid > auction.startPrice:
auctionUpdate=form.save(commit=False)
auctionUpdate.endPrice=bid
auctionUpdate.winner=user
auctionUpdate.save()
else:
messages.warning(request, "Devi puntare una cifra superiore a quella vincente!")
else:
form = bidForm()
return render(request, "bid.html", {"form":form})
forms.py
class detailForm(forms.ModelForm):
class Meta:
model = Auction
fields = ("id",)
index.html
{% for auction in auctions %}
<--! I put all the informations about auctions -->
<form method="GET">
{% csrf_token %}
<input type="hidden" name="id" value={{auction.id}}>
<input type="submit">
{% endfor %}
</form>
Thanks to everyone!

Not sure if I understand your question correctly, so basically we have a list of auctions, and when a user clicks on a related button of an auction, the user will be redirected to another page.
In this model, you need to think about 2 views and 2 templates to handle them.
A ListView to list all your actions and a DetailView to handle the detail page of each auction. So you will have a home.html for your ListView and a bid.html for your auction detail.
I think the form submission should be implemented in your detail view (in your code the bid function), and the detail view should render a page with the bid.html template.
In your home.html you may want to just leave a link of each auction like:
{% for auction in auctions %}
<a type='button' href="{{ auction.get_absolute_url }}">{{ auction.name }}</a>
{% endfor %}
You need to add this get_absolute_url method in your Auction model, for example:
# models.py
def get_absolute_url(self):
return reverse("auction-detail", kwargs={"pk": self.pk})
Also in your routing:
# urls.py
urlpatterns = [
path('/', home, name='home'),
path('/auctions/<int:pk>/', bid, name='auction-detail'),
]
You need to pass auction to your context variable in your bid function as well, in order to refer to it in your template.
def bid (request, auction_id):
auction = Auction.objects.get_object_or_404(id=auction_id)
# ...
context = {'form': form, 'auction': auction}
return render(request, "bid.html", context)
And finally in your detail view template (bid.html), use the form to submit:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="hidden" name="id" value={{auction.id}}>
<input type="submit">
</form>
Another suggestion is that try to use Class Based View such as ListView and DetailView in this case, which will handler your problem much easier. Check this article for more detail.

Related

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.

Django: ModelForm and show data on the same url

I'm new to django and trying to create my first app and I think I might need some little help :)
I have a ModelForm on a site to submit and want to show the data on the same page. I'm having trouble to set up two functions on the same page, I think i might have to use a class and set it in urls.py but I'm not able to make it work :( the code looks like this:
forms.py:
from django import forms
from .models import Eintrag
class NameForm(forms.ModelForm):
class Meta:
model = Eintrag
fields = ['Anmeldung', 'Essen']
urls.py
from django.urls import path
from . import views
app_name = 'form'
urlpatterns = [
path('', views.get_name, name='form'),
]
views.py
from django.shortcuts import render
from django.utils import timezone
from django.contrib.auth.decorators import login_required
from .forms import NameForm
from .models import Eintrag
#login_required()
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
eintrag = form.save(commit=False)
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
eintrag.Name = request.user # Set the user object here
eintrag.pub_date = timezone.now() # Set the user object here
eintrag.save()
return render(request, 'form/name.html', {'form': form})
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'form/name.html', {'form': form})
def post_list(request):
posts = Eintrag.objects.all()
return render('form/post_list.html', {'posts': posts})
name.html
...
{% include "form/post_list.html" %}
<form action="/form/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
...
post_list.html
{% for post in posts %}
{{ post }}
{% endfor %}
So the problem is in urls.py only get_name is handled and I'm clueless how I should include post_list. I rather not want to use different url's, do I have to?
Thanks for any help and advice!
You don't need a separate URL or view for the list. Just include the queryset in the context of your get_name view.
posts = Eintrag.objects.all()
return render(request, 'form/name.html', {'form': form, 'posts': posts})
with [Class Based View] it would be better.
But with your view, you can send multiple data via context.
#login_required()
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
''' codes '''
eintrag.save()
return HttpResponseRedirect(request.path) # generate an empty form
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
posts = Eintrag.objects.all() # the queryset is here, and sent via context
return render(request, 'form/name.html', {'form': form,'posts':posts})
I your html remain the same, but keep your form action='' empty
{% include "form/post_list.html" %}
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>

Can't display the data entered in form in database

The user enters the data in the form. But the data entered in the form doesn't get displayed in the Database.
views.py
def add(request):
if request.method=='POST':
form=FilesCreate(request.POST)
if form.is_valid():
form.save()
return render(request,'plagiarism/page1.html',{'form':FilesCreate()})
def add2(request):
if request.method=='POST':
form2=FilesCreate2(request.POST)
if form2.is_valid():
form2.save()
return render(request,'plagiarism/page2.html',{'form':FilesCreate2})
models.py
from django.db import models
class File1(models.Model):
#user=models.ForeignKey(User)
firstfile=models.CharField(max_length=1000, default="")
#secondfile=models.CharField(max_length=1000)
def __str__(self):
return self.firstfile
plagiarism/page1.html
<h1>Enter your first file</h1>
<form action="file2/" method="post">
{% csrf_token %}
{% for field in form %}
{{field}}
<input type="submit" value="Submit file1"/>
{% endfor %}
</form>
plagiarism/page2.html (displays page after clicking submit in page 1)
<h1>Enter your second file</h1>
<form action="plagiarism/file2/result/" method="post">
{% csrf_token %}
{% for field in form %}
{{field}}
<input type="submit" value="Get Results"/>
{% endfor %}
</form>
{% block h1 %}
{% endblock %}
<body>
plagiarism/page3.html (displays page after clicking submit in page 2)
<h1> Here is your Result </h1>
<h2>
{{data}}
</h2>
</body>
forms.py
from django.forms import ModelForm
from django import forms
from plagiarism.models import File1,File2
class FilesCreate(ModelForm):
class Meta:
model=File1
exclude=()
widgets={'firstfile':forms.Textarea(attrs={'cols':50,'rows':100})}
example.py
from django.shortcuts import render
def getresult(request):
data=95.5
return render(request,'plagiarism/page3.html',{'data': data})
urls.py
from django.conf.urls import url
from . import views
from . import example3
urlpatterns=[
url(r'^$',views.add,name='add'),
url(r'file2/$',views.add2,name='add2'),
url(r'file2/result/$',example3.getresult,name='getresult')
]
You seem to want a kind of wizard, where you process a form and it redirects you to the next, but you're not doing the basics of form processing well. For simple form handling, you can do this:
urls.py
from django.conf.urls import url
from . import views
from . import example3
urlpatterns=[
url(r'^$',views.add,name='add'),
url(r'file2/result/$', example3.getresult, name='getresult')
]
In the template, you are calling file2 with the form's action, but you really want to call the same page, to process the form with the add view:
plagiarism/page1.html
<h1>Enter your first file</h1>
<form method="post">
{% csrf_token %}
{% for field in form %}
{{field}}
{% endfor %}
<input type="submit" value="Submit file1"/>
</form>
Note the missing action attribute in the <form> element.
When you visit the root of the website, the add view will be called with a GET request. When you submit the form, the same add view will be called, with a POST request, which will then be processed:
views.py
def add(request):
if request.method == 'POST':
form = FilesCreate(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('getresult'))
else:
form = FilesCreate()
return render(request,'plagiarism/page1.html',{'form': form})
Note the HttpResponseRedirect, which redirects to a new page on success, and the else, which creates an empty form for the first time you visit the page (i.e. request.method is not POST, it is GET). This way, if the form isn't valid, the last line will render it bound to the data that was submitted and display the errors.
This should get you the data into the database, which was your first goal. If you want to go to another form upon submission, you can redirect there (instead of the result page) and do the same as above in the view add2.
There used to be a Django Form Wizard, but you can see this project to do multi-step forms.

Display dynamic data from database in Django

I am using Django and Postgresql as database. I have a HTML page with two fields name and item. I can save the data in the database by clicking on submit. But, I want to show the saved data from database in the HTML page. It means, whenever we load the page, it should show the existing saved data and after submitting new data, the list should be updated. Below is my python code.
models.py
from django.contrib.auth.models import User
from django.db import models
class AllocationPlan(models.Model):
name = models.CharField(max_length=50)
item = models.CharField(max_length=4096)
views.py
class HomePageView(TemplateView):
template_name = "index.html"
def post(self, request, **kwargs):
if request.method == 'POST':
form = AllocationPlanForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'index.html', { 'form': AllocationPlanForm() })
forms.py
from django import forms
from django.forms import ModelForm
from homeapp.models import AllocationPlan
class AllocationPlanForm(ModelForm):
class Meta:
model = AllocationPlan
fields = "__all__"
index.html
<html>
<form method="post">{% csrf_token %}
Name:<br>
<input type="text" name="name" >
<br>
Item:<br>
<input type="text" name="item" >
<br><br>
<input type="submit" value="Submit"/>
</form>
{% for i in form %}
{{ i.name }}
{{ i.item }}
{% endfor %}
</html>
It is returning NONE
Forms in Django are not used to display lists of data. It's merely used to render / validate the form (<form> tag in html). See also the forms doc.
Furthermore, it seems like you're using the TemplateView incorrectly. The post method in your view is only called on a POST request. When you're just viewing the page normally, the template is rendered normally, but since you only add the data to the template in a POST request, the template does not receive a form parameter when loading the view normally (therefore defaulting to None).
According to the TemplateView documentation, you can add the context like so:
class HomePageView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
# Get the allocation plans from database. Limit to last 10. Adjust to your own needs
context['plans'] = AllocationPlan.objects.all()[:10]
context['form'] = AllocationPlanForm()
return context
def post(self, request, **kwargs):
form = AllocationPlanForm(request.POST)
if form.is_valid():
form.save()
# Handle rest of request here (for example, return the updated page).
As you can see, there is no need to check if request.method == 'POST' in your post method, because Django only calls this method on POST requests. See also dispatch in the docs
To render the data from your database you can now access them in your template as plans:
{% for plan in plans %}
{{ plan.name }}
{{ plan.item }}
{% endfor %}
In your HTML, there is also no need to manually create the form content:
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
This will automatically create the HTML necessary for the form.

django user creation form with auth.user clean method

I have created a form to add users in my front-end but the form does not validate duplicated username.I am using auth.user model.
This is my code:
views.py
from django.contrib.auth.models import User, Group
#login_required(login_url='/login/')
#permission_required('auth.add_user',raise_exception=True)
def user_new(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(user.password)
user.save()
return redirect('userdetail', user.id)
else:
form = NewUserForm()
return render(request, 'ace/user_edit.html', {'form': form})
forms.py
class NewUserForm(forms.ModelForm):
class Meta:
model = User
fields = ['username','first_name','last_name','password','email','is_active','is_staff','groups']
widgets = {
'username':TextInput(attrs={'class': u'form-control'}),
'first_name':TextInput(attrs={'class': u'form-control'}),
'last_name':TextInput(attrs={'class': u'form-control'}),
'password':PasswordInput(attrs={'class': u'form-control'}),
'email':EmailInput(attrs={'class': u'form-control'}),
'is_active':NullBooleanSelect(attrs={'class': u'form-control'}),
'is_staff':NullBooleanSelect(attrs={'class': u'form-control'}),
'groups':SelectMultiple(attrs={'class': u'form-control'}),
}
def clean_username(self):
username = self.cleaned_data['username']
user_exists = User.objects.get(username=username)
if user_exists:
raise ValidationError("User exists")
template
...
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form method="POST" class="service-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-info">Salvar</button>
<a href="{% url 'userlist' %}">
<button class="btn btn-danger" type="button">Cancelar</button>
</a>
</form>
...
When I create a new user OK, but when a try create a user that same username of other I get a error:
The view ace.views.user_new didn't return an HttpResponse object. It
returned None instead.
If I add a print line "print form.errors" in view i get in console:
usernameUser
exists
Your view does not have an else statement for if, form is not valid it should render the template with form errors.
You need to change your view like this,
def user_new(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.set_password(user.password)
user.save()
return redirect('userdetail', user.id)
else:
return render(request, 'ace/user_edit.html', {'form': form})
else:
form = NewUserForm()
return render(request, 'ace/user_edit.html', {'form': form})
And also you need to add the tag {%for field in form%} {{field.error}}{%endfor%} along with the form fields and labels.
You need to make sure that your view returns a response for POST requests when the form is invalid. You can do this by moving the final return render() statement out of the else block.
def user_new(request):
if request.method == "POST":
form = NewUserForm(request.POST)
if form.is_valid():
...
return redirect('userdetail', user.id)
else:
form = NewUserForm()
return render(request, 'ace/user_edit.html', {'form': form})
For registration django.contrib.auth User needs the username field to be unique. If you want to use other model field as unique (as unique registration field) and not the username, for example the email field, you can use this approach or use other registration bakends like django registration or django registration redux.
Instead of fixing the bug in your code I suggest to not invent the wheel and use excellent django-allauth package. It handles user login, logout, change password, registration and social sign in. I always start new projects from adding django-allauth - it handles all authentication problems with no effort.
You can use the saved time and effort to write actual application code instead of solving trivial user management details.
Also, the proper way to check for existence of the model instance is this:
user_exists = User.objects.filter(username=username).exists()
if user_exists:
raise ValidationError("User exists")

Categories