Trying to pass form from generic.FormView to generic.DetailView - python

I'm new in Django and it's just a test for further model. I'm trying to pass a form from generic.FormView to generic.DetailView, and exhibit the datas inserted in the previous HTML (associated with the FormView) to another HTML (associated with the DetailView). I've thought it probably a problem with the link between the view.py and urls.py. The codes are:
views.py:
class IndexView(generic.FormView):
template_name = 'dutos/index.html'
form_class = GetDate
success_url = 'dutos/detail.html'
#def form_valid(self, form):
#return HttpResponse(self.success_url)
#return super.form_valid(form)
class DetailView(generic.DetailView):
model = Dutos
template_name = 'dutos/detail.html'
forms.py
class GetDate(forms.Form):
dateDataInicial = forms.DateTimeField(label='dataInicial', initial=datetime.now().strftime("%d/%m/%Y %H:%M:%S"), required=False)
dateDataFinal = forms.DateTimeField(label='dataFinal', initial=datetime.now().strftime("%d/%m/%Y %H:%M:%S"), required=False)
urls.py:
from django.urls import path
from .views import views
urlpatterns = [
path('', views.IndexView.as_view(), name="index"),
path('detail/', views.DetailView.as_view(), name="detail"),
]
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Template</title>
</head>
<body>
<h1>Teste HTML</h1>
<div class="container-fluid" id="wrapper">
<div class="row">
<form action="/detail/" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<div>
<div class="fieldWrapper">
{{ form.dateDataInicial.errors }}
<label for="{{ form.dateDataInicial }} Data Inicial: </label>
{{ form.dateDataInicial }}
</div>
<div class="fieldWrapper">
{{ form.dateDataFinal.errors }}
<label for="{{ form.dateDataFinal }} Data Final: </label>
{{ form.dateDataFinal }}
</div>
<input type="submit" value="Pesquisar">
</div>
</form>
</div>
</div>
</body>
</html>
detail.html:
<!DOCTYPE html>
<html>
<head>
<title>Template</title>
</head>
<body>
<h1>Template HTML</h1>
{{form.cleaned_data['dateDataInicial']}}
{{form.cleaned_data['dateDataFinal']}}
</body>
</html>
I've already change the "{{form.cleaned_data['dateDataInicial']}}" to "form.dateDataInicial", or just cleaned everything and put "Test" to exhibit a simples HTML. I'm getting HTTP Error 405.

There's really quite a lot wrong with the code you've posted.
You're getting 405 because the form in your index template attempts to post directly to the detail URL, which is not set up to accept POST requests. An additional problem with doing this is that the form will not be validated, because the validation is supposed to happen in the IndexView, not the DetailView. Plus, the DetailView does not know anything about the form, so form.cleaned_data would not exist in the template context; and, Django template syntax does not support dictionary lookup via square brackets, so {{ form.cleaned_data['dateDataInicial'] }} would not work.
In order for validation to work you need to submit the form back to IndexView, which will then redirect to the detail view. (Another issue with your code is that success_url should be a URL, not a template path.) The problem then becomes how to get the data from one view to the other; the session is a good way to do that. So:
class IndexView(generic.FormView):
template_name = 'dutos/index.html'
form_class = GetDate
success_url = reverse_lazy('detail')
def form_valid(self, form):
self.request.session['dates'] = form.cleaned_data
return super.form_valid(form)
change the form tag in index.html:
<form action="" method="post">
the detail view:
class DetailView(generic.DetailView):
model = Dutos
template_name = 'dutos/detail.html'
def get_context_data(self, **kwargs):
kwargs['dates'] = self.request.session.pop('dates', {})
return super().get_context_data(**kwargs)
and in detail.html:
{{ dates.dateDataInicial }}
{{ dates.dateDataFinal }}

Thanks to the help of Daniel and his code, I figured out the modifications that lead to my needs. I've changed the genericView of DetailView from "generic.DetailView" to "generic.ListView". When it was made another problem appeared. To pass "datetime" through JSON, it convert to string format and all the date was being treated as string, so I lost the possibility to show the two dates in separated fields in detail.html. To overcome this problem, I adapted the solution proposed to Daniel, and divided the field in two so I can get it by form.cleaned_data and converted it into string, and now I don't need to worry about JSON serializing a "datetime".
views.py:
class IndexView(generic.FormView):
template_name = 'dutos/index.html'
form_class = GetDate
success_url = reverse_lazy('detail')
def form_valid(self, form):
#self.request.session['dates'] = json.dumps(form.cleaned_data, cls=DjangoJSONEncoder)
self.request.session['dateInicial'] = str(form.cleaned_data['dateDataInicial'])
self.request.session['dateFinal'] = str(form.cleaned_data['dateDataFinal'])
return super().form_valid(form)
class DetailView(generic.ListView):
model = Dutos
template_name = 'dutos/detail.html'
def get_context_data(self, **kwargs):
#kwargs['dates'] = self.request.session.pop('dates', {})
kwargs['dateInicial'] = self.request.session.pop('dateInicial', {})
kwargs['dateFinal'] = self.request.session.pop('dateFinal', {})
return super().get_context_data(**kwargs)
index.html:
<div class="container-fluid" id="wrapper">
<div class="row">
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<div>
<div class="fieldWrapper">
{{ form.dateDataInicial.errors }}
<label for="{{ form.dateDataInicial }} Data Inicial: </label>
{{ form.dateDataInicial }}
</div>
<div class="fieldWrapper">
{{ form.dateDataFinal.errors }}
<label for="{{ form.dateDataFinal }} Data Final: </label>
{{ form.dateDataFinal }}
</div>
<input type="submit" value="Pesquisar">
</div>
</form>
</div>
</div>
detail.html:
<body>
<h1>Detalhes</h1>
{{ dateFinal }}
<br>
{{ dateInicial }}
</body>

Related

Redirected to same page after POST method django

I am creating this project in django. I am working on reporting films, where I go to a film-detail view, then hit the report button. A report_form shows up where I justify why I am reporting, and then I click report. Everything works fine, but there is one thing. After reporting I get sent back to a random(?) film-detail view, but I would like to go back to the view for the film I am reporting. But how???
views.py
class FilmReportView(LoginRequiredMixin, CreateView):
model = Report
fields = ['reason']
def form_valid(self, form):
form.instance.reporter = self.request.user
form.instance.reports_id = self.kwargs['pk']
return super().form_valid(form)
def get_success_url(self):
return "film/<int:pk>/report"
report_form.html
{% extends "board/base.html" %}
{% load crispy_forms_tags %}
{% load materializecss %}
{% block content %}
<div class="valign-wrapper row login-box">
<div class="col card hoverable s10 pull-s1 m6 pull-m3 l8 pull-l2">
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
<div class="card-content">
<span class="card-title">Jusity why you want to report this film?</span>
<div class="row">
{{ form|materializecss }}
</div>
</div>
<div class="card-action right-align">
<input type="reset" id="reset" class="btn-flat grey-text waves-effect">
<input type="submit" class="btn green waves-effect waves-light" value="Report">
</div>
</form>
</div>
</div>
{% endblock content %}
urls.py
urlpatterns = [
path("", views.films_view, name="board-home"),
path("film/add", FilmAddView.as_view(), name="film-add"),
path("film/<int:pk>/", FilmDetailView.as_view(), name="film-detail"),
path("film/<int:pk>/report", FilmReportView.as_view(), name="film-report"),
]
models.py
class Report(models.Model):
reason = models.TextField()
reporter = models.ForeignKey(User, on_delete=models.CASCADE, related_name="Reporter")
reports = models.ForeignKey(Film, on_delete=models.CASCADE)
def __str__(self): # pragma: no cover
return f"{self.reports.title} reported by {self.reporter.username}"
def get_absolute_url(self): # pragma: no cover
return reverse("film-detail", kwargs={"pk": self.pk})
Fix your FilmReportView get_success_url() to look like that:
def get_success_url(self):
return reverse("film-detail", kwargs={"pk": self.object.reports.id})
That should take care of it
In your views.py or even in the template you can put next parameter with url you want:
form/action/url?next={{request.path}}
See more about next:
https://www.reddit.com/r/django/comments/32s9ag/how_do_i_set_the_next_context_variable_in_django/

Passing variable with POST in django

Hi im trying to pass a name from a form to a view in django using POST. There are no errors in the execution but its passing nothing from the template and dont know if i doing something wrong here. Im starting with django so i can have newbie errors. If u need more information tell me pls.
Views.py
def crear_pdf(request):
empresa_selec = ""
form = EmpModelForm()
if request.method == 'POST':
form = EmpModelForm(data=request.POST)
if form.is_valid():
empresa_selec = form.cleaned_data['nombre']
#"empresa_selec" that's the empty variable
Models.py
class Empresa_modelo(models.Model):
nombre = models.CharField(max_length=100,blank=True,null=True)
Forms.py
class EmpModelForm(forms.ModelForm):
class Meta:
model = Empresa_modelo
fields = ["nombre"]
template.html
<div class="container-fluid">
<form method="POST" enctype="multipart/form-data" action="{% url 'crear_pdf' %}">{% csrf_token %}
<p>Empresa</p>
<input type="text" name="empresa">
<br>
<button type="submit">Subir</button>
</form>
<br>
<a class="btn btn-primary" href="{% url 'crear_pdf' %}">Atras</a>
</div>
You haven't got a field called nombre in your template; you only have empresa.
That's presumably because you don't ouput your EmpModelForm in the template. You don't show your render call in the view, but assuming you pass it as form, you should just do {{ form.as_p }} in the template.
Try using:
<input type="text" name="nombre">
There is no field named empresa.
Had a look at your code,there are a couple of issues.First you are not using the model form defined in your forms.py file in your template. Second you have defined an input text box with the name that you are not referring in your views. Either use the model form or use the same name of your input text box in your views.
def crear_pdf(request):
empresa_selec = ""
form = EmpModelForm()
if request.method == 'POST':
form = EmpModelForm(data=request.POST)
if form.is_valid():
empresa_selec = form.cleaned_data['nombre']
else:
return render(request,"template.html",{"form":form})
And in your template you can edit as such:
<div class="container-fluid">
<form method="POST" enctype="multipart/form-data" action="{% url 'crear_pdf' %}">{% csrf_token %}
{{ form.as_p }}
<br>
<button type="submit">Subir</button>
</form>
<br>
<a class="btn btn-primary" href="{% url 'crear_pdf' %}">Atras</a>
</div>
Hope this helps.

Django - ValidationError does not display

I recently tried the forms validations and faced an issue with ValidationError().
The form error does not appear in my website when I submit the form.
Here is the code:
forms.py
class ArticleForm(forms.ModelForm):
def clean_titre(self):
titre = self.cleaned_data['titre']
if len(titre) < 5:
raise ValidationError('myError')
return titre
form = ArticleForm()
template.html
<div class="form-group">TITRE
{{ form.titre.errors }}
{{ form.titre }}
</div>
views.py
def AddArticle(request):
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
save_it = form.save(commit=False)
save_it.user = request.user
save_it.save()
form.save_m2m()
return HttpResponseRedirect('/')
What did I do wrong?
--- EDIT ---
Full template.html
<form class="form" action="{% url "article.views.AddArticle" %}" method="post" enctype='multipart/form-data'>
{% csrf_token %}
<div class="form-group">TITRE
{{ form.titre.errors }}
{{ form.titre }}
</div>
<div class="form-group">SUMMARY
{{ form.media }}
{{ form.summary.errors }}
{{ form.summary }}
</div>
<div class="form-group">CONTENU
{{ form.media }}
{{ form.contenu.errors }}
{{ form.contenu }}
</div>
<div class="form-group">
{{ form.image.errors }}
{{ form.image }}
</div>
<div class="form-group">TAGS
{{ form.tags.errors }}
{{ form.tags }}
</div>
<input type="submit" class="btn btn-default" value="Submit" autocomplete="off" autocorrect="off" />
</form>
I'll post the full forms.py too, it may help.
forms.py
class ArticleForm(forms.ModelForm):
def clean_titre(self):
titre = self.cleaned_data['titre']
if len(titre) < 5:
raise ValidationError('myError')
return titre
class Meta:
model = Article
exclude = ['date', 'rating', 'user']
widgets={
"titre":forms.TextInput(attrs={'placeholder':'Le titre', 'class':'form-control'}),
"contenu":forms.Textarea(attrs={'placeholder':'Le Contenu de votre message', 'class':'form-control'}),
"image":forms.FileInput(attrs={'placeholder':'Votre Image', 'id':'uploadBtn'}),
"tags":TagWidget(attrs={'placeholder':'Vos Tags', 'class':'form-control'}),
}
form = ArticleForm()
You are missing the else portion within your view. Here is the general flow of what forms usually do:
Users navigate to a page via GET which presents them with a form
Users fill in the form and submit it by using POST
If the form is valid, users are directed to a different page
If the form is not valid, users are presented with the same page as in step 1 with the validation errors displayed. After users correct them, they are process to step 2.
Here is that flow in django view:
def AddArticle(request):
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
save_it = form.save(commit=False)
save_it.user = request.user
save_it.save()
form.save_m2m()
return HttpResponseRedirect('/')
else:
form = ArticleForm()
return render(request, 'template.html', {'form': form'})
I would however look into using class based views in Django. Initially they can seem very confusing but over time you will learn to appreciate them. Docs. Another useful resource when learning CBV.
By using CBV, the above can be simplified to:
class AddArticleView(CreateView):
success_url = 'name_of_view_here'
form_class = ArticleForm
template_name = 'template.html'
# urls.py
urlpatterns = patterns('', url(r'^articles/add/$', AddArticleView.as_view()))
Template
You also need to include the overall form error in the template, in addition to each field errors:
<form class="form" action="{% url "article.views.AddArticle" %}" method="post" enctype='multipart/form-data'>
{% csrf_token %}
{{ form.non_field_errors }}
...
</form>
Please note that you might need to wrap the errors with some bootstrap markup. More info in docs

How create a custom form in django with database values

model.py
class Venue(models.Model):
venue_Name = models.CharField(max_length=100)
place = models.CharField(max_length=50)
rent = models.IntegerField()
parking_area = models.IntegerField()
class Decoration(models.Model):
rate = models.IntegerField()
I have printed the values in database as radio buttons what i want to do is that i want to get the total sum i.e venue.rent + decoration.rate and print it in another page What shoud i give in form action I'm not that familiar with forms.
html
<form action="{% %}" method="post">
{% for venue in venues %}
<input type="radio" name=venue value=venue.rent />{{ venue.venue_Name}}
{% endfor %}
{% for decoration in decorations %}
<input type="radio" name=decor value=decoration.rate />{{ decoration.rating }}
{% endfor %}
<input type="submit" value=" " />
</form>
what should i write in view and urls to get the sum
You can use Django's form for validation and parsing. For that you would set up a form like so:
forms.py
from django import forms
class TotalSumForm(forms.Form):
venue = forms.ModelChoiceField(queryset=Venue.objects.all(), required=True)
decoration = forms.ModelChoiceField(
queryset=Decoration.objects.all(), required=True)
def get_total(self):
# send email using the self.cleaned_data dictionary
return self.cleaned_data['venue'].rent +\
self.cleaned_data['decoration'].rate
And then using a class based view, add the result to context upon submission.
views.py
from myapp.forms import TotalSumForm(
from django.views.generic.edit import FormView
class TotalCost(FormView):
template_name = 'your_template.html'
form_class = TotalSumForm
success_url = '/thanks/'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
total_result = form.get_total()
# return back to your_template.html with the total in context
return self.render_to_response(self.get_context_data(
form=form, total=total_result))
The urls are pretty simple:
urls.py
from django.conf.urls import patterns, url
import myapp.views
urlpatterns = patterns(
'',
url(r'^total_calc/$', myapp.views.TotalCost.as_view(), name='calculate_total'),
)
Your html could be modified like so
your_template.html
<html>
<body>
<h1>TEST SUCCESFULL!</h1>
{% if total %}
<p>Total cost for venue and decoration: {{ total }}</p>
{% endif %}
<form action="{% url 'calculate_total' %}" method="post">
{{ form.as_p }}
<input type="submit" value="Calculate Total" />
</form>
</body>
</html>

Django form will not validate

I am working with Django forms and for some reason, this form will not validate! It submits alright, or at least the runserver shows an http post response with code 200 (ok). For some reason though, my form will not pass the is_valid test!
views.py:
def new_show(request):
if request.method == 'POST':
img_form = ImageForm(request.POST, request.FILES)
show_form = NewShowForm(request.POST)
if show_form.is_valid():
new_Show = Show()
new_Show.title=show_form.cleaned_data['title']
new_Show.body=show_form.cleaned_data['body']
new_Show.pub_date=timezone.now()
new_Show.location=show_form.cleaned_data['location']
new_Show.time=show_form.cleaned_data['time']
new_Show.save()
if img_form.is_valid():
image=Image(image=request.FILES['imageFile'])
new_Show.image_set.add(image)
return HttpResponseRedirect(reverse('shows'))
else:
return HttpResponseRedirect(reverse('shows'))
else:
show_form = NewShowForm()
img_form = ImageForm()
return render_to_response(
'shows/new_show.html',
{'show_form': show_form, 'img_form': img_form},
context_instance=RequestContext(request)
)
Here is my template snippet:
<form action="{% url "new_show" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ show_form.non_field_errors }}</p>
<p>
<label for="title">Title:</label>
<input type="text" name="title"/>
</p>
<p>
<label for="body">Body:</label>
<textarea type="text" name="body"> </textarea>
</p>
<p>
<label for="location">Location:</label>
<input type="text" name="location"/>
</p>
<p>
<label for="time">Date:</label>
<input type="text" id="time" maxlength="25" size="25" name="time"><img src="{{ STATIC_URL }}../../static/cal.gif" width="16" height="16" border="0" alt="Pick a date">
</p>
<!-- Upload Form. Note enctype attribute! -->
{% csrf_token %}
<p>{{ img_form.non_field_errors }}</p>
<p>{{ img_form.imageFile.label_tag }}</p>
<p>
{{ img_form.imageFile.errors }}
{{ img_form.imageFile }}
</p>
<p><input type="submit" value="Add Upcoming Show"></input></p>
</form>
Here is my form Class:
class NewShowForm(forms.Form):
title=forms.CharField()
body=forms.CharField(widget=forms.TextArea)
location=forms.CharField()
time=forms.DateTimeField(required=True)
class ImageForm(forms.Form):
imageFile = forms.FileField(required=False, label='Select an Image')
Please help me!
If new_Show is a model, why not create a ModelForm instead of forms.Form?
So, instead of
class NewShowForm(forms.Form):
title=forms.CharField()
body=forms.CharField(widget=forms.TextArea)
location=forms.CharField()
time=forms.DateTimeField(required=True)
class ImageForm(forms.Form):
imageFile = forms.FileField(required=False, label='Select an Image')
why not using,
from django.forms import ModelForm
class NewShowForm(ModelForm):
class Meta:
model = NewShow
class ImageForm(ModelForm):
class Meta:
model = Image
?
Using ModelForm will ensure that form validation meets that of model. Moreover, it can cut off your code (especially line 6 to 11).
It will help to add these two lines to your view before if is_valid() to see the errors it's giving:
if request.method == 'POST':
img_form = ImageForm(request.POST, request.FILES)
show_form = NewShowForm(request.POST)
print(form.is_valid())
print(form.errors)
if show_form.is_valid():
You can paste the errors here and we can see what's the issue
Since you've put 2 Django forms together under one HTML form tag, when you submit the form on the front-end you're sending an extra field through request.POST that your NewShowForm doesn't have. If you combine both forms into a single Django form, you should be able to get this to work.

Categories