Question

I'm creating a simple blog on Flask and I'm trying to implement Flask-Admin to manage my posts. If I go to the admin area I can see a list of all my post from the DB but when I try to create a new one I got the next error:

Failed to create model. __init__() takes exactly 4 arguments (1 given)

This is my post model:

class Post(db.Model):
    __tablename__ = 'news'
    nid = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(100))
    content = db.Column(db.Text)
    created_at = db.Column(db.DateTime)

    def __init__(self, title, content):
        self.title = title.title()
        self.content = content
        self.created_at = datetime.datetime.now()

And this is my code to add the model to the UI:

from flask import Flask, session
from models import db, Post
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqlamodel import ModelView

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:pass@localhost/appname'
db.init_app(app)

admin = Admin(app)
admin.add_view(ModelView(Post, db.session))

I DO can edit models through the admin panel but not create new ones. I know I'm missing something really stupid but I can't figure out what it is.

Edit: it works if I don't implement init on the model. How can I fix this?

Was it helpful?

Solution

Take a look at the relevant part in the source code for Flask-Admin here.

The model is created without passing any arguments:

    model = self.model()

So you should support a constructor that takes no arguments as well. For example, declare your __init__ constructor with default arguments:

    def __init__(self, title = "", content = ""):
        self.title = title.title()
        self.content = content
        self.created_at = datetime.datetime.now()

OTHER TIPS

So, this is how I've implemented a Post class in my application:

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Unicode(80))
    body = db.Column(db.UnicodeText)
    create_date = db.Column(db.DateTime, default=datetime.utcnow())
    update_date = db.Column(db.DateTime, default=datetime.utcnow())
    status = db.Column(db.Integer, default=DRAFT)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))


    def __init__(self, title, body, createdate, updatedate, status, user_id):
        self.title = title
        self.body = body
        self.create_date = create_date
        self.update_date = update_date
        self.status = status
        self.user_id = user_id

If you're going to stick with instanciating your model with a created_at value of datetime.datetime.now(), you may want to reference my code above, wherein the equivalent datetime.utcnow() function is set as the default for create_date and update_date.

One thing I'm curious about is your use of self.title=title.title() and self.content = content.title(); are those values coming from a function?

If not and you're passing them as strings, I think you'd want to update those to self.title = title and self.content = content

That could explain why you're seeing your issue. If content.title() isn't a function, that would result in no argument for that parameter...

you might try using the following and seeing if it resolves your issue:

class Post(db.Model):
    __tablename__ = 'news'
    nid = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(100))
    content = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.datetime.now())

    def __init__(self, title, content, created_at):
        self.title = title
        self.content = content
        self.created_at = created_at

You need to pass the default argument in models init method and it will create models as should:

class Brand(Base):
    __tablename__ = 'Brand'
    id      = Column(Integer,    primary_key = True)
    name    = Column(String,     default = '')
    def __init__(self, name = ''): ### default argument passed
        self.name = name

This is a problem for me too. Passing default values work, but not if you want a custom value that you don't want to change in flask_admin. An example is, in my model I make a slug field on init. If I set it to slug="" in the init, I have to manually make the slug for each "post".

Posts made from CLI work, from flask_admin slug is blank unless I set it manually.

See code below (slugify is just a function that strips out characters, spaces etc and makes url-friendly slugs).

class Article(Base):
    __tablename__ = "articles"

    id = Column(Integer, primary_key=True)
    posted_time = Column(DateTime(timezone=True))
    updated_time = Column(DateTime(timezone=True))
    image = Column(String(200))
    article_title = Column(String(200))
    article_body = Column(String)
    slug = Column(String(100), unique=True)

    tags = relationship("ArticleTag", secondary=tags, backref="articles")
    categories = relationship("ArticleCategory", secondary=categories, backref="articles")

    def __init__(self, article_title="", posted_time=None, updated_time=None, image="", article_body="", slug=""):
        self.slug = slugify(article_title)
        self.article_title = article_title
        self.posted_time = posted_time
        self.updated_time = updated_time
        self.image = image
        self.article_body = article_body
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top