I am building a web-application in flask and one of the modules I am adding to it is the user module which contains the information from the users including ID, email, password and role.
For the password I am trying to generate a password hash but in my flask shell I cannot add the password to the database (however I can add email AND also if I do not generate password hash, I can add password too).
For the user module I made a Blueprint and a separate folder which includes models.py and __init__.py.
Here is the app.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Development
app = Flask(__name__)
app.config.from_object(Development)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
#app.route('/')
def index():
return "Home page"
from mod_users import users
app.register_blueprint(users)
And for the users module (folder), here is the __init__.py file:
from flask import Blueprint
users = Blueprint('users', __name__, url_prefix='/users/')
from .models import User
#users.route('/')
def user_index():
return "Hello from User index"
And here is the models.py :
from werkzeug.security import generate_password_hash
from sqlalchemy import Column, Integer, String
from app import db
class User(db.Model):
__tablename__ = 'users'
ID = Column(Integer(), primary_key=True)
email = Column(String(128), nullable=False , unique=True)
password = Column(String(128), nullable=False, unique=False)
role = Column(Integer(), nullable=False, default=0)
def set_password(self, password):
self.password = generate_password_hash(password)
In my flask shell :
from mod_users.models import User
from app import db
user = User()
user.email = ‘myemail#hgku.er’
user.set_password('123456')
For the last line I will get this error:
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'User' object has no attribute 'set_password'
But if I do:
user.password = ‘123456’
It works and I can add it to the database.
Do you know how I can fix the error?
your indentation in the User object is apparently wrong. if you want set_password to be a class function, you have to indent it into the User class scope
class User(db.Model):
__tablename__ = 'users'
ID = Column(Integer(), primary_key=True)
email = Column(String(128), nullable=False , unique=True)
password = Column(String(128), nullable=False, unique=False)
role = Column(Integer(), nullable=False, default=0)
def set_password(self, password):
self.password = generate_password_hash(password)
Related
Writing my first web app using flask / SQLAlchemy. I have a many to many relationship between 'persons' and 'facilities.' When I successfully add a person using the registration form, the association table does not get a row added. Do I have to insert that row manually?
Here is the pertinent part of the model:
# app/models.py
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from app import db, login_manager
# [START model]
# Build secondary table for many to many between facilities and persons
workers = db.Table('workers',
db.Column('facility_id', db.Integer, db.ForeignKey('facilities.id')),
db.Column('person_id', db.Integer, db.ForeignKey('persons.id'))
)
class Facility(db.Model):
__tablename__='facilities'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60))
description = db.Column(db.String(128))
persons = db.relationship('Person', secondary='workers', backref='facilities', lazy = 'dynamic')
def __repr__(self):
return "<Facility name='%s')" % (self.name)
class Person(UserMixin, db.Model):
__tablename__ = 'persons'
id = db.Column(db.Integer, primary_key=True)
last_name = db.Column(db.String(60), index=True)
username = db.Column(db.String(60), index=True, unique=True)
email = db.Column(db.String(80), index=True)
password_hash = db.Column(db.String(128))
first_name = db.Column(db.String(60), index=True)
role = db.Column(db.Integer, db.ForeignKey('roles.id'))
is_person_active = db.Column(db.Boolean, index=True)
is_admin = db.Column(db.Boolean, default=False)
comments = db.Column(db.String(255))
animals = db.relationship('Animal', secondary='permissions', backref='persons', lazy = 'dynamic'))
#property
def password(self):
"""
Prevent password from being accessed
"""
raise AttributeError('password is not a readable attribute.')
#password.setter
def password(self, password):
"""
Set password to a hashed password
"""
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
"""
Check if hashed password matches actual password
"""
return check_password_hash(self.password_hash, password)
def __repr__(self):
return "<Person name='%s', '%s', '%s')" % (self.first_name, self.last_name, self.username)
# Set up user_loader
#login_manager.user_loader
def load_user(user_id):
return Person.query.get(int(user_id))
And here is the view:
# app/auth/views.py
from flask import flash, redirect, render_template, url_for
from flask_login import login_required, login_user, logout_user
from . import auth
from .forms import LoginForm, RegistrationForm
from .. import db
from ..models import Person, Facility
#auth.route('/register', methods=['GET', 'POST'])
def register():
"""
Handle requests to the /register route
Add a person to the database through the registration form
"""
form = RegistrationForm()
form.facility_id.choices = [(f.id, f.name) for f in Facility.query.order_by('name')]
if form.validate_on_submit():
person = Person(facility=form.facility_id.data,
email=form.email.data,
username=form.username.data,
first_name=form.first_name.data,
last_name=form.last_name.data,
password=form.password.data)
# add person to the database
db.session.add(person)
db.session.commit()
flash('You have successfully registered! You may now login.')
# redirect to the login page
return redirect(url_for('auth.login'))
# load registration template
return render_template('auth/register.html', form=form, title='Register')
Thanks for the support #Michael. You were close enough that I found the problem; it was that I was not adding the person to the persons collection for the facility, so no row was inserted into the workers table. I added
facility = Facility.query.filter_by(id=form.facility_id.data).first()
facility.persons.append(person)
db.session.commit()
after the existing code
db.session.add(person)
db.session.commit()
in the registration view and it is correctly inserting rows in the workers table now.
If the above https://stackoverflow.com/a/60100671/1449799 doesn't work, I wonder if it's as simple as a spelling issue? You've said that the back ref from Facility to Person should be called facilities. perhaps in your call to the Person constructor in your register function you should change:
person = Person(facility=form.facility_id.data,
to
person = Person(facilities=[form.facility_id.data],
Perhaps this question is a duplicate of https://stackoverflow.com/a/25669256/1449799 ? It seems that the issue you're having is that in your register() function, there's no mention of facilities.
Without changing your model classes (e.g. to have the Person model know about its connected facilities in addition to the reverse of what you do have now in Facility for Person), I think you may be able to do something in register() like:
#this should maybe come after db.session.add(person), but before db.session.commit()
selected_facility = Facility.query.get(form.facility_id.data)
selected_facility.persons.append(person)
or alternatively
#this should maybe come after db.session.add(person), but before db.session.commit()
selected_facility = Facility.query.get(form.facility_id.data)
person.facilities.append(selected_facility)
I am trying get a picture link from a facebook account but get this message:
django.db.utils.IntegrityError: UNIQUE constraint failed:
user_profile.user_id
I can see a picture link in console, but I cannot save it in user profile.
here is my model.py when I'm trying to do that.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from allauth.account.signals import user_signed_up, user_logged_in
from allauth.socialaccount.models import SocialAccount
import hashlib
try:
from django.utils.encoding import force_text
except ImportError:
from django.utils.encoding import force_unicode as force_text
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
related_name='userprofile')
city = models.CharField(max_length=30, blank=True)
about = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', verbose_name='Images',
blank=True)
sound = models.BooleanField(default=False)
points = models.DecimalField(max_digits=4, decimal_places=2, default=0.00)
energy = models.IntegerField(default=0)
avatar_url = models.URLField(max_length=500, blank=True, null=True)
class Meta:
db_table = 'user_profile'
verbose_name = 'Profile'
verbose_name_plural = 'Profiles'
def __str__(self):
return str(self.user)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
##receiver(user_logged_in)
#receiver(user_signed_up)
def set_initial_user_names(request, user, sociallogin=None, **kwargs):
preferred_avatar_size_pixels = 25
if sociallogin:
if sociallogin.account.provider == 'facebook':
picture_url = "http://graph.facebook.com/{0}/picture?width={1}&height={1}".format(
sociallogin.account.uid, preferred_avatar_size_pixels)
profile = UserProfile(user=user, avatar_url=picture_url)
#profile = UserProfile.objects.get(user=user)
#profile.avatar_url = picture_url
profile.save()
If I am doing like that at the end:
#profile = UserProfile(user=user, avatar_url=picture_url)
profile = UserProfile.objects.get(user=user)
profile.avatar_url = picture_url
profile.save()
I am not gettin any message in the console, but user profile doesn't save.
This line profile = UserProfile(user=user, avatar_url=picture_url) is causing the problem as you are trying to create a new instance of profile which already exists. The profile becomes unique because of OneToOne field in your UserProfile model.
And you don't need to get the user from the database because set_initial_user_names function is already passing the registered user to you as a parameter. So just do user.userprofile. Then you can just update the user with new information.
Also I would suggest you to download the picture from the url provided and then save it in your image field of your model like this:
import urllib
from django.core.files import File
# for python 2: result = urllib.urlretrieve(picture_url)[0]
result = urllib.request.urlretrieve(picture_url)[0] # for python 3
user.userprofile.avatar.save('test.jpg', File(open(result, 'rb')))
user.userprofile.save()
I have a project myappointments, with two apps- appointments and clinic in it.
Objective:
When a user logins, details should be entered in the database.
appointments/models.py:
class Event(models.Model):
id=models.AutoField(primary_key=True, unique=True)
type=models.CharField(max_length=60)
description = models.CharField(max_length=150)
time = models.DateTimeField(default=timezone.now)
appointments/init.py:
default_app_config = 'appointments.apps.AppointmentsConfig'
appointments/apps.py:
from django.apps import AppConfig
class AppointmentsConfig(AppConfig):
name = 'appointments'
def ready(self):
import appointments.signals
appointments/signals.py:
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver
def record_loggedin(sender, user, request, **kwargs):
ev = Event(type="Login", description = 'User logged in: {}'.format(request.user.id))
ev.save()
print("User has logged in. Saved event to DB.")
user_logged_in.connect(record_loggedin)
What other modifications do I need to do this?
I'm trying to replicate this https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xxiii-application-programming-interfaces-apis focus only in the API and security (tokens) part.
I'm having problems when I'm executing the example to manage the database
>>> u = User(username='susan', email='susan#example.com')
>>> db.session.add(u)
>>> db.session.commit()
I get this error:
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.
The main idea here is to validate if the db is working properly
This is the code in my main app/init.py file:
from flask import Flask, request, current_app
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
from app.main import bp as main_bp
app.register_blueprint(main_bp)
from app.api import bp as api_bp
app.register_blueprint(api_bp, url_prefix='/api')
return app
from app import models
This is my app/models.py file:
from flask import current_app, url_for
from werkzeug.security import generate_password_hash, check_password_hash
from app import db#, login
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
token = db.Column(db.String(32), index=True, unique=True)
token_expiration = db.Column(db.DateTime)
def __repr__(self):
return '<User {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def from_dict(self, data, new_user=False):
for field in ['username', 'email']:
if field in data:
setattr(self, field, data[field])
if new_user and 'password' in data:
self.set_password(data['password'])
#staticmethod
def check_token(token):
user = User.query.filter_by(token=token).first()
if user is None or user.token_expiration < datetime.utcnow():
return None
return user
i'm using rest_framework.authtoken.models Token. i can see 3 fields which is key, created_at and user_id.
Background of App:
I use chrome app as client for app, i want to use token authentication to connect with my APIs in django rest framework. and i want to store user_id and company_id in authtoken_token table. so i could store just the token key in chrome app localstorage,
My question is how can i add an extra field like company_id to that model? i couldn't find any docs or articles about this.
I've also Jamie's answer in this article to subclass the model but i don't know how.
Thanks!
Define you own authentication method:
settings.py
'DEFAULT_AUTHENTICATION_CLASSES': (
'my_project.my_app.authentication.myOwnTokenAuthentication',
),
authentication.py
from rest_framework.authentication import TokenAuthentication
from my_project.my_app.models.token import MyOwnToken
class MyOwnTokenAuthentication(TokenAuthentication):
model = MyOwnToken
model.py
import binascii
import os
from django.db import models
from django.utils.translation import ugettext_lazy as _
from my_project.companies.models import Company
class MyOwnToken(models.Model):
"""
The default authorization token model.
"""
key = models.CharField(_("Key"), max_length=40, primary_key=True)
company = models.OneToOneField(
Company, related_name='auth_token',
on_delete=models.CASCADE, verbose_name="Company"
)
created = models.DateTimeField(_("Created"), auto_now_add=True)
class Meta:
verbose_name = _("Token")
verbose_name_plural = _("Tokens")
def save(self, *args, **kwargs):
if not self.key:
self.key = self.generate_key()
return super(MyOwnToken, self).save(*args, **kwargs)
def generate_key(self):
return binascii.hexlify(os.urandom(20)).decode()
def __str__(self):
return self.keyDefine you own authentication method: