I have made a simple form inside a html file whose path is www.site.com/posts/5. Whenever the form is submitted, it redirects back to the same page i.e www.site.com/posts/5 displaying a message given by user in the form.
However, whenever the form is submitted it doesn't call the foobar view.
The urls.py, views.py and html files are as follows:-
urls.py
urlpatterns = [
path('posts/<int:foo>',user_views.display, name="display",
path('posts/<int:foo>',user_views.foobar, name="makefoo"),
]
views.py
def foobar(request, foo):
#do something
html file
<form name="fooform" action= "{% url 'makefoo' 5 %}" method = "post">
{% csrf_token %}
<input type="text" name="FOO_BODY" maxlength="300" required>
<input type="submit" value="comment">
<input type="reset" value="clear">
</form>
Edit : user_views is just from user import views as user_views
You can not attach two views to the same URL. The {% url ... %} template tag, only generates a URL for that path. But if there is a "url clash", then it is possible that the requests ends up in the other view.
You thus should define another URL, or encode the post logic in the display view. In case of a POST request, you can thus first take the necessary steps, and then for example return a redirect to the page, such that we can again render the page:
def display(request, foo):
if request.method == 'POST':
# do something
return redirect(display, foo=foo)
#do something else (original code)
return HttpResponse(..)
This is the famous Post/Redirect/Get web development design pattern [wiki]. This is usually better than returning a HTTP response directly in the POST, since if the user performs a refresh, the POST will be performed a second time.
As mentioned in the comment by #williem, you have two path() defined in the urls.py.
Always First matching route will be picked up from the url route table. So whenever r^'posts/' is requested it will call the display() from the user_views, so it will never go to foobar(). Either remove the route with display() or change the sequence. Also, I assume you imported the user_views.
Reference:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Related
I am trying to understand how a very regularly used code-form in Django views.py actually works. I see the following (or a variation) used a lot, but I can’t find a line-by-line explanation of how the code works – which I need if I am to use it confidently and modify when needed.
Can you let me know if I have understood how Django processes these various components? If not, please indicate where I have misunderstood.
I will start with the model then introduce urls.py the view and the form. I will go through the relevant parts of the code. I will consider:
The model:
#models.py
class CC_Questions(models.Model):
# defining choices in Model : https://docs.djangoproject.com/en/4.0/ref/models/fields/
personality = [
('IF','IF'),
('IT','IT'),
('EF','EF'),
('ET','ET'),
]
q_text = models.CharField('Question text', max_length=200)
#C1_Type = models.CharField('Choice 1 Type', max_length=2)
C1_Type = models.CharField(choices=personality, max_length=2)
Choice1_text = models.CharField('Choice 1 text', max_length=100)
#C2_Type = models.CharField('Choice 2 Type', max_length=2)
C2_Type = models.CharField(choices=personality, max_length=2)
Choice2_text = models.CharField('Choice 2 text', max_length=100)
#
def __str__(self):
return self.q_text[:20]
The url
#
#urls.py
app_name = ‘polls’
urlpatterns = [
…..
# ex: /polls/p2create
path('p2create/', p2_views.p2create, name='p2create'),
…
The view:
#views.py
from.forms import Anyform
#
def p2create(request):
if request.method == 'POST':
form = AnyForm(request.POST)
if form.is_valid():
form.save()
return redirect('/polls/p2')
else:
form = AnyForm()
context = {'form' : form}
return render(request, 'pollapp2/create.html', context)
#
The form:
#forms.py
#
….
from django import forms
from .models import ….. CC_Questions …
…….
class AnyForm(forms.ModelForm):
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
The template:
#
# Create.html
#
…..
{% load widget_tweaks %}
…..
<form method="POST">
{% csrf_token %}
…
<div class="row">
<div class="col-lg-5">
<div class="form-group">
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
<label for="C1_type">Type 1</label>
{% render_field form.C1_Type class="form-control" %}
…….
Does the code operate as follows?
The user enters URL in browser: http://localhost:8000/polls/p2create/
The urls.py picks the view to execute
path('p2create/', p2_views.p2create, name='p2create'),
views.py runs the view:
def p2create(request):
Now, as no form has yet been "identified" or "loaded" (??) the following test fails:
if request.method == 'POST':
so the Else clause executes
else:
form = AnyForm()
that "sets" the variable form to "AnyForm()"
The following line creates a dictionary, named context, that creates a key 'form' that is linked with the value form (=Anyform)
context = {'form' : form}
The following line searches for create.html in the template directory, passing the directory context as a parameter
return render(request, 'pollapp2/create.html', context)
Then template, create.html, displays various input boxes (??) from :
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
When the submit button is pressed on the displayed page, this "passes back" (??) the {% render_field .. %} values to the view (?????)
<form method="POST">
...
<div class="col-lg-4">
<button type="submit" class="btn btn-info">Submit</button>
</div>
...
</form>
the view is executed again (????) , but this time request method is set to "POST" because of the form method="POST" in the template (?????)
if request.method == 'POST':
Now the same form , AnyForm , is "reloaded" (????) but with the parameter value "POST"
if request.method == 'POST':
form = AnyForm(request.POST)
Now if the form is valid (I have no idea what a "valid" or "invalid" form is)
if form.is_valid():
then all the values "captured" in the template by (???)
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
(??????)
are written by the view (?????)
form.save
to the corresponding fields in the ModelForm (?????)
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
The view then redirects and loads the home page in the browser
return redirect('/polls/p2')
Ok, So with the help of the references (mentioned below) and the workflow suggested by you, let us first see, the Django MVT workflow and then address the various questions asked in between the post.
WebForms with Django MVT in brief:
Prepare data of several different types for display in a form.
Render data as HTML.
Edit, enter data using a convenient interface.
Validate and clean up the data.
Return data to the server.
Save, delete or pass on for further processing.
The URL:
When a user requests a page from your Django-powered site, this is the algorithm the system follows to determine which Python code to execute. Which is handled by our views.py. From the frontend, if the request is not 'POST', then it is a GET request, hence the else part of the handling function in the views.py executes. This you have mentioned already.
The View: - Form data sent back to a Django website is processed by a view, generally, the same view which published the form. This allows us to reuse some of the same logic. To handle the form we need to instantiate it in the view for the URL where we want it to be published.
If we use request.POST, as in this line:
form = AnyForm(request.POST)
It transforms the form into a bound form (Form bound with data). Read more about it here.
Questioned By You (QBY) - When the submit button is pressed on the displayed page, this "passes back" (??) the {% render_field .. %} values to the view (?????)
So, yes, If the action attribute is not mentioned in the form then the data will be passed to the view responsible for displaying the form.
QBY - the view is executed again (????), but this time request method is set to "POST" because of the form method="POST" in the template (?????)
The button type submit, submits the form and make the request a POST request. The Django template sends that submitted data in the request.POST.
QBY - Now the same form, AnyForm, is "reloaded" (????) but with the parameter value "POST"
Here, if the return method at the end of the POST condition is HttpResponseRedirect it will redirect the user to the mentioned URL page, but if the same HTML is used to be rendered then the form will be displayed as a bound form. (It depends upon the requirements)
QBY - Now if the form is valid (I have no idea what a "valid" or "invalid" form is)
Form.is_valid()
The primary task of a Form object is to validate data. With a bound Form instance, call the is_valid() method to run validation and return a boolean designating whether the data was valid. If yes, then the data is being saved in the model.
QBY - then all the values "captured" in the template by (???)
All the values are sent to views in the request.POST. We can check it by
print(request.POST)
QBY - are written by the view (?????), form.save to the corresponding fields in the ModelForm (?????)
Save method is called on the Django ModelForm instance in order to save the data to the database. Calling save would run the validation check. A ValueError will be raised if the data in the form doesn't validate.
This saved data can now be processed further.
References:
[https://docs.djangoproject.com/en/4.0/topics/forms/][2]
[https://www.tangowithdjango.com/book/chapters/models_templates.html][3]
[https://docs.djangoproject.com/en/4.0/ref/forms/api/][4]
been searching a lot for fixing my issue.. New to django and might be missing a very simple logic here and looking for some help..
I have created a form in html page called thispage.html as below:
<form action="{% url 'getvaluefromform' %}" method="POST">{% csrf_token %}
<input type="text" name='mytitle' placeholder="enter title">
<input type="submit" value="save">
</form>
then I updated views.py with the below code:
from django.shortcuts import render
def index(request):
return render(request,'thispage.html')
def getvaluefromform(request):
mytitle = request.POST.get('mytitle')
print(mytitle)
return render(request,'thispage.html')
finally my urls.py has this part:
from dhango.urls import path
from . import views
urlpatterns = [
path('',views.index,name='index'),
path('getvaluefromform',views.getvaluefromform,name='getvaluefromform')
]
Problem:
when I use this I am able to get the input vallue however the url is changing to '/getvaluefromform' and when I remove 'getvaluefromform' from the url section and just keep it '' then the code view index gets picked up.
Is there a way I can call the second function when button is clicked without moving to the new path. Please advise.
P.S - I am deliberately not using a model form because I want to build a raw sql query based on user input and then run the query to get the results/ create a new table on the database.
Django forms:
If you want to get POST data from a form without changing routes you have a very good example in the official documentation.
HTML forms:
If you're not into Django forms you can do as stated below.
def getvaluefromform(request):
if request.method == 'POST':
mytitle = request.POST.get('mytitle')
return render(request,'otherpage.html')
return render(request,'thispage.html')
What this will do is basically check if there's a POST request, get the form data and if there's a GET request, you'll just render the defined page.
If you must have two different routes, I suggest using Javascript.
I need some help with django
I have kind of landing page with form, which need to have 3 different states.
Flow looks like this: enter email -> check database for user with that email -> render login or register form, still in same template.
Code looks like this:
.html
// lots of code
<form method="POST" id="form">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="send">
</form>
// lots of code
and in views.py i have render method with context
return render(
request,
'apply.html',
context,
)
where in context i placed simple standard django form like:
class ApplyForm(forms.Form):
email = forms.CharField()
phone = forms.CharField()
Is there and way to change ApplyForm to LoginForm or RegisterForm after sending POST from first one, without refreshing whole page?
I though about injecting all three forms to context, sending some ajax request, and then differentiate which form should be displayed, but I'm not sure how could it be done...
thanks in advance!
I am trying to create a simple subscription form in the front page of my site. I created the view with a model form (the model contains only name and e-mail as attributes). When I go to the root address (GET) it works fine and loads the form. I then fill it with some data, click the submit button (the form action can either be set to '' or '/', the result is the same) and it redirects to the same root page, but it does not load anything, the page remains blank. In the console I can see it calling through POST method, but not even the first print of the view function gets printed.
Any ideas? I know it must be something silly, but I spent sometime in it and haven't yet found out what it could be.
In urls.py:
url(r'', FrontPage.as_view(template_name='rootsite/frontpage.html')),
In rootsite/views.py
class FrontPage(TemplateView):
'''
Front (index) page of the app, so that users can subscribe to
have create their own instance of the app
'''
template_name = 'rootsite/frontpage.html'
def get_context_data(self,
*args,
**kwargs):
c = {}
c.update(csrf(self.request))
print self.request.method
if self.request.method is 'POST':
print 'OK - POST IT IS, FINALLY'
form = NewUsersForm(self.request.POST)
print form.__dict__
if form.is_valid():
form.save()
return HttpResponseRedirect('/' + '?thanks=1')
else:
form = NewUsersForm()
return {'form':form}
You can't return a redirect from within get_context_data - it's for context data only, hence the name.
You should really be using a proper form view for this, which includes methods for redirecting after form validation.
Did you include a csrf_token in your template (as per the example here: http://www.djangobook.com/en/2.0/chapter07.html)?
<form action="" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Submit">
</form>
I could be wrong, but I thought Django wouldn't accept a POST request without a csrf token?
I'm trying to redirect users to custom url "/gallery/(username)/" after successfully logging in. It currently redirects to the default "/account/profile/" url While I know what I can override the redirect url in my settings.py, my url is dynamic thus it will not work.
Documentation states that I need to use the "next" parameter and context processors. I have the {{next}} in my template, but I'm confused on how to actually pass the "/gallery/(username)". Any help would be greatly appreciated.
p.s: I'm trying to steer away from writing my own login view.
Django's login view django.contrib.auth.views.login accepts a dictionary named extra_context. The values in the dictionary are directly passed to the template. So you can use that to set the next parameter. Once that is done, you can set a hidden field with name next and value {{ next }} so that it gets rendered in the template.
I confess I usually use 2 redirects in order to get something like this to work.
First, Make your own registration/login.html page. You can copy-and-paste the html example in this section of the authentication docs to make the process a little easier. Instead of using the dynamic '{{ next }} variable from the context, however, hardwire the value of next to go to a generic landing view of logged-in users
<input type="submit" value="login" />
<input type="hidden" name="next" value="/gallery/" />
Then, in the view that you map to the /gallery/ URL, extract the User object from the request (since the user will now be logged in, especially if the gallery view is wrapped in a #permission_required or #login_required decorator. Use that view to redirect to the appropriate user-specific gallery page:
#login_required
def gallery(request):
url = '/gallery/%s/' % request.user.username
return HttpResponseRedirect(url)
If you already have the custom template for login form you need to add the following inside your <form> tag:
<input type="hidden" name="next" value="{{next}}" />
BTW, you don't have to create your own login view. django.contrib.auth.views.login works fine. You only need to create a template for it (registration/login.html)
being an newbie to django and stumbling over this somewhat older thread i found a differing solution for the problem of dynamically (=override a custom default only if needed) setting the next-param that i'd like to share (working fine with django 1.5, earlier versions untested):
just as django-d i wanted avoid repetition and a custom login-view, so i used the stock django.contrib.auth.views.login-view by adding the line of
url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html',}, name='login'),
to my urls.py and within the login.html-templates form-element:
{% if not next or not next.strip %}
{# avoid django.contrib.auth.views.login s default of /account/profile/ #}
{% url 'afterlogindefaultview' as next %}
{% endif %}
<input type="hidden" name="next" value="{{ next }}" />
which to my understanding follows the decoupling-practice of the url-configurations from the views.
so for views that should redirect to my apps login and afterwards head to a non-default view
i use
return HttpResponseRedirect('%s?next=%s' % (reverse('login'), reverse('mycustomnext')) )
from the view where i want to have the user to log in. i use this to get back to the view where i left off for logging the user in.
You can use a static redirect to /loggedin/ and then associate the url to a view that makes the correct redirect.
Login takes an extra step but if you want to use django's view it does the job.
create your own view for logging in, with it's own url, don't use the admin's one.
you can store the next page in the session, or pass it as a GET parameter to the login view
(i.e. /login?next=gallery) just don't forget to sanitize and validate that value before redirecting to it.