I made a research here in Stack and my problem is the opposite of the majority, I saw some ways to make it appear, but my problem is that it's appearing when the user hits the "Register" button / Refresh the register page. So it's an annoying thing that appears wherever the user enter/refresh the page because the form is empty.
View.py
#unauthenticated_user
def register(request):
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
else:
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)
HTML
<form method="POST" action="" id="ativa">
{% csrf_token %}
...
</form>
{{form_u.errors}}
{{form_c.errors}}
<div class="mt-4">
<div class="d-flex justify-content-center links">
Have an account ? Login
</div>
</div>
Print
P.S: The site is in portuguese, but I can share the form link in heroku
Your logic is opposite of what you want:
Initialize the forms with POST data regardless of whether the request is a POST or a GET request, which will result in the errors if there is no POST data.
Then you initialize empty forms when the form data is invalid.
Instead you'll want to pass POST data only if the request is a POST request, and you should initialize empty forms only if the request is not a POST request:
#unauthenticated_user
def register(request):
# If request is POST, validate forms and add objects.
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
# We can remove the else statement here,
# because the function either redirects or resumes
# normal flow and renders the template
# with the form errors.
else:
# Only initialize empty forms when no POST request was made.
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)
Related
For the following model and route, how can I get the page to display the existing data in the database field, in an editable box, with a ‘save changes’ button.
# MODEL
class Task(db.Model): #inherits from db>Model
__tablename__ = "Tasks"
id = db.Column(db.Integer, primary_key=True)
# datetime, need to work this out
Title = db.Column(db.String(4096))
Status = db.Column(db.String(4096))
Description = db.Column(db.String(4096))
Priority = db.Column(db.String(4096))
Assigned_To = db.Column(db.String(4096))
# ROUTE:
#app.route("/<int:task_id>/edit")
def _edit(task_id):
task = Task.query.get_or_404(task_id)
return render_template('update.html',task=task)
<—- update.html—->
<form method="POST" action="">
<fieldset>
{{ task.Title }}
{{ task.Description }}
</fieldset>
</form>
I would prefer not to define Forms if possible. I have an SQL Alchemy database configured and working.
The routes added to the end of this question are working fine - adding new tasks (rows) to the database and creating individual pages to view individual tasks (rows):
#app.route("/tasks", methods=['GET', 'POST'])
def new_post():
if request.method == "GET":
return render_template("tasks.html")
data = Task(
Title=request.form["title"],
Description=request.form["description"],
Status=request.form["status"],
Priority=request.form["priority"],
Assigned_To=request.form["assigned"],
)
db.session.add(data)
db.session.commit()
return redirect(url_for('index'))
#app.route("/<int:task_id>")
def qtsk(task_id):
task = Task.query.get_or_404(task_id)
return render_template('indtask.html',task=task)
I’m new to flask, I would greatly appreciate any help.
I was watching a Flask tutorial recently and this is how he deals with this situation:
#app.route("/post/<int:post_id>/update", methods=["GET", "POST"])
#login_required
def update_post(post_id):
post = Post.query.get_or_404(post_id)
if (post.author != current_user):
abort(403)
form = PostForm()
if form.validate_on_submit():
post.title = form.title.data
post.content = form.content.data
db.session.commit()
flash('Your post has been updated!', 'success')
return redirect(url_for('post', post_id=post_id))
elif request.method == 'GET':
form.title.data = post.title
form.content.data = post.content
return render_template('create_post.html', title="Update Post",
form=form, legend='Update Post')
What it does is it checks if the user is submitting the updated title or content using POST method. If the user is then just update the field and then commit it. Otherwise return the template with the title and content field in the form being filled with the current fields in the Post object.
Here's link for that tutorial (this part starts at 25:35): https://youtu.be/u0oDDZrDz9U
Hope it helps.
I was asking a simillar question.
I think you can update by using form. It doesnt have to be presented as a form to the user. You can write an html template that would not lokk like a form,but youneed a form to fetch data. Maybe you can use javascript and than you can skip the usage of form. I am on to remember javascrip an use it for my app.
I'm creating a video sharing application. On my video page, I have allowed users to post comments and to delete comments.
def video_content(request, video_id):
video = get_object_or_404(Video, pk=video_id)
....
return render(
request,
'video-content.html',
context={
'video': video,
}
)
I'm obviously omitting a lot of things in the code.
I also have a comment handler function
def add_comment(request, video_id):
video = get_object_or_404(Video, pk=video_id)
if request.method == 'POST' and request.user.is_authenticated:
# Get comment and save it
return HttpResponse()
On my video page, I have a form:
<form action="/comment/add/{{video.id}}" method="post">
<input type="text"></input>
<button type="submit">Comment</button>
</form>
All of this works fine. When the user inputs a comment and submits the form, the add_comment function is successfully called just like its supposed to, and the comment is saved. The video page does not reload, which is what I want, but the URL on the top bar changes. How can I prevent that from happening?
All you need to do, is redirect the user to the view that you want.
def add_comment(request, video_id):
video = get_object_or_404(Video, pk=video_id)
if request.method == 'POST' and request.user.is_authenticated:
# Get comment and save it
return redirect("video_content",video.id)
# return HttpResponse() # this, is not correct
return render(request,"template_name.html",{})
I'm working on a blog build on django and doing the comment stuff and I would like to build it from scratch here my views function:
def topic_detail(request, slug):
topic = get_object_or_404(Topic, slug=slug)
form = CommentForm()
if request.method == 'POST':
if request.user.is_authenticated:
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.topic = topic
comment.created_by = request.user
comment.save()
return redirect('board:topic_detail', slug=topic.slug)
else:
redirect('accounts:login')
else:
form = CommentForm()
return render(request, 'topic.html', {'topic':topic, 'form':form})
my page layout would be:
< ............................>
Something I want to write
<.............................>
Comment Field
<.............................>
Comments
So when user presses the button, server will check if that user is authenticated. If yes comment is updated, If no user will be directed to login views. Here the problem, when I'm logged in everything works fine, but when I log out test the views, It does not redirect me to the login views but just reload the page. I would appreciate if you help me.
Thanks!
You should use return redirect(....) instead of just redirect(...) to return the actual HttpResponse. Now your code continues to the last line and renders the same page again.
I have multiple forms to be shown everywhere in my project and hence I read that having a context_processor was the best way to do it. So, I created one inside my app and it looks something like this:
def forms_processor(request):
name_form = NewNameForm()
work_form = NewWorkForm()
address_form = NewAddressForm()
context = {'name_form': name_form,
'work_form': work_form,
'address_form': work_form,
}
return context
This works great, I can just use {{name_form}} anywhere in my templates and that renders the form.
Now my question is, where do I validate the form? In my views.py or the context_processors.py? Right now my views for name_form looks something like this:
def user_profile(request):
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
form.save()
else:
ctx = {'title': 'Profile', 'active_tab': 'Profile'}
return render (request, 'user_profile.html', ctx)
This isn't working actually, if I submit an invalid form, it just comes back to the same page and won't show a populated form.
If someone could guide me or redirect me to some docs on this topic, that'd be awesome! Thanks!
The problem is that your processor instantiates the form on each render. Each time you call render, your processor is called, which instantiates a new form and displays THAT form, not the form instance that you created in the view. Therefore, the form being rendered is a blank instance but the form that contains the input and errors was destroyed by garbage collection after finishing your view.
A way I would do this, is passing the form you create in the view back to context before rendering. Pass it in to a context key such as "name_form_filled". Then if that variable is present in the context, don't render "name_form", instead render "name_form_filled".
views.py
def user_profile(request):
ctx = {}
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
name_form.save() # you named this named_form, not form.
# If you want to redirect to another view when the form is saved successfuly, do it here.
else:
ctx["name_form_filled"] = form
else:
ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
return render (request, 'user_profile.html', ctx)
user_profile.html
<div id="form_container">
{% if name_form_filled %}
<!-- Render form that has input and errors from previous POST. -->
{{ name_form_filled }}
{% else %}
<!-- render empty initial form. User has not attempted to submit yet. -->
{{ name_form }}
{% endif %}
</div>
===========================================================================
Another way you could do this is turn this view into a class based view and inherit a base class based view. This base class will override the get_context_data method and add your three forms. Note that you won't be using the context processor with this methodology so you could get rid of it if wanted in this case.
All views that use your form will extend the base view class. Then, after evaluating your form, if it is invalid, overwrite your name_form context key with the invalid form instance, which will be in your context.
views.py
class BaseView(View):
def get_context_data(self, *args, **kwargs):
context = {
"name_form": NewNameForm(),
"work_form": NewWorkForm(),
"address_form": NewAddressForm()
}
return context
class UserProfileView(BaseView):
def get(self, request, *args, **kwargs):
# Do GET logic here.
ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
return render (request, 'user_profile.html', ctx)
def post(self, request, *args, **kwargs):
# Do POST logic here.
ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
name_form = NewNameForm(request.POST)
if name_form.is_valid():
name_form.save()
else:
ctx["name_form"] = name_form # will replace the empty form in context with the form instance created in name_form that has input and errors.
return render (request, 'user_profile.html', ctx)
user_profile.html
<div id="form_container">
<!-- Will render whatever is in name_form. If this is after the
user has submitted an invalid form, this form will be populated with input and errors because we overwrote it in the view. -->
{{ name_form }}
</div>
===========================================================================
I personally think that the first solution is the best but when you start getting more complex, you should probably switch over to the second solution as class based views make complex views way easier.
Direct answer: you validate the form in views.py with is_valid() method. What you need is to populate context with bound form if the form is invalid:
def user_profile(request):
ctx = {'title': 'Profile', 'active_tab': 'Profile'}
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
form.save()
return redirect(YOUR_REDIRECT_URL) # Always redirect after successful POST
ctx['form'] = form # if form is invalid return it with context
return render (request, 'user_profile.html', ctx)
Read more in documentation.
I have a very simple index page view, from which the user can fill in a login popup, which sends a post request to /login
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
context['login_form'] = LoginForm()
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
If the login is valid it simply redirects to the index page, this works fine.
The login view looks as follows:
def user_login(request):
form = LoginForm(request.POST)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context all over again
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
However when the login is incorrect I want to be able to refresh the page and show the login form errors inside the index template (in the login popup)
I'm actually able to achieve this with the above code, but I'm unhappy with the solution for the following reasons:
I have to manually fetch the context all over again, e.g user/student forms and studyspaces, this goes against the DRY principle
When the page is refreshed the url is localhost:8000/spacefinder/login
Screenshot of behaviour here
I'm wondering if there's somehow a way to use redirect to reload the index page and somehow pass errors from my login_form, e.g. something like:
return redirect('spacefinder:index', {'login_form': form})
I've looked into using messages to pass form validation errors, but struggled to get this working since Validation Errors are thrown inside forms.py, and I'm unable to fetch the request instance from inside a ModalForm to properly create a message
You are doing it the wrong way around.
Consider these prerequisites:
entry point to your page is the index view
the index view must only be accessible by authenticated users
the login view allows both methods GET and POST and is accessible to anonymous users only
The reason to use Django is to make use of all the features that it offers, and that includes handling of the above (because that is what most pages need, not only you).
To set it up correctly you need to define your urls.py like this:
from django.contrib.auth.decorators import login_required
urlpatterns = [
....
url('^login/$', user_login, 'login'),
url('^/$', login_required(index), 'index'),
....
]
In your settings/base.py (or settings.py if you have no environment differentiation) tell Django how to redirect users:
LOGIN_URL = reverse_lazy('login')
LOGIN_REDIRECT_URL = reverse_lazy('index')
https://docs.djangoproject.com/en/1.9/ref/settings/#login-url
https://docs.djangoproject.com/en/1.9/ref/settings/#login-redirect-url
Simplify your index view:
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
return HttpResponseForbidden() # prevented by Django, should never happen
return render(request, 'spacefinder/index.html', context)
Let the user_login view deliver the empty login form:
#require_http_methods(["GET", "POST"])
def user_login(request):
params = getattr(request, request.method)
form = LoginForm(params)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context for new form or form with errors
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
You have not presented any code that handles the UserForm or the StudendForm. You would need to add that to the user_login view, as well - if this is something that all users should fill in every time they login. Otherwise use a different view.
It's worth looking at modules like allauth. They might spare you some work when it comes to allowing users to login with their e-mail addresses, ascertain that e-mail addresses are unique in the system etc.