-
Notifications
You must be signed in to change notification settings - Fork 8
Commit v0.28
kwmccabe edited this page Apr 17, 2018
·
8 revisions
v0.28 - Many-to-many link through ItemUserModel and new item_user table
- +7 -0 [M] mysql/scripts/seeddata.sql
- +16 -0 [M] mysql/scripts/tables.sql
- +1 -0 [M] web/app/item/forms.py
- +51 -5 [M] web/app/item/models.py
- +4 -0 [M] web/app/item/templates/item_edit.html
- +2 -2 [M] web/app/item/templates/item_list.html
- +17 -2 [M] web/app/item/templates/item_view.html
- +38 -5 [M] web/app/item/views.py
- +21 -17 [M] web/app/user/models.py
- +4 -4 [M] web/app/user/templates/user_view.html
- +10 -0 [M] web/app/user/views.py
- +8 -0 [M] web/flaskapp.py
- Add second test
user
. - Link item
one
touser
anduser2
.
DELETE FROM `user`;
INSERT INTO `user` (active,keyname,user_email) VALUES (True,"admin","[email protected]");
INSERT INTO `user` (active,keyname,user_email) VALUES (False,"user","[email protected]");
+INSERT INTO `user` (active,keyname,user_email) VALUES (False,"user2","[email protected]");
OPTIMIZE TABLE `user`;
...
+DELETE FROM `item_user`;
+insert into `item_user` (item_id,user_id) values (1000,1001);
+insert into `item_user` (item_id,user_id) values (1000,1002);
+OPTIMIZE TABLE `item_user`;
- Add new relational table
item_user
. -
item_id
references theitem
table. -
user_id
references theuser
table. -
relation
allows the link to be typed -'editor'
will be the default.
+-- -------------------------------------------- --
+-- item_user
+-- -------------------------------------------- --
+DROP TABLE IF EXISTS `item_user`;
+CREATE TABLE `item_user` (
+ `item_id` bigint(20) NOT NULL,
+ `user_id` bigint(20) NOT NULL,
+ `relation` varchar(31) NOT NULL DEFAULT 'editor',
+ PRIMARY KEY (`item_id`,`user_id`),
+ KEY `user_item` (`user_id`,`item_id`),
+ KEY `user_relation` (`relation`),
+ FOREIGN KEY (`item_id`) REFERENCES item(`id`) ON DELETE CASCADE,
+ FOREIGN KEY (`user_id`) REFERENCES user(`id`) ON DELETE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;
+DESCRIBE `item_user`;
+SELECT "table `item_user` created" AS MSG;
- Add a
SelectMultipleField
to theEditItemForm
, corresponding with theusers_id
element ofItemModel
. - This interface will not work with any significant number of system users - we'll need to upgrade it later.
owner_id = SelectField('Item Owner', choices=[], coerce=int)
+ users_id = SelectMultipleField('Editors', choices=[], coerce=int)
submit = SubmitField('Update Item')
- Create new class
ItemUserModel
with relationships toItemModel
andUserModel
. - Add
users_id
element toItemModel
using sqlalchemy'sassociation_proxy
.
+import logging
from flask import url_for
from datetime import datetime
+from sqlalchemy.ext.associationproxy import association_proxy
from .. import db
from ..user.models import UserModel
...
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
owner = db.relationship('UserModel', backref='items')
+ # association proxy of "item_users" collection to "users_id" attribute for EditItemForm
+ # @see http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/associationproxy.html
+ users_id = association_proxy('item_users', 'user.id')
+
def to_json(self):
json_item = {
#'url': url_for('api.get_item', id=self.id),
'id' : self.id,
- 'keyname' : self.keyname,
'active' : self.active,
+ 'keyname' : self.keyname,
'item_title': self.item_title,
'item_text' : self.item_text,
'mod_create': self.mod_create,
'mod_update': self.mod_update,
#'owner_url': url_for('api.get_user', id=self.owner_id),
'owner_id' : self.owner_id,
+ #'users_url': url_for('api.get_item_users', id=self.id),
+ 'users_count': self.item_users.count()
}
return json_item
+ def __init__(self, **kwargs):
+ super(ItemModel, self).__init__(**kwargs)
+ self.id = kwargs.get('id', None)
+ self.active = kwargs.get('active', True)
+ self.keyname = kwargs.get('keyname', None)
+ self.item_title = kwargs.get('item_title',None)
+ self.item_text = kwargs.get('item_text', 0)
+ self.mod_create = kwargs.get('mod_create',None)
+ self.mod_update = kwargs.get('mod_update',None)
+ logging.debug( "ItemModel.__init__: %r" % (self))
+ logging.debug( "ItemModel.__init__: owner=%r" % (self.owner))
+ logging.debug( "ItemModel.__init__: item_users=%r" % (self.item_users))
+
def __repr__(self):
- return '<ItemModel: id="%r", keyname="%r">' % (self.id, self.keyname)
+ return '<ItemModel(id=%r,active=%r,keyname=%r,item_title=%r,item_text=%r,mod_create=%r,mod_update=%r)>' \
+ % (self.id,self.active,self.keyname,self.item_title,self.item_text,self.mod_create,self.mod_update)
def __str__(self):
- return 'Item: "%r"' % (self.keyname)
+ return 'ItemModel:%r,%r' % (self.id,self.keyname)
+
+
+class ItemUserModel(db.Model):
+ __tablename__ = 'item_user'
+ item_id = db.Column(db.BigInteger, db.ForeignKey('item.id'), primary_key=True)
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), primary_key=True)
+ relation = db.Column(db.String(31), nullable=False, default='editor')
+
+ item = db.relationship("ItemModel", backref='item_users')
+ user = db.relationship("UserModel", backref='user_items')
+
+ def __init__(self, **kwargs):
+ super(ItemUserModel, self).__init__(**kwargs)
+ self.item_id = kwargs.get('item_id', None)
+ self.user_id = kwargs.get('user_id', None)
+ self.relation = kwargs.get('relation', 'editor')
+ logging.debug( "ItemUserModel.__init__: %r" % (self))
+ logging.debug( "ItemUserModel.__init__: item=%r" % (self.item))
+ logging.debug( "ItemUserModel.__init__: user=%r" % (self.user))
+
+ def __repr__(self):
+ return '<ItemUserModel(item_id=%r,user_id=%r,relation=%r)>' % (self.item_id,self.user_id,self.relation)
+
+ def __str__(self):
+ return 'ItemUserModel:%r,%r,%r' % (self.item_id,self.user_id,self.relation)
+
- Place the new
SelectMultipleField
within the edit page.
+ <div class="form-group">
+ {{ form.users_id.label }}
+ {{ form.users_id(class_='form-control',size='5') }}
+ </div>
{% for col in cols %}
<td onclick="window.location='{{ url_for('.item_view', id=row.id) }}';">
- {% if col == 'owner_id' and row[col] %}
- <a href="{{ url_for('user.user_view', id=row[col]) }}">{{ row['owner'].keyname }}</a>
+ {% if col == 'owner_id' and row.owner %}
+ <a href="{{ url_for('user.user_view', id=row[col]) }}">{{ row.owner.keyname }}</a>
{% else %}
{{ row[col] }}
{% endif %}
- List all Item Editors on the
item_view()
page.
- <h3 class="condensed">Item '{{ item.keyname }}'</h3>
+ <h3 class="condensed">Item '{{ item.keyname }}'{% if not item.active %} - inactive{% endif %}</h3>
...
<td>Item Owner</td>
<td>
{% if item.owner %}
- <a href="{{ url_for('user.user_view', id=item.owner_id) }}">{{ item.owner['keyname'] }} ({{ item.owner['user_email'] }})</a>
+ <a href="{{ url_for('user.user_view', id=item.owner_id) }}">{{ item.owner.keyname }} ({{ item.owner.user_email }})</a>
+ {% else %}
+ None
+ {% endif %}
+</td>
+</tr>
+
+<tr>
+<td>Item Editors</td>
+<td>
+ {% if item.item_users %}
+ <ul>
+ {% for iu in item.item_users %}
+ <li><a href="{{ url_for('user.user_view', id=iu.user_id) }}">{{ iu.user.keyname }} ({{ iu.user.user_email }})</a></li>
+ {% endfor %}
+ </ul>
{% else %}
None
{% endif %}
- Import the new
ItemUserModel
for use initem_edit()
. - Test route to list items, followed by linked users.
-from .models import ItemModel, get_owner_id_choices
+from .models import ItemModel, ItemUserModel, get_owner_id_choices
+from ..user.models import UserModel
...
item = ItemModel.query.get_or_404(id)
form = EditItemForm(item)
form.owner_id.choices = get_owner_id_choices()
+ form.users_id.choices = get_owner_id_choices()
if form.validate_on_submit():
del form.mod_create, form.mod_update
+
+ # convert user_id data to ItemUserModel objects
+ # delete previous relations not in current selection
+ item_users = ItemUserModel.query.filter_by(item_id=id).all()
+ for item_user in item_users:
+ if not item_user.user_id in form.users_id.data:
+ logging.debug('1- item_edit( delete:%s )' % (item_user))
+ db.session.delete(item_user)
+ db.session.commit()
+ # insert/update current relations
+ for user_id in form.users_id.data:
+ item_user = ItemUserModel.query.filter_by(item_id=id,user_id=user_id).first()
+ if not item_user:
+ item_user = ItemUserModel(item_id=id,user_id=user_id,relation='editor')
+ logging.debug('2- item_edit( insert:%s )' % (item_user))
+ db.session.add(item_user)
+ elif item_user.relation != 'editor':
+ item_user.relation = 'editor'
+ logging.debug('3- item_edit( update:%s )' % (item_user))
+ db.session.add(item_user)
+ # remove form.users_id prior to populate_obj(item)
+ del form.users_id
+
form.populate_obj(item)
db.session.add(item)
db.session.commit()
...
if S['sort'] == 'owner_id':
- from ..user.models import UserModel
rows = rows.outerjoin(UserModel)
...
- #rows = rows.all()
- #rowcnt = len(rows)
rowcnt = rows.count()
...
+@item.route('/hello_item_users')
+def hello_item_users():
+ rows = db.session.query(ItemModel)
+
+ result = '<b>db.session.query(ItemModel)</b>'
+ for row in rows:
+ result += '<br/>| %s | ' % (row)
+ for iu in row.item_users:
+ result += ' %s | ' % (iu.user)
+ return result
def load_user(user_id):
logging.info( "load_user(%s)" % user_id)
- return UserModel.query.filter_by(user_email=user_id).first()
+ return UserModel.query.filter_by(id=user_id).first()
...
mod_update = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
- # 'back_populates' requires reciprocal relationship in ItemModel
- # 'backref' creates both sides ; needs 'primaryjoin' because foreign key is on item table
- # @see http://docs.sqlalchemy.org/en/latest/orm/relationship_api.html
- #items = db.relationship('ItemModel', back_populates='owner')
- #items = db.relationship('ItemModel', backref='owner', primaryjoin='ItemModel.owner_id == UserModel.id')
-
- def __init__(self, **kwargs):
- super(UserModel, self).__init__(**kwargs)
- if self.cnt_login is None:
- self.cnt_login = 0
-
@property
def password(self):
...
def get_id(self):
- return self.user_email
+ return self.id
...
#'items_url': url_for('api.get_user_items', id=self.id),
- 'item_count': self.items.count()
+ 'items_count': self.user_items.count()
}
return json_user
+ def __init__(self, **kwargs):
+ super(UserModel, self).__init__(**kwargs)
+ self.id = kwargs.get('id', None)
+ self.active = kwargs.get('active', True)
+ self.keyname = kwargs.get('keyname', None)
+ self.user_email = kwargs.get('user_email',None)
+ self.cnt_login = kwargs.get('cnt_login', 0)
+ self.mod_login = kwargs.get('mod_login', None)
+ self.mod_create = kwargs.get('mod_create',None)
+ self.mod_update = kwargs.get('mod_update',None)
+ logging.debug( "UserModel.__init__: %r" % (self))
+ logging.debug( "ItemModel.__init__: items=%r" % (self.items))
+ logging.debug( "ItemModel.__init__: user_items=%r" % (self.user_items))
+
def __repr__(self):
- return '<UserModel: id="%r", keyname="%r">' % (self.id, self.keyname)
+ return '<UserModel(id=%r,active=%r,keyname=%r,user_email=%r,cnt_login=%r,mod_login=%r,mod_create=%r,mod_update=%r)>' \
+ % (self.id,self.active,self.keyname,self.user_email,self.cnt_login,self.mod_login,self.mod_create,self.mod_update)
def __str__(self):
- return 'User: "%r"' % (self.keyname)
+ return 'UserModel:%r,%r' % (self.id,self.keyname)
- <h3 class="condensed">User '{{ user.keyname }}'</h3>
+ <h3 class="condensed">User '{{ user.keyname }}'{% if not user.active %} - inactive{% endif %}</h3>
...
<tr>
<td>User Items</td>
-<td><ol>
+<td><ul>
{% for item in user.items %}
- <li><a href="{{ url_for('item.item_view', id=item.id) }}">{{ item['keyname'] }}</a></li>
+ <li><a href="{{ url_for('item.item_view', id=item.id) }}">{{ item.keyname }}</a></li>
{% endfor %}
-</ol></td>
+</ul></td>
</tr>
- Test route to list users, followed by linked items.
+@user.route('/hello_user_items')
+def hello_user_items():
+ rows = db.session.query(UserModel)
+
+ result = '<b>db.session.query(UserModel)</b>'
+ for row in rows:
+ result += '<br/>| %s | ' % (row)
+ for iu in row.user_items:
+ result += " %s | " % (iu.item)
+ return result
- Add some statements to our
hello_db()
method to query the newitem_user
table.
#stmt = "select * from user join item on user.id=item.owner_id"
+ #stmt = "select * from item_user"
+
+ #stmt = "SELECT i.id AS item_id, i.keyname, u.id AS user_id, u.keyname FROM item i JOIN item_user iu ON i.id = iu.item_id JOIN user u ON iu.user_id = u.id"
+ # users, cnt_items
+ #stmt = "SELECT u.keyname, COUNT(i.id) AS cnt_items FROM user u JOIN item_user iu ON u.id = iu.user_id JOIN item i ON i.id = iu.item_id GROUP BY u.keyname"
+ # items, cnt_users
+ #stmt = "SELECT i.keyname, COUNT(u.id) AS cnt_users FROM item i JOIN item_user iu ON i.id = iu.item_id JOIN user u ON u.id = iu.user_id GROUP BY i.keyname"
Commit-v0.27 | Commit-v0.28 | Commit-v0.29
- FlaskApp Tutorial
- Table of Contents
- About
- Application Setup
- Modules, Templates, and Layouts
- Database Items, Forms, and CRUD
- List Filter, Sort, and Paginate
- Users and Login
- Database Relationships
- API Module, HTTPAuth and JSON
- Refactoring User Roles and Item Status
- AJAX and Public Pages