I have some problem to figure out how new django views (template view) and forms can works I also can't find good resources, official doc don't explain me how can get request ( I mean get and post) and forms in new django views class
Thanks
added for better explain
for example I have this form :
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
and this is the code for read and print the form (old fashion way):
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
well my question is how you can do the same thing with template view thanks
Use a FormView instead, i.e.
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
More on FormView in Django Docs
Technically TemplateView can also be used, just overwrite the post method, since by default template view does not allow you to post to it:
class ContactUsView(TemplateView):
template_name = 'contact_us/contact_us.html'
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid():
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
def get_context_data(self, **kwargs):
context = super(ContactUsView, self).get_context_data(**kwargs)
form = ContactUsEmailForm(self.request.POST or None) # instance= None
context["form"] = form
#context["latest_article"] = latest_article
return context
I think the FormView makes more sense though.
I would recommend just plodding through the official tutorial and I think realization will dawn and enlightenment will come automatically.
Basically:
When you issue a request: '''http://mydomain/myblog/foo/bar'''
Django will:
resolve myblog/foo/bar to a function/method call through the patterns defined in urls.py
call that function with the request as parameter, e.g. myblog.views.foo_bar_index(request).
and just send whatever string that function returns to the browser. Usually that's your generated html code.
The view function usually does the following:
Fill the context dict for the view
Renders the template using that context
returns the resulting string
The template generic view allows you to skip writing that function, and just pass in the context dictionary.
Quoting the django docs:
from django.views.generic import TemplateView
class AboutView(TemplateView):
template_name = "about.html"
All views.generic.*View classes have views.generic.View as their base. In the docs to that you find the information you require.
Basically:
# urls.py
urlpatterns = patterns('',
(r'^view/$', MyView.as_view(size=42)),
)
MyView.as_view will generate a callable that calls views.generic.View.dispatch()
which in turn will call MyView.get(), MyView.post(), MyView.update() etc.
which you can override.
To quote the docs:
class View
dispatch(request, *args, **kwargs)
The view part of the view -- the method that accepts a request
argument plus arguments, and returns a HTTP response. The default
implementation will inspect the HTTP method and attempt to delegate to
a method that matches the HTTP method; a GET will be delegated to
get(), a POST to post(), and so on.
The default implementation also sets request, args and kwargs as
instance variables, so any method on the view can know the full
details of the request that was made to invoke the view.
The big plusses of the class based views (in my opinion):
Inheritance makes them dry.
More declarative form of programming
Related
I'm trying to do the same thing below in Django Generic CBV.
Function Based View
def form_handling(request):
if request.method == "POST":
form = SimilarStore(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('...')
else:
form = SimilarStore()
...
Class Based View
class SimilarStoreCreateView(CreateView):
model = SimilarStore
form_class = SimilarStoreForm
template_name='cms/similarstore_new.html'
success_url=reverse_lazy('cms:similarstore')
def form_valid(self, form):
form = SimilarStoreForm(self.request.POST, self.request.FILES)
form.save()
return redirect(self.get_success_url())
def form_invalid(self, form):
form = SimilarStoreForm()
...
I'm confused about how I can check if request.method is POST or GET in Django Generic CBV. I know Django regular CBV supports get() and post(). However, in Generic views, there's no post(). What should I do to handle POST requests in Django Generic?
Also, my CBV codes throw an error 'SimilarStoreForm' object has no attribute 'cleaned_data'. Why is that?
CreateView has post() method it's inherited from ProcessFormView. You can find method source here. As for your question from the source code you may see that form_valid and form_invalid method will be triggered only for POST requests.
You don't need to override form_invalid method since by default it will render invalid form with form's error. And you don't need to override form_valid also, CreateView do redirect automatically after. So you can simple do:
class SimilarStoreCreateView(CreateView):
model = SimilarStore
form_class = SimilarStoreForm
template_name='cms/similarstore_new.html'
success_url=reverse_lazy('cms:similarstore')
And this will give you same logic as in your function based view.
CreateViews have a post method as shown below that you can use
def post(self, request, *args, **kwargs):
self.object = None
return super().post(request, *args, **kwargs)
Here is a good reference for all the methods and attributes of CBVs
Basically, your current setup is all you need. You see, CreateView's post method is effectively its base class'es (BaseCreateView) post method which calls its own base class'es (ProcessFormView) post method, that being
class ProcessFormView(View):
# ...
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance with the passed
POST variables and then checked for validity.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Therefore, whenever a POST HTTP request gets routed to SimilarStoreCreateView it will finally be dispatched to the aforementioned post method already implementing the desired workflow of yours.
I'm using Django-registration-redux and I want give more data to a view to render my base template. I read the example in doc.
My url.py:
class MyPasswordChangeView(PasswordChangeView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# context['book_list'] = Book.objects.all() # example in doc
context_dict = services.get_base_data_for_views(request)
return context_dict
urlpatterns = [
...
path('accounts/password/change/', MyPasswordChangeView.as_view(
success_url=reverse_lazy('auth_password_change_done')), name='auth_password_change'),
...
]
I have the extra data in services.py but this code gives error:
name 'request' is not defined
So context_dict isn't defined. Where can I take my request from? Mainly I need the user (but print(user)= 'user' is not defined). Or should I write another function?
In methods of Django class based views, you can access the request with self.request.
class MyPasswordChangeView(PasswordChangeView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context_dict = services.get_base_data_for_views(self.request)
return context_dict
Therefore you can access the user with self.request.user. Normally you would want to use login_required or LoginRequiredMixin so that only logged-in users can access the view, but in your case PasswordChangeView takes care of that for you.
I haven't found any definitive documentation on this, but I have a contact form that I need to use in the sidebar in multiple views. Currently my code is dirty because I am repeating the code snippet below in multiple views to process the form. Is there a way to place the Post Request in a template that can be inherited?
View
def contact(request):
form_class = ContactForm
if request.method == 'POST':
form = form_class(data=request.POST)
messages.add_message(request, messages.SUCCESS, 'Thank you, we have received your message.')
if form.is_valid():
...
Thank you for your help.
Now, I'm assuming you do the same operation as below while every view is run:
def contact(request):
# First you choose the form.
form_class = ContactForm
# Then you want to know if request is POST type
if request.method == 'POST':
# You take the form data from given POST
form = form_class(data=request.POST)
# You add message to messages.
messages.add_message(request, messages.SUCCESS, 'Thank you, we have received your message.')
If you do the same over and over again, you can create your own function at the beginning of your views.py file of any app to make it short and not to repeat yourself again and again.
def take_message(request, form, messages, message):
if request.METHOD == "POST":
# I'm reinitializing <form> variable here.
form = form(data=request.POST)
# That <message> variable below must be a string, then you can dynamically pass your message.
messages.add_message(request, messages.SUCCESS, message)
Then use it in your view:
def contact(request):
take_message(request, ContactForm, messages, "Thanks, we got your message.")
# And the rest here.
However, I recommend for you to use class-based views since they can handle any request types as method. So, I'm changing that take_message method as below:
def take_message(request, form, messages, message):
# I'm reinitializing <form> variable here.
form = form(data=request.POST)
# That <message> variable below must be a string, then you can dynamically pass your message.
messages.add_message(request, messages.SUCCESS, message)
Then, my view is as below:
from django.views.generic import TemplateView
# And any other important imports.
# ...
class ContactView(TemplateView):
template_name = "contact.html" # This is your template.
def get(self, request):
# Do things when the method is GET. Like, viewing current messages in a hypothetical admin template.
def delete(self, request):
# Do things when the method is DELETE. Don't forget to use authentication here, so only superuser can delete messages.
def post(self, request):
# Do things when the method is POST.
# I'm assuming anonymous users can send messages, so there's no need for authentication here.
take_message(request, ContactForm, messages, "Thanks you, we got your message.")
# Other things to do.
# urls.py
url(r"^contact/$", ContactView.as_view(), name="contact-page")
I want to create one page with both a login and signup form. Per this answer: https://stackoverflow.com/a/1395866/2532070, the best way is to use a separate <form action="{{url}}" tag for each form, and use two different views to handle the POST data for each form. My problem is that using the same view would allow me to pass the errors back to the forms, but if I use separate views with separate urls, I have to redirect the user back to the original page and add these parameters as part of a query string, which doesn't seem very efficient.
My Urls:
url(r'^$', HomePageView.as_view(), name="home_page"),
url(r'^profile/(?P<pk>\d+)/$', MemberUpdate.as_view(template_name="profile.html"), name="profile_page"),
url(r'^signup/$', SignupUser.as_view(), name="signup_user"),
My views:
class HomePageView(TemplateView):
template_name = "index.html"
login_form = AuthenticationForm()
signup_form = UserCreationForm()
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context['login_form'] = self.login_form
context['signup_form'] = self.signup_form
context['signup_action'] = reverse("signup_user")
return context
class SignupUser(CreateView):
model = settings.AUTH_USER_MODEL
form_class = MemberForm
template_name = "index.html"
def get_success_url(self):
return reverse("profile_page", args=[self.object.pk])
def form_invalid(self, form):
# here, how do I pass form.errors back to the home_page?
# I know messages framework is possible but this isn't as
# easy to add each error to its respective field as
# form.errors would be.
return HttpResponseRedirect(reverse('home_page'))
...and I would have a third view for the login form's POST data. Is this an acceptable way to manage my forms/views, or am I better off simply writing one overall view that distinguishes between the signup and login form within its post method with an if statment?
What I'm trying to do is Django boilerplate for functional views. Any help here is very much appreciated, as the docs show examples for the template view and list view, but I've found very little for the model-based generic views. Am I missing an example in the docs?
I have a model that represents an entry in a calendar. There's a foreign key to another object (not a user) that owns the entry. What I want to do is simply to create the entry, ensuring that the entry's foreign key is properly set and then return the user to the appropriate calendar page.
I don't know, though, how class-based generic views receive their URL arguments and I'm not clear on how to set the success_url so that it reuses the id that was originally passed to the creation URL. Again, thank you in advance for your help.
What I'm asking, essentially, is, what is the class-based generic view equivalent of the following:
def create_course_entry(request, class_id):
'''Creates a general calendar entry.'''
if request.method == 'POST':
form = CourseEntryForm(request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.course = Class.objects.get(pk=class_id)
new_entry.full_clean()
new_entry.save()
return HttpResponseRedirect('/class/%s/calendar/' % class_id)
else:
form = CourseEntryForm()
return render_to_response('classes/course_entry_create.html',
{ 'class_id': class_id, 'form': form, },
context_instance=RequestContext(request))
You could subclass the edit.CreateView generic view, set the class/course in the dispatch() method, and save this by overriding the form_valid() method:
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.generic.edit import CreateView
class CourseEntryCreateView(CreateView):
form_class = CourseEntryForm
model = CourseEntry
def dispatch(self, *args, **kwargs):
self.course = get_object_or_404(Class, pk=kwargs['class_id'])
return super(CourseEntryCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.course = self.course
self.object.save()
return HttpResponseRedirect(self.get_success_url())
If you're not customising the CourseEntryForm ModelForm, then you can leave out the form_class property.
Unfortunately, it is not possible to call super() in the form_valid() method - due to the way it has been written would mean the object would be saved again.
If you need the Class (course?) instance in the template context, then you can add this in the get_context_data() method:
def get_context_data(self, *args, **kwargs):
context_data = super(CourseEntryCreateView, self).get_context_data(
*args, **kwargs)
context_data.update({'course': self.course})
return context_data
An alternative to Matt Austin's answer might be to override the get_form method:
from django.shortcuts import get_object_or_404
from django.views.generic import CreateView
class CourseEntryCreateView(CreateView):
form_class = CourseEntryForm
model = CourseEntry
def get_form(self, form_class):
form = super(CustomCreateView, self).get_form(form_class)
course = get_object_or_404(Class, pk=self.kwargs['class_id'])
form.instance.course = course
return form
This way, .course is on the CourseEntry instance in the context, and on the instance created when the form is saved upon POST.