FlaskFM - Table 'user is already defined for this MetaData Instance

Hello,

I’m doing the FlaskFM project in the Build Python Web Apps with Flask.

Doing the FlaskFM part of the course I run into the following issue on step 13.

>>> from models import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ccuser/workspace/project-flask-music/models.py", line 5, in <module>
    class User(db.Model):
  File "/usr/local/lib/python3.6/dist-packages/flask_sqlalchemy/model.py", line 67, in __init__
    super(NameMetaMixin, cls).__init__(name, bases, d)
  File "/usr/local/lib/python3.6/dist-packages/flask_sqlalchemy/model.py", line 121, in __init__
    super(BindMetaMixin, cls).__init__(name, bases, d)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/ext/declarative/api.py", line 75, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/ext/declarative/base.py", line 131, in _as_declarative
    _MapperConfig.setup_mapping(cls, classname, dict_)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/ext/declarative/base.py", line 160, in setup_mapping
    cfg_cls(cls_, classname, dict_)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/ext/declarative/base.py", line 190, in __init__
    self._setup_table()
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/ext/declarative/base.py", line 538, in _setup_table
    **table_kw
  File "/usr/local/lib/python3.6/dist-packages/flask_sqlalchemy/model.py", line 90, in __table_cls__
    return sa.Table(*args, **kwargs)
  File "<string>", line 2, in __new__
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/util/deprecations.py", line 128, in warned
    return fn(*args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/sqlalchemy/sql/schema.py", line 489, in __new__
    "existing Table object." % key
sqlalchemy.exc.InvalidRequestError: Table 'user' is already defined for this MetaData instance.  Specify 'extend_existing=True' to redefine options and columns on an existing Table object.

Models.PY Code below

from app import app, db

#the User model: each user has a username, and a playlist_id foreign key referring

#to the user's Playlist

class User(db.Model):

  id = db.Column(db.Integer, primary_key = True)

  username = db.Column(db.String(50), index = True, unique = True) 

  playlist_id = db.Column(db.Integer,  db.ForeignKey('playlist.id'))

  

  #representation method

  def __repr__(self):

        return "{}".format(self.username)

#create the Song model here + add a nice representation method

class Song(db.Model):

  id = db.Column(db.Integer, primary_key = True)

  artist = db.Column(db.String(50), index = True, unique = True) 

  title = db.Column(db.String(50), index = True, unique = True)

  n = db.Column(db.Integer, index = False, unique = False)

def __repr__(self):

  return f"{self.title} by {self.artist}"

    

#create the Item model here + add a nice representation method

class Item(db.Model):

  id = db.Column(db.Integer, primary_key = True)

  song_id = db.Column(db.Integer, db.ForeignKey('song.id'))

    

#create the Playlist model here + add a nice representation method

class Playlist(db.Model):

  id = db.Column(db.Integer, primary_key = True)

  playlist_id = db.Column(db.Integer, dbForeignKey('playlist.id'))

  items = db.relationship('Item', backref='playlisy', lazy='dynamic')


app. py code below

from flask import Flask, render_template

from flask_sqlalchemy import SQLAlchemy

#import SQLALchemy

app = Flask(__name__)

#set the SQLALCHEMY_DATABASE_URI key

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.config['SECRET_KEY'] = 'you-will-never-guess'

app.config['SQLALCHEMY_DATABASE_URI'] = 'song_library.db'

#create an SQLAlchemy object named `db` and bind it to your app

db = SQLAlchemy(app)

#a simple initial greeting

@app.route('/')

@app.route('/index')

def greeting():

    return render_template('greeting.html')

# app name 

@app.errorhandler(404) 

def not_found(e): 

  return render_template("404.html") 

routes.py code below

from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from app import app, db
from models import User, Song, Playlist, Item
from flask import render_template, request, url_for, redirect, flash

#A form for inputing new songs via Dashboard
class SongForm(FlaskForm):
  title = StringField(label = "Song Title:", validators=[DataRequired()])
  artist = StringField(label = "Artist:", validators=[DataRequired()])
  submit = SubmitField("Add Song")

#A function we made to check if an item to be added is already in the playlist
def exists(item, playlist):
  """Return a boolean
    True if playlist contains item. False otherwise.
    """
  for i in playlist: #for each item in playlist
    if i.song_id == item.song_id: #check if the primary key is equal
       return True
  return False

#The home page of FlaskFM
#Lists all the users currently in the database
#renders the home.html template providing the list of current users
@app.route('/profiles')
def profiles():
    current_users = [] #change here to a database query
    return render_template('users.html', current_users = current_users)

#Displays profile pages for a user with the user_id primary key
#renders the profile.html template for a specific user, song library and 
#the user's playlist 
@app.route('/profile/<int:user_id>')
def profile(user_id):
   user = User.query.filter_by(id = user_id).first_or_404(description = "No such user found.")
   songs = Song.query.all()
   my_playlist = None #change here to a database query
   return render_template('profile.html', user = user, songs = songs, my_playlist = my_playlist)

#Adds new songs to a user's playlist from the song library
#redirects back to the profile that issued the addition
@app.route('/add_item/<int:user_id>/<int:song_id>/<int:playlist_id>')
def add_item(user_id, song_id, playlist_id):
   new_item = Item(song_id = song_id, playlist_id = playlist_id)
   user = User.query.filter_by(id = user_id).first_or_404(description = "No such user found.")
   my_playlist = Playlist.query.filter_by(id = user.playlist_id).first()
   if not exists(new_item, my_playlist.items):
      song = Song.query.get(song_id)
      #using db session add the new item
      #increase the counter for the song associated with the new item
      #commit the database changes here
   return redirect(url_for('profile', user_id = user_id))

#Remove an item from a user's playlist
#Redirects back to the profile that issues the removal
@app.route('/remove_item/<int:user_id>/<int:item_id>')
def remove_item(user_id, item_id):
   #from the Item model, fetch the item with primary key item_id to be deleted
   #using db.session delete the item
   #commit the deletion
   return redirect(url_for('profile', user_id = user_id))
   
#Display the Dashboard page with a form for adding songs
#Renders the dashboard template
@app.route('/dashboard', methods=["GET", "POST"])
def dashboard():
  form = SongForm()
  if request.method == 'POST' and form.validate():
    new_song = None
    #create a new song here
    #add it to the database
    #commit to the database
  else:
        flash(form.errors)
  unpopular_songs = []  #add the ordering query here
  songs = Song.query.all()
  return render_template('dashboard.html', songs = songs, unpopular_songs = unpopular_songs, form = form)

Not entirely sure how I should interpret this?
As far as I can see in my code I’m not trying to redefine user anywhere?

Thanks in advance

Hi,

If I were to guess the root of the problem, it might have to with how your db is looking right now, combined with your model definitions. That being said, I don’t know how CC sets all this up. I usually always put a __tablename__ = 'user' in whatever model I’m making (user in this case, student if it’s class Student, etc.).

There may be some answers here for you: https://stackoverflow.com/questions/37908767/table-roles-users-is-already-defined-for-this-metadata-instance

Hey,

well the strange thing is that it runs fine when I try in my local environment.
So I would think that there might be a bug in the CC env?

Perhaps someone from CC can replicate?