From f5f5d37eb6dedd45f0b50337b41e0aa7a5100daf Mon Sep 17 00:00:00 2001 From: Steven Loria Date: Fri, 18 Apr 2014 23:49:07 -0400 Subject: [PATCH] Remove many_to_one, use more explicit ReferenceCol instead Mostly because I couldn't get many_to_one to work with Flask-SQLA =/ --- .../{{cookiecutter.app_name}}/database.py | 50 ++++++------------- .../tests/test_models.py | 10 +++- .../{{cookiecutter.app_name}}/user/models.py | 14 +++++- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py index aba1e49..15e2f8e 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/database.py @@ -1,41 +1,12 @@ # -*- coding: utf-8 -*- -'''Database module, including the SQLAlchemy database object and DB-related +"""Database module, including the SQLAlchemy database object and DB-related utilities. -''' +""" from sqlalchemy.orm import relationship -from sqlalchemy.ext.declarative import declared_attr - from .extensions import db -# Helpers from Mike Bayer's atmcraft example app -# https://bitbucket.org/zzzeek/pycon2014_atmcraft/src/a6d96575bc49?at=master -def many_to_one(clsname, **kw): - """Use an event to build a many-to-one relationship on a class. - - This makes use of the :meth:`.References._reference_table` method - to generate a full foreign key relationship to the remote table. - - """ - @declared_attr - def m2o(cls): - cls._references((cls.__name__, clsname)) - return relationship(clsname, **kw) - return m2o - -def one_to_many(clsname, **kw): - """Use an event to build a one-to-many relationship on a class. - - This makes use of the :meth:`.References._reference_table` method - to generate a full foreign key relationship from the remote table. - - """ - @declared_attr - def o2m(cls): - cls._references((clsname, cls.__name__)) - return relationship(clsname, **kw) - return o2m - +relationship = relationship class CRUDMixin(object): """Mixin that adds convenience methods for CRUD (create, read, update, delete) @@ -56,24 +27,31 @@ class CRUDMixin(object): @classmethod def create(cls, **kwargs): - '''Create a new record and save it the database.''' + """Create a new record and save it the database.""" instance = cls(**kwargs) return instance.save() def update(self, commit=True, **kwargs): - '''Update specific fields of a record.''' + """Update specific fields of a record.""" for attr, value in kwargs.iteritems(): setattr(self, attr, value) return commit and self.save() or self def save(self, commit=True): - '''Save the record.''' + """Save the record.""" db.session.add(self) if commit: db.session.commit() return self def delete(self, commit=True): - '''Remove the record from the database.''' + """Remove the record from the database.""" db.session.delete(self) return commit and db.session.commit() + + +def ReferenceCol(tablename, nullable=False, **kwargs): + """Column that adds primary key foreign key reference.""" + return db.Column( + db.ForeignKey("{0}.id".format(tablename)), + nullable=nullable, **kwargs) diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/tests/test_models.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/tests/test_models.py index 5b974f0..46ef22a 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/tests/test_models.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/tests/test_models.py @@ -3,7 +3,7 @@ import unittest from nose.tools import * # PEP8 asserts from {{ cookiecutter.app_name }}.database import db -from {{ cookiecutter.app_name }}.user.models import User +from {{ cookiecutter.app_name }}.user.models import User, Role from .base import DbTestCase from .factories import UserFactory @@ -29,5 +29,13 @@ class TestUser(DbTestCase): user = UserFactory(first_name="Foo", last_name="Bar") assert_equal(user.full_name, "Foo Bar") + def test_roles(self): + role = Role(name='admin') + role.save() + u = UserFactory() + u.roles.append(role) + u.save() + assert_in(role, u.roles) + if __name__ == '__main__': unittest.main() diff --git a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py index ad49d80..38c804b 100644 --- a/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py +++ b/{{cookiecutter.app_name}}/{{cookiecutter.app_name}}/user/models.py @@ -3,10 +3,21 @@ import datetime as dt from flask.ext.login import UserMixin -from {{cookiecutter.app_name}}.database import db, CRUDMixin +from {{cookiecutter.app_name}}.database import ( + db, + CRUDMixin, + ReferenceCol, + relationship, +) from {{cookiecutter.app_name}}.extensions import bcrypt +class Role(CRUDMixin, db.Model): + __tablename__ = 'roles' + name = db.Column(db.String(80), unique=True, nullable=False) + user_id = ReferenceCol('users', nullable=True) + user = relationship('User', backref='roles') + class User(UserMixin, CRUDMixin, db.Model): __tablename__ = 'users' @@ -19,6 +30,7 @@ class User(UserMixin, CRUDMixin, db.Model): active = db.Column(db.Boolean()) is_admin = db.Column(db.Boolean()) + def __init__(self, username=None, email=None, password=None, first_name=None, last_name=None, active=False, is_admin=False):