The form to change email/profile pictures doesn't appear? - javascript

Any guidance on this issue will be greatly appreciated. I entered the necessary fields so that on the profile page the user would be able to edit the profile picture and the username and email. when I created the form, it only displayed the update button and the "Profile Info" text. How can fix this so that the form to edit the user information appears?
in views.py
'''
#login_required
def profile(response):
if response.method == "POST":
u_form = UserUpdateForm(response.POST, instance=response.user)
p_form = ProfileUpdateForm(response.POST, response.FILES, instance=response.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(response, f'Your account has been updated!')
return redirect("/profile")
else:
u_form = UserUpdateForm(instance=response.user)
p_form = ProfileUpdateForm(instance=response.user.profile)
args = {}
args['u_form']= u_form,
args['p_form']= p_form
return render(response, 'register/profile.html')
'''
in forms.py
'''
from django import forms
#from django.contrib.auth. import login, authenticate
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from django.contrib.auth.models import User
from .models import Profile
from django.forms import ModelForm
class RegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ["username", "email"]
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
'''
in profile.html
{% extends "register/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.username }}</h2>
<p class="text-secondary">{{ user.email }}</p>
</div>
</div>
<div class="container">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Info</legend>
{{ u_form|crispy }}
{{ p_form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update</button>
</div>
</form>
</div>
{% endblock content %}

To render some variable in the template it must be passed as the context to the template. In the line:
return render(response, 'register/profile.html')
You never pass it the context. render accepts the optional third argument as the context. So the line should be:
return render(response, 'register/profile.html', args)
Note: You accept the request parameter in the view as response! This is a great misnomer! The request is what the user makes to the server and the response is what the server returns. You should accept the parameter as request instead.

Related

Flask - contact form sending email on submit, but not redirecting and bootstrap btn not showing

from flask_mail import Mail, Message
app.config['MAIL_SERVER'] = 'xxxx'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'xxxx'
app.config['MAIL_PASSWORD'] = 'xxxx'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_DEFAULT_SENDER'] = 'xxx'
mail = Mail(app)
app.config['SECRET_KEY'] = 'xxx'
# Contact Form
class ContactForm(FlaskForm):
name =StringField("Name",validators=[DataRequired()])
email = StringField("Email",validators=[DataRequired(), Email()])
message = TextAreaField("Message",validators=[DataRequired()])
submit = SubmitField('Send')
#app.route('/', methods=['GET', 'POST'])
def home():
form = ContactForm()
if form.validate_on_submit() == False:
return render_template('home2.html',form=form)
else:
msg = Message('WEBSITE CONTACT', sender='xxx', recipients=['xxxx'])
msg.body = '%s \n %s \n\n %s ' % (form.name.data, form.email.data, form.message.data)
mail.send(msg)
return redirect(url_for('form_complete'))
return render_template('home2.html',form=form)
#app.route('/complete')
def form_complete():
return render_template('form_submitted.html')
The contact form successfully sends the email, however it does not redirect to form_complete.
I would like, after the contact form has been submitted (via a bootstrap button), for an email to be sent and a redirect to form_submitted.html
Furthermore, if I change the class in the HTML, the Send text just disappears:
...
<div class="container">
<h3 class="big text-center" data-aos="fade-down" data-aos-delay="0">
Contact
</h3>
<form method="POST" action ="">
{{ form.hidden_tag() }}
{% for message in form.name.errors %}
<div class="flash"><br><br>{{ message }}<br><br></div>
{% endfor %}
{{ form.name.label(class="form-control-label") }}
{{ form.name(class="form-control form-control-lg") }}
{% for message in form.email.errors %}
<div class="flash"><br><br>{{ message }}<br><br></div>
{% endfor %}
{{ form.email.label(class="form-control-label") }}
{{ form.email(class="form-control form-control-lg") }}
{% for message in form.message.errors %}
<div class="flash"><br><br>{{ message }}<br><br></div>
{% endfor %}
{{ form.message.label(class="form-control-label") }}
{{ form.message(class="form-control form-control-lg") }}
<!--{# {{ form.submit(class="btn btn-outline-info") }} #}-->
{{ form.submit() }}
</form>
</div>
...
edit:
When the submit button is pressed the contact form correctly sends to my email, though it doesn't redirect as I would like.
There is a script.js containing some remnant php form info - but removing this from script.js has not changed anything.
Github:
https://github.com/olbliss/OB_Flask
I found the issue, It was Caused By script.js.
in static/script.js Comment Out or just delete The Line 170
$("form:not(.SFG)").submit(function(event){
// event.preventDefault();
var form = $(this);
var grecaptchaContainer = document.getElementById("g-recaptcha");
if($(grecaptchaContainer).length>0){
Comment Out or just delete event.preventDefault(); and everything will work.
In Bootstrap Button, you have naming Conflincts between Bootstrap.css and framework.css and framework.min.css Because there are Class.btn which affects Bootstrap class .btn that's why your Bootstrap Button Doesn't appear You need to change naming of that .btn class in static\framework.css and static\framework.min.css

Populate text fields on select field update in Flask / WTForms

Folks, scratching my head at this, there's a kind of an answer to this here, but having difficulty implementing it.
I currently have a recipe and styles table, and when submit an "add recipe" form, it copies data from the styles table into the recipe. What I would like to do is to select a style in the add recipe form and have this data populate form fields. So I'd like style-type for example to be populated in the form on updating the style select dropdown.
My set up:
Routes:
#app.route('/recipe/new', methods=['GET', 'POST'])
#login_required
def addrecipe():
form = RecipeForm()
if form.validate_on_submit():
recipe = Recipe(recipe_name=form.recipe_name.data,
recipe_style=form.style.data.id,
style_name=form.style.data.name,
style_type = form.style.data.type)
db.session.add(recipe)
db.session.commit()
flash('You added your recipe, get brewing!', 'success')
return redirect(url_for('recipes'))
return render_template('add_recipe.html', title = 'Add Recipe', form=form, legend='Add Recipe')
Models:
class Recipe(db.Model):
id = db.Column(db.Integer, primary_key=True)
recipe_name = db.Column(db.String(100), nullable=False)
recipe_style = db.Column(db.Text, db.ForeignKey('styles.id'))
style_name = db.Column(db.String(100))
style_type = db.Column(db.String(100))
# used for query_factory
def getStyles():
return Styles.query.order_by(Styles.name.asc())
Forms:
class RecipeForm(FlaskForm):
recipe_name = StringField('Recipe Name', validators=[DataRequired(), Length(min=2, max=20)])
style = QuerySelectField(query_factory=getStyles,
get_label="name")
style_type = StringField('Style Type')
The Form HTML:
<form method="POST" action="">
{{ form.hidden_tag() }}
<legend class="border-bottom mb-4">{{ legend }}</legend>
<fieldset class="form-group card p-3 bg-light">
<h5 class="card-title">Overview</h5>
<div class="form-row">
<div class="form-group col-md-3">
{{ form.recipe_name.label(class="form-control-label") }}
{% if form.recipe_name.errors %}
{{ form.recipe_name(class="form-control form-control-sm is-invalid") }}
<div class="invalid-feedback">
{% for error in form.recipe_name.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.recipe_name(class="form-control form-control-sm") }}
{% endif %}
</div>
</fieldset>
<fieldset class="form-group card p-3 bg-light">
<h5 class="card-title">Style</h5>
<div class="form-row">
<div class="form-group col-md-3">
{{ form.style.label(class="form-control-label") }}
<input class="form-control form-control-sm" type="text" placeholder="Search Styles" id="myInput" onkeyup="filterFunction()">
{% if form.style.errors %}
{{ form.style(class="form-control form-control-sm is-invalid") }}
<div class="invalid-feedback">
{% for error in form.style.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.style(class="form-control form-control-sm", id="style_name") }}
{% endif %}
</div>
<div class="form-group col-md-2">
{{ form.style_type.label(class="form-control-label") }}
{% if form.style_type.errors %}
{{ form.style_type(class="form-control form-control-sm is-invalid") }}
<div class="invalid-feedback">
{% for error in form.style_type.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.style_type(class="form-control form-control-sm", id="styletype", style_type_tag='{{ form.style.data.type }}' ) }}
{% endif %}
</div>
</div>
</fieldset>
My Javascript so far:
style_name.oninput = function(o) {
// style = document.getElementById('styletype')
styletype.value = $(o).attr('style_type_tag')
}
I can get some basic stuff working with the JS function. So when I update the dropdown, it'll populate the field with some text. What I can't figure out is how to pull the style_type info from the database. The link at the top here loads that info into the html tags of the text box, but it's a little different to what I'm doing. The poster has looped through some items and it isn't a form. My style_type_tag is just showing up as the raw text. I'm guessing that the loop here is crucial but I can't quite make the step to getting into my setup.
Any help much appreciated!
So the answer to this was building a simple API. I'm sure there are simpler methods, but I wanted a bit of practice here and thought it would be useful in building other features into the project.
I followed Brad Traversy's vid on the subject and used the GET section to make this. His project was a simple single file project, so I had to get a bit more involved with imports etc in my project.
Grab postman to interact with the API
Install Marshmallow, To requirements.txt, add the lines:
flask-marshmallow
marshmallow-sqlalchemy
then run
pip install -r requirements.txt
Import and initialise marshmallow. In init.py:
from flask_marshmallow import Marshmallow
ma = Marshmallow(app)
Add style schema To models.py
# import module
from flaskblog import ma
# create schema with the fields required
class StyleSchema(ma.Schema):
class Meta:
fields = ("id", "name", "origin", "type")
# initialise single style schema
style_schema = StyleSchema()
# initialise multiple style schema
styles_schema = StyleSchema(many=True)
Note that the strict=True isn't necessary any more with Marshmallow.
Create endpoints/routes, to routes.py:
# Get All Styles
#app.route('/styleget', methods=['GET'])
def styles_get():
all_styles = Styles.query.all()
result = styles_schema.dump(all_styles)
# return jsonify(result.data) - note that this line is different to the video, was mentioned in the comments. Was originally "return jsonify(result.data)"
return styles_schema.jsonify(all_styles)
# Get Single Product
# passes the id into the URL
#app.route('/styleget/<id>', methods=['GET'])
def style_get(id):
style = Styles.query.get(id)
return style_schema.jsonify(style)
Update JS script:
style_name.onchange = function() {
// creates a variable from the dropdown
style = document.getElementById('style_name').value
// uses the variable to call the API and populate the other form fields
fetch('/styleget/' + style).then(function(response) {
response.json().then(function(data) {
// this takes the 'type' data from the JSON and adds it to the styleType variable
styleType = data.type;
// adds the data from the variable to the form field using the ID of the form.
styletype.value = styleType
});
});
}
Hope this helps anyone who runs into the same challenge!

My html button doesn't update data in database (Flaskapp), what is wrong with my code?

So I am creating and working around forms in my web app. I have a profile page for all users (displaying their first name, last name, email, etc). I am trying to add an 'edit' button to it and once they click on it, they are taken to the update form page where they type in the new details and click on the update button and once they do so, they are taken back to their profile page but with the new details they have entered this time (a simple edit button)
my forms.py class for the update form is:
class Updateform(FlaskForm):
email = StringField("Email", validators=[DataRequired(), Email()])
fname = StringField("First Name", validators=[DataRequired(), Length(min=2, max=20)])
lname = StringField("Last Name", validators=[DataRequired(), Length(min=2, max=20)])
phoneno = StringField("Phone Number", validators=[DataRequired()])
submit = SubmitField("Update account")
my route to the html is:
#auth.route("/updateform", methods=["GET", "POST"])
def updateform():
form = Updateform()
if(form.validate_on_submit()):
user: User = User.select().where(User.email == form.email.data).first()
user.email = form.email.data
user.fname = form.fname.data
user.lname = form.lname.data
user.phoneno = form.phoneno
user.save()
flash('Your account has been updated', 'success')
return redirect (url_for("user.profile"))
return render_template("user/formupdate.html", form=form)
and here is the template for the update form((formupdate.html)):
{% extends "admin-layout.html" %}
{% block content %}
<div>
{% from "_formhelpers.html" import render_field %}
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="font-weight-bold text-primary">UpdateForm</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-sm-6 col-md-8">
<div>
{{ form.hidden_tag()}}
{{ render_field(form.fname, class="form-control")}}
{{ render_field(form.lname, class="form-control")}}
{{ render_field(form.email, class="form-control")}}
{{ render_field(form.phoneno, class="form-control")}}
Update form
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
My problem is that when I click on the update button after entering new details, nothing changes - the page goes back to the profile page as it is supposed to, but the details are still the same and none of the new details are visible anywhere.
What am I doing wrong and how do I fix it?
Check your implementation of user.save() for a call to commit the update to the database. Here is one of my routes for comparison, watch out for db.session.commit():
from flask import render_template, flash, redirect, url_for
from flask_login import login_required
from app import db
from app.artists import bp
from app.artists.forms import ArtistForm
from app.main.forms import EmptyForm
from app.models import Artist
#bp.route('/', methods=['GET', 'POST'])
#login_required
def overview():
create_form = ArtistForm()
delete_form = EmptyForm()
if create_form.validate_on_submit():
artist = Artist(name=create_form.name.data)
artist.save()
db.session.add(artist)
db.session.commit() # <-- persist data in database
flash(f'Created artist {create_form.name.data}.')
return redirect(url_for('artists.overview'))
return render_template('artists/overview.html', artists=Artist.query.all(), create_form=create_form, delete_form=delete_form)

How to update a single field from modal form in Django?

I have a form that keeps track of when people enter/leave different areas. Whenever there is a discrepancy, for example, someone forgets to "leave" an area before entering a new one, the user is prompted an "estimate" of the time they believe they left the previous area at(edited_timestamp). The only two required fields on the main form are the employee number and the work area, as these are used to verify/keep track of data.
When I try to reproduce the situation that would make the modal show up, it works, but when I attempt to submit it, I get these messages:
and these are the errors that are being returned.
Now, while I don't understand why the "Enter valid date/time" error is showing, I'm guessing the other two errors are due to the main form requiring the employee_number and the work_area and probably for this request, even though it is updating by the ID, it still wants the other two fields.
I guess my question is, how could I modify this so that these two fields are not required for the modal?
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False, help_text="Work Area", related_name="work_area")
station_number = models.ForeignKey(Station, on_delete=models.SET_NULL, null=True, help_text="Station", related_name="stations", blank=True)
edited_timestamp = models.DateTimeField(null=True, blank=True)
time_exceptions = models.CharField(max_length=2, blank=True, default='', choices=EXCEPTION_STATUS)
time_in = models.DateTimeField(help_text="Time in", null=True, blank=True)
time_out = models.DateTimeField(blank=True, help_text="Time out", null=True)
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number', 'edited_timestamp')
urls.py
urlpatterns = [
url(r'enter-exit-area/$', views.enter_exit_area, name='enter_exit_area'),
url(r'update-timestamp-modal/(?P<main_pk>\d+)/$', UpdateTimestampModal.as_view(), name='update_timestamp_modal'),
]
(Took out leave_area code for redundancy)
views.py
def enter_exit_area(request):
form = WarehouseForm()
enter_without_exit = None
exit_without_enter = None
if request.method == 'POST':
temp = request.POST.copy()
form = WarehouseForm(temp)
if form.is_valid():
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
if 'enter_area' in request.POST:
new_entry = form.save()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(work_area=area) & Q(time_out__isnull=True) & Q(time_in__isnull=True)) & (Q(station_number=station) | Q(station_number__isnull=True))).update(time_in=datetime.now())
# If employee has an entry without an exit and attempts to enter a new area, mark as an exception 'N', meaning they're getting the modal
enters_without_exits = EmployeeWorkAreaLog.objects.filter(Q(employee_number=emp_num) & Q(time_out__isnull=True) & Q(time_exceptions="")).exclude(pk=new_entry.pk).order_by("-time_in")
if len(enters_without_exits) > 0:
enter_without_exit = enters_without_exits[0]
enters_without_exits.update(time_exceptions='N')
message = 'You have entered %(area)s' % {'area': area}
if station is not None:
message += ': %(station)s' % {'station': station}
messages.success(request, message)
form = WarehouseForm()
return render(request, "operations/enter_exit_area.html", {
'form': form,
'enter_without_exit': enter_without_exit,
})
class UpdateTimestampModal(CreateUpdateModalView):
main_model = EmployeeWorkAreaLog
model_name = "EmployeeWorkAreaLog"
form_class = WarehouseForm
template = 'operations/modals/update_timestamp_modal.html'
modal_title = 'Update Missing Time'
enter_exit_area.html
{% extends "base.html" %}
{% load core_tags staticfiles %}
{% block head %}
<script src="{% static "js/operations/warehouse_enter_exit.js" %}"></script>
{% endblock head %}
{% block main %}
{% if enter_without_exit %}
<div id="auto-open-ajax-modal" data-modal="#create-update-modal" data-modal-url="{% url "operations:update_timestamp_modal" enter_without_exit.id %}" class="hidden"></div>
{% endif %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<div>
<div style="color: red">{{ form.employee_number.errors.as_text }}</div>
<label>Employee</label>
{{ form.employee_number }}
</div>
<div>
<div style="color: red">{{ form.work_area.errors.as_text }}</div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div style="color: red">{{ form.station_number.errors.as_text }}</div>
<div>
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
{% modal id="create-update-modal" title="Update Timestamp" primary_btn="Submit" default_submit=True %}
update_timestamp_modal.html
{% load core_tags %}
<form id="create-update-form" method="post" action="{% url "operations:update_timestamp_modal" main_object.id %}">
{% csrf_token %}
<label>Update</label>
<div class="row">
<div class="form-group col-xs-6">
{% standard_input form.edited_timestamp datetimepicker=True hide_label=True %}
</div>
</div>
</form>
warehouse_enter_exit.js
$(function () {
// Submit the edited timestamp form when they click the "Submit" button in the modal
$(document).on('click', '#update-timestamp-modal-btn-primary', function (e) {
e.preventDefault();
forms.ajaxSubmit($('#create-update-form'), function (data) {
if (data.success && data.redirect) {
window.location.href = data.redirect;
} else {
if (data.warning) {
messages.warning(data.warning);
} else {
messages.error("An error occurred when saving this timestamp, please try again.");
}
}
});
});
});
Could I maybe edit the JS to only update the edited_timestamp field? Or maybe a way I can edit the views so that only that ID is accessed to update that field? The URL to the modal access it based on ID so I thought there could be a way to edit only based on this field.
You'll need to override those fields in your form code.
Like this:
class WarehouseForm(AppsModelForm):
employee_number = forms.ModelChoiceField(queryset=EmployeeWorkAreaLog.objects.all(), required=False)
work_area = forms.CharField(required=False)
class Meta...

How to pass a modified sortable list from html to another html?

I am writing web application by Flask. I made a sortable list in HTML using jquery-sortable.js. I want to pass the new sequences of the list after making modification from one HTML to another HTML.
I have tried to use GET/POST methods. However, it didn't work. Nothing was passed to another HTML.
home.html
<script>
...
$('#sortedList').val(sorted_list);
...
</script>
<form action="http://localhost:5000/about" method="POST">
{{ form.hidden_tag() }}
<div class="form-group" style="display: none;">
{{ form.sortedList.label(class="form-control-label") }}
{{ form.sortedList(class="form-control form-control-lg") }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn_outline-info") }}
</div>
</form>
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class SortedForm(FlaskForm):
sortedList = StringField('Sorted List', validators=[DataRequired()])
submit = SubmitField('Submit')
main.py
#app.route("/about", methods=['GET', 'POST'])
def about():
form = SortedForm()
if form.validate_on_submit():
flash(f'Sorted list passed {form.sortedList.data}~', 'success')
return render_template('about.html', title='About', form=form)
about.html
{% extends "layout.html" %}
{% block content %}
{{ form.sortedList.data }}
{% endblock content %}

Categories

Resources