How to use context with class in CreateView in django?
Before i have:
#views.py
from django.views.generic import CreateView
from cars.models import *
def CreateCar(CreateView):
info_sended = False
if request.method == 'POST':
form = FormCar(request.POST, request.FILES)
if form.is_valid():
info_sended = True
form.save()
else:
form = FormCar()
ctx = {'form': form, 'info_sended':info_sended}
return render_to_response("create_car.html", ctx,
context_instance=RequestContext(request))
Now, a have, and try:
class CreateCar(CreateView):
info_sended = False
template_name = 'create_car.html'
model = Car
success_url = 'create_car' #urls name
def form_valid(self, form):
info_sended = True
ctx = {'form': form, 'info_sended':info_sended}
return super(CreateCar, self).form_valid(form)
My html page is:
<!-- create_car.html -->
{% extends 'base.html' %}
{% block content %}
{% if info_sended %}
<p>Data saved successfully</p>
<p>Show List</p>
{% else %}
<form class="form-horizontal" action="" method="post">
{% csrf_token %}
{% include "form.html" %}
<div class="col-md-offset-1">
<button class="btn btn-primary" type="submit">Add</button>
</div>
</form>
{% endif %}
{% endblock %}
You should define get_context_data() method in your class view. Update your code as
from django.shortcuts import render
class CreateCar(CreateView):
info_sended = False
template_name = 'create_car.html'
model = Car
success_url = 'create_car' #urls name
def form_valid(self, form):
self.info_sended = True
# Instead of return this HttpResponseRedirect, return an
# new rendered page
super(CreateCar, self).form_valid(form)
return render(self.request, self.template_name,
self.get_context_data(form=form))
def get_context_data(self, **kwargs):
ctx = super(CreateCar, self).get_context_data(**kwargs)
ctx['info_sended'] = self.info_sended
return ctx
You have to use get_context_data
class CreateCar(CreateView):
info_sended = False
template_name = 'create_car.html'
model = Car
success_url = 'create_car' #urls name
def form_valid(self, form):
self.info_sended = True
return super(CreateCar, self).form_valid(form)
def get_context_data(self, **kwargs):
ctx = super(CreateCar, self).get_context_data(**kwargs)
ctx['info_sended'] = self.info_sended
return ctx
If you see the django source CreateView inherits from BaseCreateView this one inherits from ModelFormMixin in turn this one inherits from FormMixin and this one inherits from ContextMixin and the only method this one defines is get_context_data.
Hope this helps you.
PD: This may be a bit confusing for better understanding of inheritance in Python feel free of read this article about MRO.
Since your are creating a new instance of a Car, there is no context for get_context_data because there is no object yet. I didn't test using Mixin to get the context from another class as suggested above, but that seems reasonable. However, if I can assume you want to use the basic CreateView, UpdateView and DeleteView, then I solved this by assuming I will have no context for CreateView. Then in my template I used an if to make the decision, such as:
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value={% if not buttonword %}Save{% else %}{{ buttonword }}{% endif %}>
</form>
In DeleteView I include:
context['buttonword'] = 'Delete'
In UpdateView I include:
context['buttonword'] = 'Update'
As I said, I do not set buttonword in CreateView. Hence, when the template logic is done, if buttonword is assigned, the word in it shows up in the button, otherwise Save shows up on the button.
Related
I used to send form as content in function-base-view and I could use for loop to simply write down fields and values like:
{% for x in field %}
<p>{{ x.label_tag }} : {{ x.value }} </p>
I don't remember whole the way so maybe I wrote it wrong but is there anyway to do this with class-based-views, because when I have many fields its really hard to write them 1by1
Not entirely sure if this is what you need. But still I will try to answer. I took an example with class AuthorDetail(FormMixin, DetailView) as a basis. In get_context_data saved the form itself. In the template, first I displayed the form, then the value from the bbs model and requested
form.message.label_tag. To get the tag, I looked at this documentation.
In the class, replace the Rubric model with your own. In the template path: bboard/templ.html replace bboard with the name of your application where your templates are located.
views.py
class Dw(FormMixin, DetailView):
model = Rubric
template_name = 'bboard/templ.html'
form_class = TestForm
context_object_name = 'bbs'
def get_context_data(self, **kwargs):
context = super(Dw, self).get_context_data(**kwargs)
context['form'] = self.get_form()
print(77777, context['form']['message'].label_tag())
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
forms.py
class TestForm(forms.Form):
message = forms.CharField()
templates
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="adding">
</form>
<h2>{{ bbs }}</h2>
<h2>{{ form.message.label_tag }}</h2>
urls.py
urlpatterns = [
path('<int:pk>/', Dw.as_view(), name='aaa'),
]
I have a DetailView Based on a model ( A ) and on the same template I have a ModelFormView from a model B which has FK to model (A)
The data from form doesn't get saved to the database.
This is the DetailView:
class LocationView(DetailView):
template_name = "base/stocks/location.html"
model = LocationStock
def get_context_data(self, **kwargs):
context = super(LocationView, self).get_context_data(**kwargs)
context['form'] = OutsModelForm
return context
def get_object(self):
id_ = self.kwargs.get("id")
return get_object_or_404(LocationStock, id=id_)
This is the FormView:
class OutsAdd(FormView):
form_class = OutsModelForm
success_url = reverse_lazy('base:dashboard')
def form_valid(self, form):
return super().form_valid(form)
This is the url.py:
path('locations/<int:id>', LocationView.as_view(), name='location-detail'),
path('locations/outs', require_POST(OutsAdd.as_view()), name='outs-add'),
This is the template:
<form method="POST" action="{% url 'outs-add' %}" >
<div class="modal-content">
{% csrf_token %}
{% render_field form.quantity placeholder="Quantity"%}
{% render_field form.id_year placeholder="Year"%}
{% render_field form.id_location placeholder="ID Location"%}
</div>
<div class="modal-footer">
<input class="modal-close waves-effect waves-green btn-flat" type="submit" value="Save">
</div>
</form>
The data gets POSTED in the /locations/outs but is not saving to the actual database.How can I save it ?
The functionality of Django's FormView is really only meant to display a form on a GET request, show form errors in the case of form_invalid, and redirect to a new URL if the form is valid. In order to persist the data to the database you have two options. First you can simply call form.save() in your FormView:
class OutsAdd(FormView):
form_class = OutsModelForm
success_url = reverse_lazy('base:dashboard')
def form_valid(self, form):
form.save()
return super().form_valid(form)
Or, you can use the generic CreateView. Django's CreateView is similar to a FormView except it assumes it's working with a ModelForm and calls form.save() for you behind the scenes.
I am inheriting from CreateView to create a form for a model.
class NewBlogView(CreateView):
form_class = BlogForm
template_name = 'blog_settings.html'
def form_valid(self, form):
blog_obj = form.save(commit=False)
blog_obj.owner = self.request.user
blog_obj.slug = slugify(blog_obj.title)
blog_obj.save()
return HttpResponseRedirect(reverse('home'))
Here is my template code:
{% extends 'base.html' %}
{% block content %}
<h1>Create New User</h1>
<form action='' method='post'>{% csrf_token %}
{{ form.as_p }}
<input type='submit' value='Create Account' />
</form>
{% endblock %}
At this moment everything is working as expected, but when I am overriding get_context_data() my title field is disappearing.
class NewBlogView(CreateView):
form_class = BlogForm
template_name = 'blog_settings.html'
def form_valid(self, form):
blog_obj = form.save(commit=False)
blog_obj.owner = self.request.user
blog_obj.slug = slugify(blog_obj.title)
blog_obj.save()
return HttpResponseRedirect(reverse('home'))
def get_context_data(self, **kwargs):
ctx = super(NewBlogView, self).get_context_data(**kwargs)
print(ctx)
return ctx
I am thinking although I am running the original get_context_data() form the function that I am inheriting, there is something that is going wrong when it comes to taking the field name from the form_class. Can someone help with that confusion that I have?
My ValidationError seems to be stuck somewhere!
The routine clean() seems to be running as expected since I am not being forwared to success_url, but the error message does not show up.
forms.py
from django import forms
from django.core.exceptions import ValidationError
class ContactForm(forms.ModelForm):
def clean(self):
raise forms.ValidationError('basic error')
class Meta:
model = Person
views.py
class ContactView(generic.edit.FormView):
def form_valid(self, form):
template_name = 'union/contact.html'
form_class = ContactForm # class of union/forms.py
success_url = '/union/VIP_confirm'
context_object_name = 'contact'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
context['count'] = countcal()
return context
def form_valid(self, form):
if True:
model_id = form.save()
person_id = model_id.pk
return HttpResponseRedirect(
reverse('union:VIP_save', kwargs={'person_id': person_id}))
union/contact.html template for the ContactView
<form action="{% url 'union:ContactView' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
<input type="submit" value="abschicken" />
<!-- regular value submission -->
</form>
I followed the official docs and several other sources but to no success as to find out what the problem is or where it lies. To solve this, do you need additional infos about my app?
EDIT: I seem to miss something in the template, since the test error message (basic error) shows up if the template just uses the form-placeholder {{form}}
Using Django 1.7.1
EDIT EDIT:
OK. The comments pointed me to the right direction. I missed {{form.errors}}.
My problem is not to show django form fields on template.It's silly but I just haven't found any solution.
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['name', 'email', 'text']
def __init__(self, content_type, id, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.content_type = content_type
self.id = id
def save(self, commit=True):
post_type = ContentType.objects.get_for_model(Post)
comment_type = ContentType.objects.get_for_model(Comment)
comment = super(CommentForm, self).save(commit=False)
if self.content_type == 'post':
comment.content_type = post_type
comment.post = self.id
else:
parent = Comment.objects.get(id=self.id)
comment.content_type = comment_type
comment.post = parent.post
comment.object_id = self.id
if commit:
comment.save()
return comment
my view:
def add_comment(request, content_type, id):
if request.method == 'POST':
data = request.POST.copy()
form = CommentForm(content_type, id, data)
if form.is_valid():
form.save()
return redirect(reverse('index'))
my add_comment template:
<form method="post" action="{% url 'add_comment' 'post' post.id %}">
{% csrf_token %}
{% if not user.is_authenticated %}
{{ form.name.label_tag }}
{{ form.name }}
{{ form.email.label_tag }}
{{ form.email }}
{% endif %}
{{ form.text.label_tag }}
{{ form.text }}<br>
<input type="submit" value="Comment" />
</form>
and I included like:
<button id="button" type="button">Add Comment</button>
<div id="post_comment_form">{% include 'articles/add_comment.html' %}</div>
</article> <!-- .post.hentry -->
why not django rendered form fields,despite of showing buttons?
EDIT:
I'm rendering form in post view.
def post(request, slug):
post = get_object_or_404(Post, slug=slug)
comments = Comment.objects.filter(post=post.id)
return render(request,
'articles/post.html',
{'post': post,
'form': CommentForm,
'comments': comments,
# 'child_comments': child_comments
}
)
You forgot to instantiate the form, change this line:
'form': CommentForm,
to this
'form': CommentForm(),
In your view, you're not sending any context variables to the template, so your 'form' object isn't available for your template to process.
For example, the following return statement will render your .html and pass along all local variables, this isn't necessarily the best option (how much do you want your template to have access to), but is simple:
from django.shortcuts import render
...
return render(request, "template.html", locals())
you can also pass a dictionary instead of all local variables. Here's the documentation for render