How to perform Validation on Flask-admin fields - python

I want to perform some validation on Flask Admin. The Flask app is connected to a backend postgreSQL DB and is using SQLAlchemy ORM.
I want to be able to perform validation checks on two fields (lan_nics, wan_nics) in the ServerView(flask.ext.admin.contrib.sqla.modelView). Basically I want to ensure the sum of two fields are not greater than a certain number. Here is the form:
ServerView
Here is the test_app code. I've tried to follow the documentation for flask-admin. I added a def and then referenced that def in the form_args for validators dictionary... but it doesn't seem to work. At the moment I'm just trying to ensure that both of the fields have a value less than 5.
from flask import Flask
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.sqlalchemy import SQLAlchemy
from wtforms.validators import ValidationError
import psycopg2
# Flask and Flask-SQLAlchemy initialization here
app = Flask(__name__)
db = SQLAlchemy(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:postgres#127.0.0.1/testdb'
app.secret_key = 'MySecret'
conn = psycopg2.connect("dbname='testdb' user='postgres' host='127.0.0.1' password='postgres'")
class Server(db.Model):
__tablename__ = "server"
id = db.Column('server_id', db.Integer, primary_key=True)
model = db.Column('model', db.Unicode)
lan_nics = db.Column('lan_nics', db.Integer)
wan_nics = db.Column('wan_nics', db.Integer)
def __str__(self):
return self.model
class Hardware(db.Model):
__tablename__ = "hardware"
id = db.Column('hardware_id', db.Integer, primary_key=True)
model = db.Column('model', db.Unicode)
max_nics = db.Column('max_nics', db.Integer)
def __str__(self):
return self.model
class ServerView(ModelView):
# Disable model creation
can_create = True
# Override displayed fields
column_list = ('model', 'lan_nics', 'wan_nics')
column_display_all_relations = True
column_searchable_list = ('model')
column_labels = dict(model='Model', lan_nics='LAN Nics', wan_nics='WAN NICs')
form_columns = [ 'model', 'lan_nics', 'wan_nics' ]
def max_allowed(form, field):
if field.data > 5:
raise ValidationError('Max number of interfaces exceeded')
form_args = dict(
lan_nics=dict(validators=[max_allowed]),
wan_nics=dict(validators=[max_allowed])
)
class HardwareView(ModelView):
# Disable model creation
can_create = True
# Override displayed fields
column_list = ('model', 'max_nics')
column_display_all_relations = True
column_searchable_list = ('model')
column_labels = dict(model='Model', max_nics='Max Nics')
form_columns = ['model', 'max_nics' ]
def __str__(self):
return self.model
admin = Admin(app, name="MyApp1")
# Add administrative views here
admin.add_view(ModelView(Server, db.session))
admin.add_view(ModelView(Hardware, db.session))
app.run(port=8000, host="0.0.0.0")

Instead of form_args, I did on_model_change:
class ServerView(ModelView):
...
...
...
def on_model_change(self, form, model, is_created):
hardware = "hardware"
query = "SELECT nics FROM hardware WHERE model = '{}'".format(hardware)
with conn.cursor() as cur:
cur.execute(query)
max_nics = int(cur.fetchone()[0])
if (int(form.lan.data) + int(form.wan.data) > max_nics):
raise ValidationError('Max number of interfaces exceeded!')
else:
return model

Actually, your first approach works. You have a small typo: instead of if field.data > 5: you should check for if len(field.data) > 5:.
The documentation of flask-admin does not really cover validators but under the hood, it does just delegate the work to WTForms. Their documentation is very helpful: https://wtforms.readthedocs.io/en/3.0.x/validators/ It also shows existing validators such as Length, you could have written instead:
from wtforms.validators import Length
form_args = dict(
lan_nics=dict(
validators=[Length(min=5, message='Max number of interfaces exceeded')]
),
)

Related

FlaskAdmin : triggering a function when clicking SAVE button

I'd like to add a bunch of program anytime the user is adding data to the model, after clicking the save button (or delete / edit).
In my program, one column is filled by raw SQL queries. So before inserting the record in the model, I'd like to program to test the query to make sure the SQL language has been correctly imputed.
here is the model:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
db = SQLAlchemy(app)
admin = Admin(app, template_mode='bootstrap3')
class rGraphs(db.Model):
__tablename__ = 'rGraphs'
graph_id = db.Column(db.Integer, primary_key=True)
Description = db.Column(db.Text, nullable=True)
TABLE_SRC = db.Column(db.String(50), nullable=True)
G_QUERY = db.Column(db.Text, nullable=True)
colfilters = db.relationship('tblParamColFilter', backref='rGraphs', lazy='dynamic')
coltransfos = db.relationship('tblParamColformat', backref='rGraphs', lazy='dynamic')
graphlayouts = db.relationship('tblParamGraphLayout', backref='rGraphs', lazy='dynamic')
def __repr__(self):
return self.Description
class rGraphsView(ModelView):
form_columns = ['graph_id', 'Description', 'TABLE_SRC','G_QUERY']
admin.add_view(rGraphsView(rGraphs, db.session, name='Graphics list'))
if __name__ == '__main__':
#si modele doit etre mis a jour
#manager.run()
app.run(debug=True)
You need to override the following methods in your inherited class rGraphsView: update_model(self, form, model), create_model(self, form) and delete_model(self, model).
A simple example:
class rGraphsView(ModelView):
form_columns = ['graph_id', 'Description', 'TABLE_SRC','G_QUERY']
def allow_update(self, form, model):
# test your form data and/or existing model data here
print form.G_QUERY.data
print model.G_QUERY
# return True or False
return True
def allow_create(self, form):
# test your form data here
print form.G_QUERY.data
# return True or False
return True
def allow_delete(self, model):
# test your model data here
print model.G_QUERY
# return True or False
return True
def update_model(self, form, model):
if allow_update(form, model):
# passes our allow_update test call super method
return super(rGraphsView, self).update_model(form, model)
else:
flash('Your failed update message here', 'error')
def create_model(self, form):
if allow_create(form):
# passes our allow_create test call super method
return super(rGraphsView, self).create_model(form)
else:
flash('Your failed create message here', 'error')
def delete_model(self, model):
if allow_delete(model):
# passes our allow_delete test call super method
return super(rGraphsView, self).delete_model(model)
else:
flash('Your failed delete message here', 'error')

column_filter by grandparent's property in flask-admin

I have three related SQLAlchemy models in my flask-admin application (simplified):
class Client(db.Model, BasicMixin, ActiveMixin, TimestampMixin):
id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
title = db.Column(db.String(1000))
issues = db.relationship('Issue', backref='client', cascade='all, delete-orphan')
class Issue(db.Model, BasicMixin, ActiveMixin, TimestampMixin):
id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
date = db.Column(db.Date, default=datetime.date.today())
client_id = db.Column(UUIDType, db.ForeignKey('clients.id'), nullable=False)
articles = db.relationship('Article', backref='issue', cascade='all, delete-orphan')
class Article(db.Model, BasicMixin, TimestampMixin):
id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
title = db.Column(db.String())
body = db.Column(db.String())
issue_id = db.Column(UUIDType, db.ForeignKey('issues.id'), nullable=False)
Client has many Issues, each Issue has many Articles.
I also have a ModelView for Article in which I should be able to filter list of Articles by Client (select Client by name and show Articles belonging to this Client only). What should I do to create such a filter in flask-admin?
Here's a single-file example using SQLite:
Mostly straightforward Flask, SQLalchemy and Flask-Admin. The class of interest is FilterByClientTitle.
class FilterByClientTitle(BaseSQLAFilter):
# Override to create an appropriate query and apply a filter to said query with the passed value from the filter UI
def apply(self, query, value, alias=None):
return query.join(Article.issue).join(Issue.client).filter(Client.title == value)
# readable operation name. This appears in the middle filter line drop-down
def operation(self):
return u'equals'
# Override to provide the options for the filter - in this case it's a list of the titles of the Client model
def get_options(self, view):
return [(client.title, client.title) for client in Client.query.order_by(Client.title)]
The view for the Article model has a couple of important settings/overrides:
class ArticleView(BaseAdminView):
# ......
# No need to specify the column as we'll set the SQLalchemy filter directly in the filter's apply method
column_filters = [FilterByClientTitle(column=None, name='Client Title')]
# Need this so the filter options are always up-to-date
#expose('/')
def index_view(self):
self._refresh_filters_cache()
return super(ArticleView, self).index_view()
Here's the complete example, requires the faker library for the random data:
import datetime
from flask import Flask, url_for
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin, expose
from faker import Faker
from flask_admin.contrib.sqla.filters import BaseSQLAFilter
from markupsafe import Markup
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sample_db.sqlite'
db = SQLAlchemy(app)
#app.route('/')
def index():
return 'Click me to get to Admin!'
class Client(db.Model):
__tablename__ = 'clients'
id = db.Column(db.Integer(), primary_key=True)
title = db.Column(db.String(1000))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return self.title
class Issue(db.Model):
__tablename__ = 'issues'
id = db.Column(db.Integer(), primary_key=True)
date = db.Column(db.Date, default=datetime.date.today())
client_id = db.Column(db.Integer(), db.ForeignKey('clients.id'), nullable=False)
client = db.relationship(Client, backref=db.backref('issues', uselist=True, cascade='all, delete-orphan'))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return unicode(self.date)
class Article(db.Model):
__tablename__ = 'articles'
id = db.Column(db.Integer(), primary_key=True)
title = db.Column(db.String())
body = db.Column(db.String())
issue_id = db.Column(db.Integer(), db.ForeignKey('issues.id'), nullable=False)
issue = db.relationship(Issue, backref=db.backref('articles', uselist=True, cascade='all, delete-orphan'))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return '{title} ... {body} ...'.format(title=self.title[:30], body=self.body[:30])
class BaseAdminView(ModelView):
can_view_details = True
named_filter_urls = True
class ClientView(BaseAdminView):
column_list = ('id', 'title')
column_default_sort = ('title', False)
column_filters = ['id', 'title']
class IssueView(BaseAdminView):
column_list = ('id', 'date', 'articles')
column_default_sort = ('date', False)
column_filters = ['id', 'date']
column_formatters = {
'articles': lambda v, c, m, n: Markup('<br>'.join([unicode(a) for a in m.articles])),
}
class FilterByClientTitle(BaseSQLAFilter):
# Override to create an appropriate query and apply a filter to said query with the passed value from the filter UI
def apply(self, query, value, alias=None):
return query.join(Article.issue).join(Issue.client).filter(Client.title == value)
# readable operation name. This appears in the middle filter line drop-down
def operation(self):
return u'equals'
# Override to provide the options for the filter - in this case it's a list of the titles of the Client model
def get_options(self, view):
return [(client.title, client.title) for client in Client.query.order_by(Client.title)]
class ArticleView(BaseAdminView):
column_list = ('title', 'body', 'issue', 'issue.client')
column_labels = {
'issue': 'Issue Date',
'issue.client': 'Client Title'
}
column_default_sort = ('title', False)
def issue_link(self, context, model, name):
return Markup('{date}'.format(
url=url_for('issue.index_view', flt1_id_equals=model.issue.id),
date=model.issue.date)
)
def client_link(self, context, model, name):
return Markup('{title}'.format(
url=url_for('client.index_view', flt1_id_equals=model.issue.client.id),
title=model.issue.client.title)
)
# Display Issue Date and Client Title as links back to their filtered views
column_formatters = {
'title': lambda v, c, m, n: '{} ...'.format(m.title[:20]),
'body': lambda v, c, m, n: '{} ...'.format(m.body[:40]),
'issue': issue_link,
'issue.client': client_link,
}
# No need to specify the column as we'll set the SQLalchemy filter directly in the filter's apply method
column_filters = [FilterByClientTitle(column=None, name='Client Title')]
# Need this so the filter options are always up-to-date
#expose('/')
def index_view(self):
self._refresh_filters_cache()
return super(ArticleView, self).index_view()
admin = Admin(app, template_mode="bootstrap3")
admin.add_view(ClientView(Client, db.session))
admin.add_view(IssueView(Issue, db.session))
admin.add_view(ArticleView(Article, db.session))
def build_sample_db():
fake = Faker()
number_of_clients = 100
number_of_issues_per_client = 5
number_of_articles_per_issues = 5
db.drop_all()
db.create_all()
clients = []
issues = []
articles = []
for client_counter in range(0, number_of_clients):
client_title = fake.last_name()
clients.append({
'id': client_counter,
'title': client_title
})
for issue_counter in range(0, number_of_issues_per_client):
issue_id = number_of_issues_per_client * client_counter + issue_counter
issues.append({
'id': issue_id,
'client_id': client_counter,
'date': fake.date_time_this_decade(before_now=True, after_now=False, tzinfo=None)
})
for article_counter in range(0, number_of_articles_per_issues):
articles.append({
'id': (number_of_articles_per_issues * issue_id) + article_counter,
'issue_id': issue_id,
'title': '{} - {}'.format(client_title, fake.catch_phrase()),
'body': '{} - {}'.format(client_title, fake.text(max_nb_chars=200))
})
db.session.bulk_insert_mappings(Client, clients)
db.session.bulk_insert_mappings(Issue, issues)
db.session.bulk_insert_mappings(Article, articles)
db.session.commit()
if __name__ == '__main__':
build_sample_db()
app.run(debug=True)

flask admin edit child objects on click

Flask-Admin shows child objects defined by relationships in its standard edit view. For example, if User objects have Address children, looking at the edit view for User will show the Address child in the appropriate field. The user can then remove the object, or add another one.
I want users to be able to click through, or otherwise have the ability to enter the edit view of child objects. In the example I'm describing, the user should be able to access the edit view of the Address object directly from the edit view of the User object.
The only thing I've found at all related is inline_models, but this isn't a solution. The implementation is extremely fragile (it can't handle long distance relationships, for example). Flask-Admin is aware of child objects! I can see them in the view! I just want them to become a link to their own edit view...
Anyone have any idea how to accomplish this or can link to an example?
Here is a single file simple example of placing a link to another model's edit view in an edit view. It may help you or not.
I've used a User - Address relationship, a user has an address and address can have many users.
I've used Faker to generate sample data so you'll need to pip install faker into your environment.
The idea is to use Flask-Admin form rules and in this case I'm configuring form_edit_rules.
I've created two custom rules:
Link, inheriting BaseRule. The constructor takes three values; an endpoint, a name of an attribute to pass along with the endpoint in the Flask url_for method and finally the text to appear as the link. In this example the endpoint is 'address.edit_view' because this is the view we want to link to.
MultiLink, similar to Link accepts it works with a relation.
Here's the code (there's little error checking):
from random import randint
from flask import Flask, url_for
from flask_admin.contrib import sqla
from flask_admin import Admin
from flask_admin.form.rules import BaseRule
from faker import Faker
from flask_sqlalchemy import SQLAlchemy
from markupsafe import Markup
from sqlalchemy import func, select
from sqlalchemy.ext.hybrid import hybrid_property
fake = Faker()
# Create application
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Create in-memory database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
# app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
# Flask views
#app.route('/')
def index():
return 'Click me to get to Admin!'
class Address(db.Model):
__tablename__ = 'addresses'
id = db.Column(db.Integer, primary_key=True)
number = db.Column(db.String(255))
street = db.Column(db.String(255))
city = db.Column(db.String(255))
country = db.Column(db.String(255))
#hybrid_property
def user_count(self):
return len(self.users)
#user_count.expression
def user_count(cls):
return select([func.count(User.id)]).where(User.address_id == cls.id).label("user_count")
def __unicode__(self):
return ', '.join(filter(None, [self.number, self.street, self.city, self.country]))
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(255))
last_name = db.Column(db.String(255))
email = db.Column(db.String(254))
address_id = db.Column(db.Integer, db.ForeignKey('addresses.id'), index=True)
address = db.relationship(Address, backref=db.backref('users'))
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
return '{} {}'.format(self.first_name, self.last_name)
class Link(BaseRule):
def __init__(self, endpoint, attribute, text):
super(Link, self).__init__()
self.endpoint = endpoint
self.text = text
self.attribute = attribute
def __call__(self, form, form_opts=None, field_args=None):
if not field_args
field_args = {}
_id = getattr(form._obj, self.attribute, None)
if _id:
return Markup('{text}'.format(url=url_for(self.endpoint, id=_id), text=self.text))
class MultiLink(BaseRule):
def __init__(self, endpoint, relation, attribute):
super(MultiLink, self).__init__()
self.endpoint = endpoint
self.relation = relation
self.attribute = attribute
def __call__(self, form, form_opts=None, field_args=None):
if not field_args
field_args = {}
_hrefs = []
_objects = getattr(form._obj, self.relation)
for _obj in _objects:
_id = getattr(_obj, self.attribute, None)
_link = 'Edit {text}'.format(url=url_for(self.endpoint, id=_id), text=str(_obj))
_hrefs.append(_link)
return Markup('<br>'.join(_hrefs))
class UserAdmin(sqla.ModelView):
can_view_details = True
form_edit_rules = (
'first_name',
'last_name',
'email',
'address',
Link(endpoint='address.edit_view', attribute='address_id', text='Edit Address')
)
class AddressAdmin(sqla.ModelView):
can_view_details = True
column_list = ['number', 'street', 'city', 'country', 'user_count', 'users']
form_edit_rules = (
'number',
'street',
'city',
'country',
'users',
MultiLink(endpoint='user.edit_view', relation='users', attribute='id')
)
admin = Admin(app, template_mode="bootstrap3")
admin.add_view(UserAdmin(User, db.session))
admin.add_view(AddressAdmin(Address, db.session))
def build_db():
db.drop_all()
db.create_all()
for _ in range(0, 20):
_users = []
for _ in range(0, randint(1, 10)):
_user = User(
first_name=fake.first_name(),
last_name=fake.last_name(),
email=fake.safe_email(),
)
_users.append(_user)
_address = Address(
number=fake.random_digit_not_null(),
street=fake.secondary_address(),
city=fake.city(),
country=fake.country(),
users = _users
)
db.session.add(_address)
db.session.commit()
#app.before_first_request
def first_request():
build_db()
if __name__ == '__main__':
app.run(port=5000, debug=True)

How do I setup dependent factories using Factory Boy and Flask-SQLAlchemy?

What is the correct way to use factory boy with Flask-SQLAlchemy and Foreign Key constraints?
Consider the following Flask SQLAlchemy Model setup:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
# ------------------------------
# SQLAlchemy Table Models
# ------------------------------
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'UserTable'
user_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'GroupTable'
group_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
# -------------------------
# Create the SQL tables
# -------------------------
db.create_all()
The User schema requires a Group foreign key when creating a new user. Since the Group primary key is assigned from the database, the factory would need to commit a group entry and get the entry's primary key so that it can provide it to the new User.
How do I create a group, save it to the DB, and provide it's key to the User Factory?
Factory Boy has examples for dealing with Foreign Keys but they don't seem to apply to SQLAlchemy. Here are the factories and the failure point:
# ----------------------------------------
# Factory-Boy User and Group Factories
# ----------------------------------------
from factory import alchemy, Sequence, RelatedFactory
class GroupFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = Sequence(lambda n: "Group {}".format(n))
# group_pk = Sequence(lambda n: n)
class UserFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = User
sqlalchemy_session = db.session # the SQLAlchemy session object
user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = RelatedFactory(GroupFactory)
# ----------------------
# Factory tests
# ----------------------
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
assert group_from_factory.group_pk is None
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first()
assert group_from_db.group_pk is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.group_pk == group_from_factory.group_pk
# Create a new User from our factory
user_from_factory = UserFactory()
db.session.add(user_from_factory)
# ----------------------------------------------
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null)
# ----------------------------------------------
db.session.commit()
assert user_from_factory.user_pk is not None
assert user_from_factory.name is not None
assert user_from_factory.group_fk is not None
The issue comes from using a RelatedFactory: those are intended for reverse ForeignKey relations (e.g if you want to build a Group object which already contains a User).
For a direct ForeignKey - like the relation from User to Group, use a SubFactory:
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = db.session
# No need to force the user_pk, it is built automatically from the database
# user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = factory.SubFactory(GroupFactory)
I'm not very familiar with Flask-SQLAlchemy, but I've just added a small example to the repository (at https://github.com/rbarrois/factory_boy/tree/master/examples/flask_alchemy) that works yet is quite similar to your situation.
Xelnor's git link shows the best answer so far, but it requires changes to the sqlalchemy model. Here is the finished working copy of my post using Xelnor's solution:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
SQLAlchemy Table Models
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'user'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_id = db.Column(db.Integer, db.ForeignKey('group.id'), nullable=False)
The 'group' db.relationship is what makes the SubFactory call work.
UserFactory passes group to the User model, which is setup with this
relationship() definition.
group = db.relationship('Group', backref=db.backref('groups', lazy='dynamic'))
def __init__(self, name, group):
self.name = name
self.group = group
def __repr__(self):
return '<Group for %r: %s>' % (self.group, self.name)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'group'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Group %r>' % self.name
Create the SQL tables
db.create_all()
Factory-Boy User and Group Factories
from factory import alchemy, Sequence, SubFactory, fuzzy
class BaseFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
abstract = True
sqlalchemy_session = db.session
class GroupFactory(BaseFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = fuzzy.FuzzyText()
class UserFactory(BaseFactory):
class Meta:
model = User
sqlalchemy_session = db.session
name = fuzzy.FuzzyText()
group = SubFactory(GroupFactory)
Factory tests
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
assert group_from_factory.id is None
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.id == group_from_factory.id).first()
assert group_from_db.id is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.id == group_from_factory.id
# Create a new User from our factory
user1_from_factory = UserFactory(name=u'first')
user2_from_factory = UserFactory(name=u'second')
db.session.add(user1_from_factory)
db.session.add(user2_from_factory)
db.session.commit()
assert user1_from_factory.id is not None
assert user1_from_factory.name is not None
assert user1_from_factory.id is not None
assert user2_from_factory.id is not None
assert user2_from_factory.name is not None
assert user2_from_factory.id is not None
You can use LazyAttribute and a lambda to generate a your new Group and then pull it's 'group_pk' out.
Working version of your code below:
# coding=utf-8
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
# ------------------------------
# SQLAlchemy Table Models
# ------------------------------
class User(db.Model):
""" A SQLAlchemy simple model class who represents a user with a ForeignKey Constraint"""
__tablename__ = 'UserTable'
user_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.Unicode(20))
group_fk = db.Column(db.ForeignKey("GroupTable.group_pk"), nullable=False)
class Group(db.Model):
""" A SQLAlchemy simple model class who represents a user """
__tablename__ = 'GroupTable'
group_pk = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(), nullable=False)
# -------------------------
# Create the SQL tables
# -------------------------
db.drop_all()
db.create_all()
# ----------------------------------------
# Factory-Boy User and Group Factories
# ----------------------------------------
from factory import alchemy, Sequence, LazyAttribute
class GroupFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = Group
sqlalchemy_session = db.session # the SQLAlchemy session object
name = Sequence(lambda n: "Group {}".format(n))
group_pk = Sequence(lambda n: n)
class UserFactory(alchemy.SQLAlchemyModelFactory):
class Meta(object):
model = User
sqlalchemy_session = db.session # the SQLAlchemy session object
user_pk = Sequence(lambda n: n)
name = Sequence(lambda n: u'User %d' % n) # coding=utf-8
group_fk = LazyAttribute(lambda a: GroupFactory().group_pk)
# ----------------------
# Factory tests
# ----------------------
# Create a new Group from our factory
group_from_factory = GroupFactory(name='a new group name')
# Save it to our DB
db.session.add(group_from_factory)
db.session.commit()
# Verify that Group Saved correctly to DB
group_from_db = db.session.query(Group).filter(Group.group_pk == group_from_factory.group_pk).first()
assert group_from_db.group_pk is not None
assert group_from_db.name == 'a new group name'
assert group_from_db.group_pk == group_from_factory.group_pk
# Create a new User from our factory
user_from_factory = UserFactory()
db.session.add(user_from_factory)
# ----------------------------------------------
# FAILS AT COMMIT() - NOT NULL constraint failed (group_fk is null)
# ----------------------------------------------
db.session.commit()
assert user_from_factory.user_pk is not None
assert user_from_factory.name is not None
assert user_from_factory.group_fk is not None

Google App Engine Django BooleanProperty not binding to table

I have the following,
class Company(db.Model):
companyvalid = db.BooleanProperty(required=True)
class AddCompanyForm(djangoforms.ModelForm):
class Meta:
model = Company
exclude = ['companyentrytime']
exclude = ['companylatlong']
however I cannot get the o/p from the Django stored in the database. I can also only add a record when the checkbox is checked, but this is not reflected in the underlying table when saving the record. What is the smartest way to do this? Thanks
class AddCompanyCategoriesHandler(webapp.RequestHandler):
def get(self):
memcache.flush_all()
form_requirements = AddCompanyCategoriesForm()
path = os.path.join(os.path.dirname(__file__), 'addcompanycat.html')
self.response.out.write(template.render(path, {'form': form_requirements}))
def post(self):
form_requirements = AddCompanyCategoriesForm(data=self.request.POST)
if form_requirements.is_valid():
myname = form_requirements.clean_data['categoryname']
entity = form_requirements.save(commit=False)
entity.put()
=========================================================================================
I'm trying to use the BooleanField, but this fails to work, with the server giving out a 504 error. Here is my model. I've been experimenting with this BooleanFields format, but I'm not sure how this relates to my model. My model is
class Company(db.Model):
companyurl = db.StringProperty(required=True)
companyname = db.StringProperty(required=True)
companydesc = db.TextProperty(required=True)
companyaddress = db.PostalAddressProperty(required=True)
companypostcode = db.StringProperty(required=True)
companyemail = db.EmailProperty(required=True)
companycountry = db.StringProperty(required=True)
companyvalid = db.BooleanProperty()
#companyvalid = db.BooleanField(required=True, label="Check this")
companyentrytime = db.DateTimeProperty(auto_now_add=True)
companylatlong = db.GeoPtProperty()
#property
def catname(self):
return self.companycategory.name
companycategory = db.ReferenceProperty(CompanyCategory, collection_name='compcategory')
and the following
class AddCompanyForm(djangoforms.ModelForm):
class Meta:
model = Company
#companyvalid = model.BooleanField(default=False)
exclude = ['companyentrytime']
exclude = ['companylatlong']
So my question is that if I have to use this BooleanField, how should I put it in the AddCompanyForm and should there be an entry in the model?
Try using BooleanField (https://docs.djangoproject.com/en/dev/ref/models/fields/#booleanfield) rather than Boolean Property in your model?

Categories

Resources