I have a SQLALchemy table:
class User(db.Model, UserMixin):
user_id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), nullable=False, unique=True)
password_hash = db.Column(db.String(40), nullable=False)
But I want users to be able login and register using either by creating a new account with username and password, or using a social media account (google/twitter/vk.com). My idea is to have a base User table that will have abstract methods like get_name() that will have to be defined in the derived tables for each social media.
Pseudocode for this:
Base user
class User(db.Model, UserMixin):
user_id = db.Column(db.Integer, primary_key=True)
# Overridden method from UserMixin
def get_id(self):
return self.user_id
def get_name(self): raise NotImplementedError
Google user
class GoogleUser(User):
first_name = db.Columnn(db.String(40), nullable=False)
last_name = db.Columnn(db.String(40), nullable=False)
def get_name(self):
return '{} {}'.format(self.first_name, self.last_name)
E-mail user
class EmailUser(User):
username = db.Column(db.String(100), nullable=False, unique=True)
password_hash = db.Column(db.String(40), nullable=False)
def get_name(self):
return self.username
The question is what is the best way to implement it with SQLAlchemy & UserMixin?
Thanks for any help in advance.
What I usually do is create a User model with all the information that is needed by the application e.g.
class User(db.Model, UserMixin):
user_id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(100), nullable=False, unique=True)
password_hash = db.Column(db.String(40), nullable=True)
Then for third party services authentication, I create a table which stores token and other relevant data regarding that service that the application requirese.g.
class GoogleUser(db.Model):
first_name = db.Column(db.String(40), nullable=False)
last_name = db.Column(db.String(40), nullable=False)
google_token = db.Column(db.String(200), nullable=False)
I connect this new table to the User model via foreign key:
google_user_id = db.Column(db.Integer, db.ForeignKey("google_user.id"))
This way the user can easily connect their third party accounts and have an account on your application.
Facebook Login's documentation also recommends this
Hope it clears things up!
Related
I am new to flask and SQLite and have very little knowledge of how they work.
Models.py
from datetime import datetime
from flaskblog import db, login_manager
from flask_login import UserMixin
#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, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
class PostIntmath(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)
def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"
These are the models I have defined.
I don't know why this is wrong but it gives me InvalidRequestErrors.
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->user'. Original exception was: When initializing mapper mapped class User->user, expression 'Post' failed to locate a name ("name 'Post' is not defined"). If this is a class name, consider adding this relationship() to the <class 'flaskblog.models.User'> class after both dependent classes have been defined.
My full project is at https://github.com/MGeureka/flask_web_app
HELP!!!
When declaring a relationship, you must use the class name exactly as it appears in the class definition.
The reason SQL Alchemy can't find 'Post' is because the name you used is 'PostIntmath' instead.
Change this line
posts = db.relationship('Post', backref='author', lazy=True)
to
posts = db.relationship('PostIntmath', backref='author', lazy=True)
And your error will go away.
SQL alchemy allows you to define the mapped relationship class with a string in this way so that these classes can be in separate files and still work without needing imports. Behind the scenes, the library looks for all the tables associated with the db object and attempts to find one that matches the relationship string name you've supplied.
I am working on a Flask app, using Flask-SQLAlchemy extension for database interactions. Since I have multiple apps writing on the same DB, I was getting concurrency issues with SQLite and I wanted to switch to PostgreSQL instead. I am able to create the tables on new database without a problem and pgAdmin displays the tables and columns.
# working
def createTables():
with app.app_context():
from models import User, Invoice
db.create_all()
But when it comes to adding a user, I am now getting an error: sqlalchemy.exc.NoForeignKeysError Although, I think, I declared one-to-many relationship in my models, based on the documentation, I get an error states that "there are no foreign keys linking these tables."
# not working
def create_test_user():
with app.app_context():
user = User(
username="Bob",
email="bob#email.com",
password="test"
)
db.session.add(user)
db.session.commit()
The full error message:
""" NoForeignKeysError: Could not determine join condition between parent/child tables on relationship User.invoices
- there are no foreign keys linking these tables.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression. """
I can't figure out what causes the error. What is missing with my models?
# models.py
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
invoices = db.relationship('Invoice', backref='user', lazy=True)
class Invoice(db.Model):
__tablename__ = "invoice"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False)
Solved
Your code works for me. Maybe you need to re-create your tables or something similar. To be sure that we have the identical code: I have tested the following code:
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
invoices = db.relationship('Invoice', backref='user', lazy=True)
class Invoice(db.Model):
__tablename__ = "invoice"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False)
In the route:
user = User(
username="Bob",
email="bob#email.com",
password="test"
)
db.session.add(user)
db.session.commit()
print(user)
I finally solved the problem and it was not where I was looking for. I was getting NoForeignKeysError due to importing a wrong model file during initializing the app. One of my imported modules was calling a wrong/old version of the model. It was causing the table relationship in the actual model to break I guess.
When I went through step by step create_test_user() I noticed that the error occurs actually during the class creation, before even it hits to db.session.add and I replicated the error even without a DB. I went through all my modules that are calling the models and caught wrong model import.
I'm using Flask and SQLAlchemy to create a web app and I'm having troubles with self-reference. On my project I have companies (model), a company can have a provider (another company) or not. If one company is a provider, that company should have a list of clients (every company that it provides services).
Using SQLAlchemy Documentation I've tried to make this self-reference relationship.
This are my models:
from flask_login import UserMixin
from . import db
class Organization(db.Model):
id = db.Column(db.Integer, primary_key=True)
rut = db.Column(db.String(10), unique=True)
name = db.Column(db.String(100))
users = db.relationship('User', backref='organization', lazy=True)
class Company(db.Model):
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(100), unique=True)
name = db.Column(db.String(100))
provider_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=True)
clients = db.relationship('Company', remote_side=[id])
users = db.relationship('User', backref='company', lazy=False)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(100))
name = db.Column(db.String(1000))
last_name = db.Column(db.String(1000))
organization_id = db.Column(db.Integer, db.ForeignKey('organization.id'), nullable=False)
company_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=True)
is_admin = db.Column(db.Boolean, default=False)
is_active = db.Column(db.Boolean, default=False)
The problem is that, even though I can register the provider_id (provider company) I cannot relate this with the clients field on the provider object. So If I have this company #1 which is company #2's provider, and I check the company #1's clients it's empty. Any ideas of what am I doing wrong?
This is my first time working with Flask and SQLAlchemy.
And I need to set up the list of clients because to delete companies I want to be able to delete just the ones with no clients.
I have three models: Role, User, and Post. Role -> User is one to many, and User -> Post is one to many. When I just had User and Post, everything worked. However, my website will need varying degrees of authorization.
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
users = db.relationship('User', backref='role', lazy=True)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
Here is the error message I get:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError)
no such column: user.role_id
Thanks!
You don't need to explicitly define the role_id field - the relationship defined in Role (and the backref parameter you specified) has already created a backwards relationship on User objects for you. (The SQLAlchemy documentation around backrefs may be helpful to you.)
As such, if you have an instantiated User object u, you should be able to get details about the user's role via u.role (which will give you a Role instance) and the ID of the role as u.role.id.
Thus, your full set of model definitions only needs to look like this:
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
users = db.relationship('User', backref='role', lazy=True)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
If you do this, then for a Post object p, you can get the id of the role of the author of the post as p.author.role.id (for example).
I'm developing a website with dashboard which admins and moderators can create users. I want to separate users created by moderator and admin. Moderator can only edit the users that him/her created but admin can edit all the users.
I created my user model like this:
class User(UserMixin, ResourceMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# User details
name = db.Column(db.String(50), index=True)
phone = db.Column(db.String(24))
address = db.Column(db.String(255))
email = db.Column(db.String(255), unique=True, index=True, nullable=False,
server_default='')
password = db.Column(db.String(128), nullable=False, server_default='')
# Relationships
created_id = db.Column(db.Integer,
db.ForeignKey('users.id', onupdate='CASCADE', ondelete='SET NULL'),
index=True)
updated_id = db.Column(db.Integer,
db.ForeignKey('users.id', onupdate='CASCADE', ondelete='SET NULL'),
index=True)
Most of the situations this code works like charm. But when moderator or admin tries to edit self, program throws CircularDependency error. What might cause the problem?
Thanks