Django Redirect with non-default table - python

In my Django project, I have a generic view. It has a single select form in which the user can choose a formset from a list of formsets to load.
Then, a POST request is sent to the server, requesting the new formset. The view is reloaded with the new formset while remaining on the same URL.
Here's an example:
Assume there are two tables: default_formset and formset_2
If I am making updates on my default_formset the redirect works perfectly. The issue I am having is with 'updating' non-default formsets like formset_2.
When an update is made on formset_2 on redirect it will load up the default_formset rather than loading up formset_2
In foo.py:
def generic_view(request):
#Here, the user's input determines what formset is rendered
formset_names = SingleSelectForm(formsets, 'formset_name', request.POST)
#This variable gets the user's input
formset_selected = request.POST.get('formset_name', 'default_formset')
''' '''
Forms are initialized
''' '''
if request.POST:
if 'formset_name' in request.POST:
formset = the_formset(form_kwargs=empty_forms, initial=initial_values)
if 'update' in request.POST:
if formset.is_valid():
for form in formset:
if form.has_changed():
# Do some mysql stuff
return HttpResponseRedirect("/foo/")
In urls.py:
from website.foo import foo
urlpatterns = [
path('foo/', foo.generic_view, name='generic_view'),
]
If a user updated a row in formset_2, is it possible to reload this generic_view with the updated formset_2 instead of default_formset?

Related

What's the best way to link views and pass data between them?

I have a basic diary application that has two views: Add and Edit view. When user hits 'Add' button on Add view, the data is saved into database and simple message is shown. When user hits 'Update' button on Edit View, the data is updated and a last update stamp is added.
So far these views run independently. The Edit View by default loads the last saved entry and allows for that to be updated.
I want to update it so that upon successful addition of new diary entry on the Add View, it transitions to the Edit View to allow the user to Edit and Update if they want to. How do I link the views together and pass the relevant data to Edit View to know which Entry (database object) to fetch for edit? Also I would like to be able to use the Edit View independently to fetch a specified diary entry for example on a GET. So the Edit View is agnostic to whomever called it, it just knows which diary entry to load.
The code for Add and Edit View as they are now, are displayed below:
def addEntry(request):
entryForm = None
if request.method == "POST":
entryForm = EntryForm(request.POST)
if entryForm.is_valid():
entryForm.save(commit = True)
request.method = "GET"
return entrySubmitted("Entry has been submitted")
else:
logger.info(entryForm.errors)
else:
# Set up view with new entry form
entryForm = EntryForm()
template = getEntryViewTemplate(entryForm)
return render(request, "DiaryEntry.html", template)
def editEntry(request):
# Get the latest entry from the database
entry = Entry.objects.last();
if request.method == 'GET':
entryForm = EntryForm(instance = entry)
else:
entryForm = EntryForm(request.POST, instance = entry)
if entryForm.is_valid():
entryForm.setLastUpdated(datetime.now())
entryForm.save(commit = True)
templateData = getEntryViewTemplate(entryForm)
return render(request, "EditEntry.html", templateData)
Thanks in Advance,
Francis
Look django documentation about Class Base View.
https://docs.djangoproject.com/en/1.10/topics/class-based-views/intro/#
You can create a class, with different method.
Get, Post(create object), Put(Update object)
And you have a lot of methods and attributes usefull.
You can link your class view to a specific model (Entry) for example.
https://docs.djangoproject.com/fr/1.10/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_queryset
How do I link the views together and pass the relevant data to Edit View to know which Entry (database object) to fetch for edit?
You need to pass ID Object to edit. Two solutions. In your Post data. Or by argument in your url.
http://your_web_site.com/entry/id_entry/
And you init your form with entry data
In your URL django you can link to url to your class base view.
url(r'^entry/$', views.EntryView.as_view(), name='create_entry'),
url(r'^entry/(?P<pk>[-\w/]+)$', views.EntryView.as_view(), name='update_entry'),
I hope this will help you

How to deal with errors in large formsets?

I'm just starting with formsets and I have trouble making a user friendly error system.
So the user sees a list view that shows him all instances of a model already written into a formset. This pages is meant to show him the data and edit it as well.
Before I was using a lot of individual forms with a save button for every form. But now I want to improve it to have a formset that provides a single save button for all of the forms.
But there comes my problem: Before I used to send the user via "action" to another url (e.g. .../update/ ) which processes the request and then sends him back to the list view he saw before. That's to avoid multiple submits when hitting F5.
But now if I do this and only a single form is wrong all the information the user had entered is lost.
So instead I stopped using the extra URL and made the processing part of the list view. Now I can use form.error on every form, but also the user resubmits when hitting F5.
Is there a way to have both?
Also: I have 1 extra form. But if the user changes it, and I feed the POST data into the formset, save it and then put it back to the page I lost the extra formset, because now the former extra is showing the newly entered instance and there is no true extra field left until the page is refreshed without sending post data.
Here is my View:
class BVServerListView(View):
def get(self, request):
eigene_server = Server.objects.filter(user=request.user)
EigeneServerFormset = modelformset_factory(Server, extra=1, form=ServerForm)
eigene_server_formset = EigeneServerFormset(queryset=eigene_server)
context = {'eigene_server': eigene_server_formset,}
return render(request, 'bildverteiler/server_list.html', context)
def post(self, request):
eigene_server = Server.objects.filter(user=request.user)
EigeneServerFormset = modelformset_factory(Server, extra=1, form=ServerForm)
eigene_server_formset = EigeneServerFormset(request.POST, request.FILES)
for form in eigene_server_formset.forms:
if form.data.get('delete', False):
server = Server.objects.get(user=request.user, name=form.data['name'])
server.delete()
else:
if form.has_changed() and form.is_valid():
server = form.save(commit=False)
server.user = request.user
server.save()
context = {'eigene_server': eigene_server_formset,}
return render(request, 'bildverteiler/server_list.html', context)
There is no difference between using a single form or a formset here. The answer is the same: post to the same view, but redirect after a successful save.
The other thing that you are doing wrong here is to validate and save individual forms one by one. Don't do that, because you could end up with a situation that the first forms are valid and get saved, but subsequent ones are invalid and therefore the formset needs to be redisplayed. Instead, validate the formset as a whole:
if eigene_server_formset.is_valid():
for form in eigene_server_formset.forms:
if form.cleaned_data.get('delete'):
... delete ...
else:
form.save()
return HttpResponseRedirect('somewhere_else')
return render...

Django: Multiple forms possible when using FormView?

I've recently learned Django forms by subclassing FormView, where the desired form is assigned to the FormView.form_class attribute. When the form validates, the form_valid() method is invoked (for that one form). For example:
from accounts.forms import SignUpForm, UpdateAccountForm, UpdateBillingForm
class SignUpView(FormView):
form_class = SignUpForm
def form_valid(self, form):
# code when form validates...
However, I now have a situation where I need three unique forms on one page (with only one form visible to the user at a time). So, I'd like to handle them all in the same View.
Are multi-form pages possible using FormView? I'm not sure how to handle it, both in terms of passing multiple forms to the View (e.g. the other UpdateAccountForm and UpdateBillingForm), as well as distinguishing which one was submitted/validated? What would be the best way?
Well, for what it's worth here's what ultimately worked for me, using a generic View.
1) I added a hidden input field (named 'action') to each individual form on the page. For example, this is the form for updating user's info, which is pulling in UserForm:
<form action='/account/' method='post'>{% csrf_token %}
<input type='hidden' name='action' value='edit_user'>
{{ user_form.as_p }}
<input type='submit' value='Update'>
</form>
2) In my View logic, I can distinguish the forms by applying a prefix (per other SO posts and Django docs). Then, depending on the incoming 'action', I only bind the applicable form to the POST request (so validations aren't applied across all of them). In my case, I had two forms defined in forms.py, UserForm and BillingForm:
from django.views.generic.edit import View
from django.shortcuts import render
from django.http import HttpResponse
from accounts.forms import UserForm, BillingForm
class AccountView(View):
def get(self, request):
# code for GET request...
def post(self, request):
#instantiate all unique forms (using prefix) as unbound
user_form = UserForm(prefix='user_form')
billing_form = BillingForm(prefix='billing_form')
# determine which form is submitting (based on hidden input called 'action')
action = self.request.POST['action']
# bind to POST and process the correct form
if (action == 'edit_user'):
user_form = UserForm(request.POST, prefix='user_form')
if user_form.is_valid():
# user form validated, code away..
elif (action == 'edit_billing'):
billing_form = BillingForm(request.POST, prefix='billing_form')
if billing_form.is_valid():
# billing form validated, code away..
# prep context
context = {
'user_form': user_form,
'billing_form': billing_form,
}
return render(request, 'accounts/account.html', context)
Seems to work well, hopefully this is the right approach (?)
You can write a plain python class mimicking the Form API (at least the useful parts) and wrapping your three forms. Detecting which form has been submitted is just a matter of adding a hidden input with the form's identifier in each form (hint : use prefixes for your forms and use that same prefix as identifier).
The other solution is to use a simple function-based view instead, but even there I'd still use the same "form wrapper" pattern as far as I'm concerned.

passing data between class based forms

I am fairly new to Django and class based forms, and I am having trouble understanding how these interact with each other. Following from the django project example, I have tried to build a "search form", which would sit on all pages of my project:
# forms.py
from django import forms
class SearchForm(forms.Form):
myquery = forms.CharField(max_length=255,label="", help_text="sq")
def __unicode__(self):
return self.myquery
# views.py
from searchapp.forms import SearchForm
from django.views.generic.edit import FormView
from django.views.generic import TemplateView
class SearchView(FormView):
template_name = 'index.html'
form_class = SearchForm
success_url = '/searchres/'
def form_valid(self, form):
thequery=form.cleaned_data.get('myquery')
return super(SearchView, self).form_valid(form)
class Meta:
abstract = True
class SearchResView(SearchView):
template_name = 'searchres.html'
#urls.py
from django.conf.urls import patterns, include, url
from django.conf import settings
from deals.views import IndexView
from searchapp.views import SearchView, SearchResView
urlpatterns = patterns('',
url(r'^index/', SearchView.as_view(),name="home"),
url(r'^searchres/', SearchResView.as_view(),name="searchresx"),
)
The plan is the start off with a simple form for user to enter the search query, and also show the input form on the results page. I have the following questions here (sorry - I am a Django newbie esp. to Class Based Views):
How does one pass data ("thequery") to the success_url? i.e I would like success_url to have access to "thequery" so that I can use something like {{thequery}} on my template tags.
Upon submitting the form(name="home"), I see POST data from the form on my firebug, but I am able to see just "myquery" rather than "thequery". How does one use get_context_data() here to add/post "thequery" variable aswell?
Finally, I was wondering if it would be possible to construct the success_url based on "thequery" string i.e something like success_url = '/searchres/?q=' + thequery
Thank you in advance - I am hoping to learn more.
I would suggest using function based views for this. If you choose to subclass a generic view you will need to dig through a lot of documentation and possibly source code, to find the right methods to override. (If you're really keen then look at the ListView class along with the get_queryset(), get() and post() methods)
A single django view will normally handle both rendering the empty form AND processing the submitted form.
So the search page (both the form and the results), live at http://your-site.com/search. Your url conf is -
urlpatterns = patterns('',
#...
(r'^search/$', 'searchapp.views.search'),
)
And your view looks something like this -
def search(request):
if request.method == 'POST':
form = SearchForm(request.POST)
if form.is_valid():
my_query = form.cleaned_data['myquery']
object_list = YourModel.objects.filter(# some operation involving my_query)
return render_to_response('search_results.html', {'object_list': object_list})
else:
form = SearchForm()
render_to_response('search_form.html', {'form': form})
(Note I've assumed your form method is post rather than get - I know this isn't great http but it's a common pattern with django)
To respond to your questions -
Don't use your own method for cleaning data. Add a clean_myquery method to your form and access it with form.fields['myquery'].clean() (or if you've called is_valid() on your form, it's accessible with just form.cleaned_data['myquery']).
You want to try and avoid passing data for processing to the template. Do as much processing as you can in the view, then render the template. However if you want to pass myquery as a string for the template to render, then add it in to the context dictionary (the second non-key-word argument) in render_to_response -
return render_to_response('search.html', {'object_list': object_list, 'myquery': my query})
The post data is constructed from the form fields. You don't have a form field thequery. The view is processing the POST data - it's not creating it that's done by the html (which in turn is constructed by the Form class). Your variable thequery is declared in the view.
Django's URL dispatcher ignores query strings in the URL so http://your_site.com/ssearch will be processed by the same view as http://your_site.com/search?myquery=findstuff. Simply change the html in the template from <form method='post'> to and access the data in django with request.GET. (You'll need to change the code from the view I described above to include a new check to see whether you're dealing with a form submission or just rendering a blank form)
Have a good read of the docs on views, forms and the url dispatcher.

How to use django UserCreationForm correctly

I am new to Django and am just starting my first website. I am trying to set registration for new users.
I used the built in view for login and logout but there is none for registration, in the doc, it says that I should use built in form : UserCreationForm.
The code of my view is :
def register(request):
if request.method =='POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(form.cleaned_data['username'], None, form.cleaned_data['password1'])
user.save()
return render_to_response('QCM/index.html') # Redirect after POST
else:
form = UserCreationForm() # An unbound form
return render_to_response('register.html', {
'form': form,
},context_instance=RequestContext(request))
It works fine but I am not satisfied as this code is written in the views.py that handles the core of my application (multiple choice question).
My questions are :
Is this the correct way of using the UserCreationForm
Where could I put this code so it would be separated from the rest of
my app
Thank you for your answers.
Django is modular, so you can write a separate accounts or user management app that can handle user creation, management.
In that case, you put the code for register in the views.py of accounts app.
You can directly save the UserCreationForm which will give you user object.
example:
...
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
...

Categories