I am developing a web-site using Django/Python. I am quite new to this technology and I want to do the web-site in a right way.
So here is my problem:
Imagine, that there is a Product entity and product view to display the Product info.
I use (product_view in my views.py ).
There is also Message entity and the Product might have multiple of them.
In Product view page ( I use "product_view" action in my views.py ) I also query for the messages and display them.
Now, there should be a form to submit a new message ( in product view page ).
Question #1: what action name should form have ( Django way, I do understand I might assign whatever action I want )?
Option #1: it might be the same action "product_view". In product_view logic I might check for the HTTP method ( get or post ) and handle form submit or just get request. But it feels a bit controversial for me to submit a message to the "product_view" action.
Option #2: create an action named "product_view_message_save". ( I don't want to create just "message_save", because there might be multiple ways to submit a message ). So I handle the logic there and then I make a redirect to product_view. Now the fun part is: if the form is invalid, I try to put this form to the session, make the redirect to the "product_view", get the form there and display an error near the message field. However, the form in Django is not serializable. I can find a workaround, but it just doesn't feel right again.
What would you say?
Any help/advice would be highly appreciated!
Best Regards,
Maksim
You could use either option.
Option #1: In the post method (if using Class-based-views, otherwise check for "post" as the request type), just instantiate the form with MessageForm(request.POST), and then check the form's is_valid() method. If the form is valid, save the Message object and redirect back to the same view using HttpResponseRedirect within the if form.is_valid(): code block.
If you're checking for the related Messages objects in your template, the newly created message should be there.
Option #2: Very similar to Option #1, except if the form is not valid, re-render the same template that is used for the product_view with the non-valid form instance included in the template context.
Related
I'm working on a Django project and to make the forms experience far smoother I want to spread a ModelForm across a few pages. It would be ideal if users who are logged in can save their progress in the form without actually posting the content (in this case, a JobApplication where users can come back to filling in info without actually sending off the application).
Currently I have looked at other answers on SO such as this one; but this only shows me how to utilise caching to store information between the views where the form is present.
Models.py (models, forms and views have been simplified for readability):
class JobApplication(models.Model):
job = models.ForeignKey(JobPost,on_delete=models.SET_NULL,...)
user = models.ForeignKey(AUTH_USER_MODEL,...)
details = models.CharField(max_length=300)
skills = models.CharField(max_length=300)
feedback = models.CharField(max_length=300)
#... [insert more fields] ...
Forms.py:
class Application(forms.ModelForm):
details = forms.CharField() # To go in page 1 of the form process
skills = forms.CharField() # To go in page 2
feedback = forms.CharField() # To go in page 3
class Meta:
model = JobApplication
fields = ['details','skills','feedback']
Views.py:
from . import forms
def view1(request):
form = forms.Application()
if request.method == 'POST':
form = forms.Application(data=request.POST)
... some logic here which I am not sure of ...
return render(request, 'view1.html', {})
def view2(request):
form = forms.Application()
if request.method == 'POST':
form = forms.Application(data=request.POST)
...
return render(request, 'view2.html', {})
def view3(request):
form = forms.Application()
if request.method == 'POST':
form = forms.Application(data=request.POST)
...
return render(request, 'view3.html', {})
Note that I'm happy to edit my forms or models to achieve this multi-page, save progress effect that you may see on job sites.
Let me know if there's any more code I can add that will be useful, since I'm not too sure what else will be required.
Thanks!
I had a similar use case in my application what I did was created
multiple forms out the models and a central view controlling the
progress of the form.
The view has a list of forms it has to propagate through
GET : /form/<index> => form/0
POST : Save data to the form
Initially the form will have no initial data, for index > 0 the
initial data will be the previously saved model object
When user clicks on next increment the URL index counter, Decrease
it for prev, Don't save anything on skip
Here is a gist of how it would look.
https://gist.github.com/bhavaniravi/b784c57ae9d24fce359ae44e2e90b8e3
I don't know if this is the best optimized method of all times but this is what I did. Any suggestions on improvement is most welcomed
You will need a Form for each action you need. With it on hands, you can use a feature from Django 1.7 called Form Wizard (Yes, it is built in), the best way achieving this is using Class-Based Views, that is way more flexible, clean and cohesive than FBV in this case.
https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#
Basically you will define a list of steps and forms, both tied to the same URL. You can use customized templates for each form:
https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#using-a-different-template-for-each-form
[EDITED]
As jayt said in comments, the formtools was deprecated since version 1.8 and is now apart from core package and can be found in https://github.com/django/django-formtools/
Good luck. =)
I'm working on a Django project and to make the forms experience far
smoother I want to spread a ModelForm across a few pages. It would be
ideal if users who are logged in can save their progress in the form
without actually posting the content (in this case, a JobApplication
where users can come back to filling in info without actually sending
off the application).
You are mixing UI/UX concepts with technical implementation here.
Your overall process is a job application where you have these states:
create a new applicant (user) if not yet done
create a new application for that user
work on that application until the user says they are done (allowed to span multiple (browser) sessions)
mark application as done -> actually apply to the job
How the data for the application is collected (in whatever forms or such) and "submitted" in a web development sense is independent of the actual job application action - it just need to happen beforehand.
So rather than just use one single Django form, you have these possibilities:
(A) Create smaller models that represent a certain content section of the form and that should get their own form. Create ModelForms for those and make them accessible in their own views.
(B) Stick with the single model as you have now but create custom Django forms for each of the separate pages as you have planned. You can still use ModelForm and model=JobApplication but each form has to specify the set of fields it covers and validate only these fields.
Progress Tracking
(A) or (B): You could track how much information has been input (in percent, e.g. by simply counting all fields and all non-empty fields and calculating their percentage.
(A): With different django models you could add a last modified timestamp to each model and show if and when this section was edited.
Main Page
In an overview page you could collect all the data for the user so that they can see what the application will look like (to someone else maybe even) - and there they can also click a button "apply to the job!", or they can open each specific form if they see that any of the data is not yet complete.
Actual Job Application
Once the user clicks on "apply to the job" a different POST happens that for example sets a datetime field on the JobApplication model. This way you can identify all applications that have reached this process step.
Same with any other steps that happen after that. Maybe the user can create another application by copying an existing one. Or their accepted status is also entered in that system and they can log in and check that. Or the system sends out email notifications or similar. Just add fields for anything that is of interest, then you can also reflect that in the UI if you want to.
I'm currently learning django from the 'How to tango with django' site and i'm unable to understand the chapter dealing with forms.
Appreciate it if someone would help me out.
http://www.tangowithdjango.com/book17/chapters/forms.html
the first step is to create a forms page which maps to models.py. I seem to understand this part. I also understand that we create
a view to process the data acquired from these forms. I'm not able to understand the below code in the views page.
from rango.forms import CategoryForm
def add_category(request):
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)
else:
# If the request was not a POST, display the form to enter details.
form = CategoryForm()
How do the urlmapper know that a request method is POST or GET before the user actually enters any data in the form?
On a similar note, when would a form have a get method?
form = CategoryForm(request.POST) - would someone explain this to me? CategoryForm looks to be a class which is already inheriting from another class
what does the request.POST argument convey ?
1) The urlmapper does by default not care about GET or POST request method. It will route any request to the given view-function.
Normally, your form html-code will look like this:
<form method="post" action="some_url">
...
</form>
So, when you submit the form, the data will be send to some_url with the specified method, in this case post.
You may want to read something about when to use GET or POST, normally forms are transferred using POST.
2) form = CategoryForm(request.POST) will bind the values provided in the request's POST-dictionary to the form. You may say, it prepopulates this. This way, further working with the form (like validating it by calling form.is_valid()) will be made possible.
Perhaps you should investigate further on Django forms and modelforms by reading some official documentation.
Why do you think the URL mapper knows if it's a post it a get? It doesn't, and it doesn't care.
The thing you are missing is that this view has two responsibilities: showing the initial form (on GET) and processing the submitted form (on POST).
Your second question shows an unfamiliarity with basic Python syntax. request.POST is the parameter to the initialisation of the form instance.
I've been developing a django project for 1 month. So I'm new at Django. My current problem with Django is; When I have multiple forms in one page and the page is submitted for a form, the other forms field values are lost. Because they are not posted.
I've found a solution for this problem;
When there is get method, I send the other forms value with the page url and I can handle them from the get request.
When there is post method, I keep the others form fields value in
hidden inputs in HTML side in the form which is posted. Hence I
can handle it from the post request.
Maybe I can keep them in session object. But it may not be good to keep them for whole time which the user logg in. But I dont know. I may have to use this method.
Is there another way which is more effective to keep all forms fields in Django?
Any Suggestion?
Thank!
You can make use of AJAX for a single form submission instead of whole page submit.
In a previous question, I was trying to figure out the right strategy for to passing data between forms in Pyramid. Based on the answer I received, I decided the approach of using a hidden form.
I started implementing this and think there must be a better way of passing along the data. Specifically, passing parameters through the url results in a tuple that is messy to parse.
I want it to be general enough to not to know what parameters the form has and also it needs to handle file fields as well.
How I'm currently attempting to pass the form data to the confirmation page:
#view_config(renderer="templates/derived/load/error.mak", route_name='process_model_route')
def process_model(self):
#processing logic and validaton, failiure in validation sends user to error.mak
return HTTPFound(route_url('confirm_model_route', self.request, fparams=self.request.POST))
Route: config.add_route('confirm_model_route', 'rnd2/model/confirm/*fparams')
#view_config(renderer="templates/derived/confirm/model.mak", route_name='confirm_model_route')
def confirm_model(self):
form_dict = self.request.matchdict['fparams']
#need to decode and pass to template
return dict({'load_route':load_route, 'form_dict':form_dict})
The confirm/model.mak template would contain the hidden form.
The idea with this method is:
Client visits page.
Server renders the form.
Client fills in form and POSTs to URL.
Server renders a new page that contains a hidden form with all of the data it just received in the POST.
Client POSTs to a URL, confirming the submission.
Server persists the data from the hidden form and redirects.
Now depending on usability, it's up to you to decide how many different URLs you actually want here and how many views in Pyramid. You have to think about what happens with invalid data?
Notice in the outline above, once the user POSTs the form to a URL, that URL must return the confirmation page containing a hidden form. If you try to redirect the user to a confirmation page instead, you must persist the data somehow, either in a session or through the hack you showed in your example (shoving all of the data into the GET). The second solution is very bad because it abuses the true purpose of GET in HTTP.
There is also the convention that every POST should result in a redirect to avoid a client submitting the form multiple times. With this in mind you might consider the simple solution of rejecting POSTs that do not have a "confirmed" flag and simply setting the "confirmed" flag in javascript after prompting the user. This allows you to keep your form handling logic simple.
If you don't want to rely on javascript and you don't want to persist the form data in a session, then you run into the issue of not redirecting after the first POST but other than that it should be simple from the outline above.
Is there anyway to pass context variables to a redirect response? I want to redirect a user to a success page after they submit a form, but I don't want the success page to be just a static html file. I need to display extra information based on the form data.
I have looked at this question, but the solution presented there simply renders a different file at the same url. I'd like to redirect the user so that hitting refresh at the page won't submit duplicate entries into the application.
Right now the only thing I have been able to use with some success is redirecting to a url while passing it GET variables as described here. That just seems like a bit of a hack, and was just wondering if there is any better solution...
Thank You
The way I see it you have three options:
Use GET variables in the redirect.
Store something in the session.
If you are creating an object using the form that was submitted, put the id of that object in the redirect url and use it in the new view.
The limitation you are running up against is that http is stateless, not something inherent in django.
How about storing your values in a session, then have the redirected page pick up the values from there?