Unable to create models on Flask-admin
Asked Answered
D

4

14

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?

Dwarf answered 22/6, 2013 at 20:9 Comment(1)
Flask-Admin can't create model if it has constructor with non-default parameters - it does not know what to do with it. In this case, feel free to override create_model method (github.com/mrjoes/flask-admin/blob/master/flask_admin/contrib/…) and implement your own model creation logic.Krieg
P
21

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()
Purnell answered 22/6, 2013 at 23:46 Comment(2)
And there's a reason for that - Flask-Admin can't guess what model constructor parameters are and what they're supposed to receive as input.Krieg
The link is broken QQNoles
T
3

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
Tannenberg answered 22/6, 2013 at 22:22 Comment(3)
I'm using title() to save the value on title case. Actually I shouldn't be converting the content, mistake caused by too many hours of coding I guess. The thing is I'm having the same problem on every model so for example, on a registration form, I'm unable to get the password and store a hashed version of it.Dwarf
well, let's try with the Post class to begin with; have you tried the code I offered? See if that works for you. I'm curious, what do you mean by "I'm using title() to save the value on title case"? What are you saving it for? Where are you saving it to?Tannenberg
Yes, your code works, thanks. I store the post title on title case (This is a Post Title on Title Case). I still need to know how to pass the form values to the init method of the model.Dwarf
C
0

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
Callisto answered 10/9, 2015 at 0:53 Comment(0)
L
0

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
Lapointe answered 30/5, 2018 at 17:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.