Flask-SQLalchemy update the current_user - python

How would I update the current users firstname and lastnames in my Flask-SQLAlchemy database?
I have tried to use the following code based off of the flask-sqlalchemy documentation - however, it only commits the update for the email field and does not update the record for either the firstname or lastname fields.
# views.py
#users.route("/account", methods=['GET', 'POST'])
#login_required
def account():
form = UpdateUserForm()
if form.validate_on_submit():
current_user.firstname = form.firstname.data
current_user.lastname = form.lastname.data
current_user.email = form.email.data
db.session.commit()
return redirect(url_for('users.account'))
elif request.method == 'GET':
form.firstname.data = current_user.firstname
form.lastname.data = current_user.lastname
form.email.data = current_user.email
return render_template('account.html', form=form)
#forms.py
class UpdateUserForm(FlaskForm):
email = StringField('Email',validators=[InputRequired(message = 'Enter a valid email'),Email()])
firstname = StringField('First Name',validators=[InputRequired(message = 'Enter your first name')])
lastname = StringField('Last Name',validators=[InputRequired(message = 'Enter your last name')])
submit = SubmitField('Update')
def validate_email(self,email):
if User.query.filter_by(email=email.data).first():
raise ValidationError('This email has been registered already!')
# Summarized Template & Form Submission
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="account" role="tabpanel" aria-labelledby="account-tab">
<div class="col-5">
</div>
<div class="col-sm-12 col-md-7">
<form method="post" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
<label class="small mb-0">{{ form.firstname.label }}</label>
{{ form.firstname(class="form-control") }}
</div>
<div class="form-group">
<label class="small mb-0">{{ form.lastname.label }}</label>
{{ form.lastname(class="form-control") }}
</div>
<div class="form-group">
<label class="small mb-0">{{ form.email.label }}</label>
{{ form.email(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
</div>
</div>
# Model.py
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer,primary_key=True)
selected_theme = db.Column(db.String(64),nullable=False,default='default')
email = db.Column(db.String(64),unique=True,index=True)
fullname = db.Column(db.String(64))
firstname = db.Column(db.String(64))
lastname = db.Column(db.String(64))
password_hash = db.Column(db.String(128))
def __init__(self,email,fullname,password, firstname, lastname):
self.email = email
self.fullname = fullname
self.firstname = firstname
self.lastname = lastname
self.password_hash = generate_password_hash(password)
def check_password(self,password):
return check_password_hash(self.password_hash,password)
def __repr__(self):
return f"Username {self.username}"

Sorry, I can't find a mistake. The following code works for me. Even if there is no real difference to yours, it is worth trying.
class UpdateUserForm(FlaskForm):
email = StringField(
'Email',
validators=[
# InputRequired is deprecated and replaced by DataRequired
DataRequired(message = 'Enter a valid email'),
Email()
]
)
firstname = StringField(
'First Name',
validators=[
DataRequired(message = 'Enter your first name')
]
)
lastname = StringField(
'Last Name',
validators=[
DataRequired(message = 'Enter your last name')
]
)
submit = SubmitField('Update')
def validate_email(self, email):
if User.query.filter_by(email=email.data).first():
raise ValidationError('This email has been registered already!')
#users.route("/account", methods=['GET', 'POST'])
#login_required
def account():
# You can forward your current data to the form here
form = UpdateUserForm(request.form, obj=current_user)
if form.validate_on_submit():
current_user.firstname = form.firstname.data
current_user.lastname = form.lastname.data
current_user.email = form.email.data
db.session.commit()
return redirect(url_for('users.account'))
return render_template('account.html', **locals())
<form method="POST">
{{ form.csrf_token }}
<div class="form-group">
{{ form.email.label() }}
{{ form.email(class='form-control') }}
{% if form.email.errors: %}
<div class="invalid-feedback">
{{ form.email.errors[0] }}
</div>
{% endif %}
</div>
<div class="form-group">
{{ form.firstname.label() }}
{{ form.firstname(class='form-control') }}
{% if form.firstname.errors: %}
<div class="invalid-feedback">
{{ form.firstname.errors[0] }}
</div>
{% endif %}
</div>
<div class="form-group">
{{ form.lastname.label() }}
{{ form.lastname(class='form-control') }}
{% if form.lastname.errors: %}
<div class="invalid-feedback">
{{ form.lastname.errors[0] }}
</div>
{% endif %}
</div>
{{ form.submit(class='btn btn-primary') }}
</form>

Thanks for your help! Was able to resolve my issue by including the following updated to ignore the email submission if it is unchanged (I think it was recognized as a duplicate record and preventing unique value). This little addition seems to resolve.
def validate_email(self,email):
if User.query.filter_by(email=email.data).first() and email.data != current_user.email:
raise ValidationError('Email has been already been registered.')

Related

Data is not stored in flask sqlAlchemy

Im creating a flask app and the data is not stored when i click register after adding all the information. when I click the register button there are no any errors showing.
heres my routes.py
#app.route('/register', methods=['GET', 'POST'])
def register_page():
form = RegisterForm()
if form.validate_on_submit():
user_to_create = User(username=form.username.data,email=form.email.data,password_hash=form.password1.data)
db.session.add(user_to_create)
db.session.commit()
flash(f'Account created Successfully...! you are now logged in as {{ user_to_create.username }}',
category='success')
return redirect(url_for('topup_page'))
if form.errors != {}:
for error in form.errors.values():
flash(f'There was an error with creating the user{error}', category='danger')
return render_template('register.html', form=form)
here is my register.html file
<div class="col-6">
<div class="container">
<form method="POST" class="form-register">
<h2>Please Create Your Account</h2>
{{ form.hidden_tag() }}
<br>
{{ form.username.label()}}
{{ form.username(class="form-control", placeholder="User Name")}}
<br>
{{ form.email.label()}}
{{ form.email(class="form-control", placeholder="Email Address")}}
<br>
{{ form.password1.label()}}
{{ form.password1(class="form-control", placeholder="Password")}}
<br>
{{ form.password2.label()}}
{{ form.password2(class="form-control", placeholder="Confirm Password")}}
<br>
{{ form.submit(class="btn btn-lg btn-block btn-primary disabled") }}
<div class="checkbox mb-3">
<h6> Already have an account? Sign In
</h6>
</div>
</form>
</div>
here is my models.py file
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer(), nullable=False, primary_key=True)
username = db.Column(db.String(20), nullable=False, unique=True)
email = db.Column(db.String(30), nullable=False, unique=True)
password = db.Column(db.String(60), nullable=False)
credit = db.relationship('Credit', backref='owner')
i think you need to add a name attribute to the inputs. Sorry if this is a wrong answer.I have not used flask in a long time.
I will show you sample code:
Template:
<form action="{{ url_for('app.update_view') }}" method="post"
enctype="multipart/form-data">
<input name="name1" placeholder='abc'>
<input name="name2" placeholder='xyz'>
<input name="name3" placeholder='123'>
<button type="submit" class="btn btn-primary">Register</button>
</form>
View:
#app.route('/path', methods=['POST', 'GET'])
#roles_required('admin,member') # doenst matter
def update_view():
try:
if request.method == 'POST':
name1 = request.form.get('name1')
name2 = request.form.get('name2')
name3 = request.form.get('name3')
if name1 is not None and name2 is not None:
YourModelNAme.create_classmethod(name1, name2, name3)
return render_template("directory_name/your_html.html")
except Exception as e:
return create_json_response(
{'message': 'Error->' + e.__str__()}, False, 201), 201
Model:
class YourModelNAme(db.Model, UserMixin):
name1 = db.Column(db.Integer(), nullable=False, primary_key=True)
name2 = db.Column(db.String(20), nullable=False, unique=True)
name3 = db.Column(db.String(60), nullable=False)
#classmethod
def create_classmethod(cls,name1,name2,name3):
your_model = YourModelNAme()
your_model.name1 = name1
...
db.session.add(your_model)
db.session.commit()
return your_model

How to upload image using sqlalchemy (python flask)

I'm a new programmer, i'm using python flask and sqlalchemy database.
i want to upload image and add the filename to my database
this is my code:
in this python file i create Post db model
models.py
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
image_file = db.Column(db.Text, unique=True, nullable=False)
Here create form using class
forms.py
class PostForm(FlaskForm):
title = StringField('Title', validators=[DataRequired()])
content = TextAreaField('Content', validators=[DataRequired()])
picture = FileField('Update Picture', validators=[FileAllowed(['jpg', 'png'])])
submit = SubmitField('Post')
Here my route:
route.py
#app.route("/post/new", methods=['GET', 'POST'])
#login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
if form.picture.data:
picture_file = save_picture_post(form.picture.data)
post = Post(title=form.title.data, content=form.content.data , image_file = picture_file ,author=current_user)
db.session.add(post)
db.session.commit()
flash('Your post has been created!', 'success')
return redirect(url_for('home'))
return render_template('create_post.html', title='New Post',
form=form, legend='New Post')
And my html to create post is as following:
create_post.html
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div class="col-md-8"></div>
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">new post</legend>
<div class="form-group">
<label for="title">title</label>
{% if form.title.errors %}
{{ form.title(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.title.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.title(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
<label for="content">post</label>
{% if form.content.errors %}
{{ form.content(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.content.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.content(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group" style="float: right;">
<label for="picture">add photo </label>
{{ form.picture(class="form-control-file") }}
{% if form.picture.errors %}
{% for error in form.picture.errors %}
<span class="text-danger">{{ error }}</span></br>
{% endfor %}
{% endif %}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-warning") }}
</div>
</form>
</div>
</div>
</div>
{% endblock content %}
My problem is the following line of code not working:
if form.picture.data:
picture_file = save_picture_post(form.picture.data)
my error:
return func(*args, **kwargs)
File "E:\flask\HomeX\application\routes.py", line 154, in new_post
post = Post(title=form.title.data, content=form.content.data , image_file = picture_file ,author=current_user)
UnboundLocalError: local variable 'picture_file' referenced before assignment
Edit: The save_picture_post is a method in route.py:
def save_picture_post(form_picture):
random_hex = secrets.token_hex(8)
_, f_ext = os.path.splitext(form_picture)
picture_fn = random_hex + f_ext
picture_path = os.path.join(app.root_path, 'static/job', picture_fn)
output_size = (125, 125)
i = Image.open(form_picture)
i.thumbnail(output_size)
i.save(picture_path)
return picture_fn
Your saving logic should be within form.picture.data, viz.
if form.validate_on_submit():
if form.picture.data:
picture_file = save_picture_post(form.picture.data)
post = Post(title=form.title.data, content=form.content.data , image_file = picture_file ,author=current_user)
db.session.add(post)
db.session.commit()
flash('Your post has been created!', 'success')
return redirect(url_for('home'))

TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

I've created a custom user model in Django and everything went fine, but when I try to create a new user it gives me an error.
Here are my files :
models.py, where I created the user table:
class UserModelManager(BaseUserManager):
def create_user(self, email, password, pseudo):
user = self.model()
user.name = name
user.email = self.normalize_email(email=email)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password):
'''
Used for: python manage.py createsuperuser
'''
user = self.model()
user.name = 'admin-yeah'
user.email = self.normalize_email(email=email)
user.set_password(password)
user.is_staff = True
user.is_superuser = True
user.save()
return user
class UserModel(AbstractBaseUser, PermissionsMixin):
## Personnal fields.
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=16)
## [...]
## Django manage fields.
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['email', 'name']
objects = UserModelManager()
def __str__(self):
return self.email
def get_short_name(self):
return self.name[:2].upper()
def get_full_name(self):
return self.name
forms.py, where I created the signup form:
class SignUpForm(UserCreationForm):
email = forms.CharField(required=True, help_text='البريد الإلكترونى الخاص بك - يجب ان يكون حقيقى (يستخدم لتسجيل الدخول) ')
name = forms.CharField(required=True, help_text='إسمك الحقيقى - سيظهر كأسم البائع')
password1 = forms.CharField(widget=forms.PasswordInput,
help_text='كلمة المرور - حاول ان تكون سهلة التذكر بالنسبة لك')
password2 = forms.CharField(widget=forms.PasswordInput,
help_text='تأكيد كلمة المرور - إكتب نفس كلمة المرور السابقة مرة أخرى')
class Meta:
model = UserModel
fields = ('email','name', 'password1', 'password2', )
labels = {
'name': 'إسمك الحقيقى - سيظهر كأسم البائع',
'email': 'البربد الإلكترونى Email',
'password1': 'كلمة المرور',
'password2': 'تأكيد كلمة المرور'
}
views.py with a view that renders the signup template:
def signup(request):
if request.method == 'POST':
signup_form = SignUpForm(request.POST)
if signup_form.is_valid():
signup_form.save()
username = signup_form.cleaned_data.get('username')
raw_password = signup_form.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
login(request, user)
return redirect('signup_confirm')
else:
signup_form = SignUpForm()
context = {
'signup_form': signup_form,
}
return render(request, 'fostania_web_app/signup.html', context)
Finally, signup.html, the template itself where the form is being used:
{% extends 'fostania_web_app/base.html' %}
{% block content %}
{% load static %}
<br>
<br>
<div align="center">
<div class="card border-primary mb-3" style="max-width: 40rem;" align="center">
<div class="card-header">إنشاء حساب جديد</div>
<div class="card-body text-dark">
<h5 class="card-title">
<img src="{% static 'img/add_user_big.png' %}">
</h5>
<p class="card-text"> {% if user.is_authenticated %}
<div align="center">
لقم قمت بتسجيل الدخول بالفعل <br>
يمكنك <a href = '{% url 'logout' %}'>
<button class="btn btn-danger" >تسجيل الخروج</button> </a> إذا كنت ترغب الدخول على حساب اخر
</div>
{% else %}
<form method="post" align="right">
{% csrf_token %}
{% for field in signup_form %}
<p>
{{ field.label_tag }}<br>
{{ field }}<br>
<font color="gray">{{ field.help_text }}</font>
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
{% endfor %}
<br>
<button type="submit" class="btn btn-success">تسجيــل</button><br>
بالضغط على تسجيل انت توافق على شروط الإستخدام الخاصة بموقعنا !
</form>
</div>
{% endif %}
</p>
</div>
</div>
{% endblock %}
The error I'm getting is:
ImproperlyConfigured at /signup/
TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'
I suspect that the problem is actually in your urls.py. If you point your url to the wrong view (maybe a CBV that you have associated to the /signup/ path), that could cause this error.

Assign request.user.id to OneToOneField in Model using form

I'm using django auth for users. Every user can create one row - based on Client model. But i have problem because I cant assign in form.is_valid to field id request.user.id.
Because id is required I exclude this field in form class Meta.
Please give me some advice how i can assign user.id to my OneToOneField field.
I'm using PyCharm and when i put form. i dont see any of fields in my Model so i thing that i make some mistake in my code :(
Model:
class Client(models.Model):
id = models.OneToOneField(User, on_delete=models.CASCADE, unique=True, primary_key=True)
name = models.CharField(max_length=256, unique=True)
vat = models.CharField(max_length=12, unique=True)
address = models.CharField(max_length=64)
zip_code = models.CharField(max_length=10, help_text='Zip Code')
city = models.CharField(max_length=64)
country = models.CharField(max_length=6, default='US')
forwarding_address = models.CharField(max_length=64, blank=True)
forwarding_zip_code = models.CharField(max_length=10, blank=True)
forwarding_city = models.CharField(max_length=64, blank=True)
forwarding_country = models.CharField(max_length=6, blank=True)
phone = models.CharField(max_length=20)
def __str__(self):
re = self.name + ' [' + str(self.id) + ']'
return re
Form:
class ClientProfileForm(forms.ModelForm):
class Meta:
model = Client
exclude = ['id']
View:
def profile_create(request):
if request.method == 'POST':
form = ClientProfileForm(request.POST)
if form.is_valid():
form.save(commit=False)
form.id = request.user.id
form.save()
return HttpResponseRedirect('/client/profile/')
dict = {}
dict['form'] = form
return render(request, 'client/profile_edit.html', dict)
else:
if Client.objects.filter(id=request.user.id).exists():
return HttpResponseRedirect('/client/profile/edit/')
else:
dict = {}
dict['form'] = ClientProfileForm()
return render(request, 'client/profile_edit.html', dict)
Template:
{% extends 'registration/base.html' %}
{% block title %} Client profile {% endblock %}
{% block content %}
{{ form.non_field_errors }}
<form role="form" action="" method="post">{% csrf_token %}
{{ form.name.errors }}
<div class="form-group login-input">
<i class="fa fa-envelope overlay"></i>
<input type="text" class="form-control text-input"
{% if form.name.value != nulls %} value="{{ form.name.value }}" {% endif %}
id="{{ form.name.name }}" name="{{ form.name.name }}">
</div>
{{ form.vat.errors }}
<div class="form-group login-input">
<i class="fa fa-envelope overlay"></i>
<input type="text" class="form-control text-input"
{% if form.vat.value != nulls %} value="{{ form.vat.value }}" {% endif %}
id="{{ form.vat.name }}" name="{{ form.vat.name }}">
</div>
{{ form.address.errors }}
<div class="form-group login-input">
<i class="fa fa-envelope overlay"></i>
<input type="text" class="form-control text-input"
{% if form.address.value != nulls %} value="{{ form.address.value }}" {% endif %}
id="{{ form.address.name }}" name="{{ form.address.name }}">
</div>
(....)
<div class="row">
<div class="col-sm-12">
<button type="submit" class="btn btn-default btn-block">Create</button>
</div>
</div>
</form>
{% endblock %}
Cheers!
That's not the right pattern. It should be:
if form.is_valid():
obj = form.save(commit=False)
obj.id = request.user.id
obj.save()

SQLAlchemy with Flask does not commit

When I submit my form from webpage, I am not able to commit. Flashing data I need, I see they're there and correct, but something fails when committing. I am sure I am making a mistake somewhere because mostly commit are working except two. This is one of the two that is not working.
Models:
class Feedback(db.Model):
__tablename__ = 'feedback'
id = db.Column(db.Integer, primary_key = True)
rate = db.Column(db.Integer)
comment = db.Column(db.Text())
sender_id = db.Column(db.Integer)
receiver_id = db.Column(db.Integer)
Forms:
class LeaveFeedbackForm(Form):
rate = IntegerField('Rate', validators = [DataRequired(),
NumberRange(min = 1, max = 5, message = 'Rates admitted are only 1,2,3,4,5')])
comment = TextAreaField('Comment', validators = [DataRequired()])
submit = SubmitField('Submit')
Views:
#app.route('/leave_feedback/<sender>/<receiver>', methods = ['GET', 'POST'])
def leave_feedback(receiver, sender):
form = LeaveFeedbackForm()
rec = int(receiver)
sen = int(sender)
if form.validate_on_submit():
feedback = Feedback( rate = form.rate.data,
comment = form.comment.data,
receiver_id = rec,
sender_id = sen
)
db.session.add(feedback)
db.session.commit()
flash('Feedback Left Correctly.')
return redirect(url_for('index'))
flash(form.rate.data)
flash(form.comment.data)
flash(rec)
flash(sen)
return render_template('leave_feedback.html', receiver_id = receiver, sender_id = sender, form = form)
html:
{% block content %}
<div class="row">
<div class="large-6 columns">
<h1>Leave Feedback</h1>
</div>
</div>
<form action="" method="post" name="leavefeedback">
<div class="row">
<div class="large-6 columns">
<label>Rate
{{ form.rate }}
</label>
</div>
</div>
<div class="row">
<div class="large-6 columns">
<label>Comment
{{ form.comment }}
</label>
</div>
</div>
<div class="row">
<div class="large-6 columns">
<input class="button radius" type="submit" value="Leave Feedback">
</div>
</div>
</form>
{% endblock %}
You should add an else statement:
if form.validate_on_submit():
...
else:
for error in form.errors.itervalues():
flash(error[0])
Then you will get an error message from form.
I figured out my mistake, I simply forgot in my form:
{{ form.hidden_tag() }}

Categories