-
Notifications
You must be signed in to change notification settings - Fork 8
Commit v0.26
kwmccabe edited this page Dec 6, 2018
·
10 revisions
v0.26 - Link ItemModel to UserMode with item.owner_id
- +27 -23 [M] mysql/scripts/tables.sql
- +1 -0 [M] web/app/item/forms.py
- +17 -0 [M] web/app/item/models.py
- +5 -1 [M] web/app/item/templates/item_edit.html
- +8 -3 [M] web/app/item/templates/item_list.html
- +12 -1 [M] web/app/item/templates/item_view.html
- +45 -9 [M] web/app/item/views.py
- +19 -2 [M] web/app/user/models.py
- +1 -1 [M] web/app/user/templates/user_edit.html
- +1 -1 [M] web/app/user/templates/user_profile.html
- +10 -1 [M] web/app/user/templates/user_view.html
- +1 -1 [M] web/app/user/views.py
- +2 -1 [M] web/flaskapp.py
- Create table
user
prior to tableitem
. - Add column
owner_id
to tableitem
. - Add
FOREIGN KEY ('owner_id') REFERENCES user('id')
to tableitem
.
-- -------------------------------------------- --
--- item
--- -------------------------------------------- --
-DROP TABLE IF EXISTS `item`;
-CREATE TABLE `item` (
- `id` bigint(20) NOT NULL AUTO_INCREMENT,
- `active` tinyint(1) NOT NULL DEFAULT '1',
- `keyname` varchar(63) NOT NULL,
- `item_title` varchar(255) DEFAULT NULL,
- `item_text` text,
- `mod_create` datetime DEFAULT CURRENT_TIMESTAMP,
- `mod_update` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-
- PRIMARY KEY (`id`),
- UNIQUE KEY `item_keyname` (`keyname`),
- KEY `item_active` (`active`),
- KEY `item_title` (`item_title`),
- KEY `item_update` (`mod_update`)
-) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;
-DESCRIBE `item`;
-SELECT "table `item` created" AS MSG;
-- -------------------------------------------- --
-- user
-- -------------------------------------------- --
...
SELECT "table `user` created" AS MSG;
+
+
+-- -------------------------------------------- --
+-- item
+-- -------------------------------------------- --
+DROP TABLE IF EXISTS `item`;
+CREATE TABLE `item` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `active` tinyint(1) NOT NULL DEFAULT '1',
+ `keyname` varchar(63) NOT NULL,
+ `item_title` varchar(255) DEFAULT NULL,
+ `item_text` text,
+ `mod_create` datetime DEFAULT CURRENT_TIMESTAMP,
+ `mod_update` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `owner_id` bigint(20) NULL,
+
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `item_keyname` (`keyname`),
+ KEY `item_active` (`active`),
+ KEY `item_title` (`item_title`),
+ KEY `item_update` (`mod_update`),
+ FOREIGN KEY (`owner_id`) REFERENCES user(`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;
+DESCRIBE `item`;
+SELECT "table `item` created" AS MSG;
- Add
owner_id
element toEditItemForm
. -
choices
is populated by the view/admin/item/edit/
. -
coerce=int
converts id values to integers prior to the update.
mod_update = DateTimeField('Item Updated')
+ owner_id = SelectField('Item Owner', choices=[], coerce=int)
submit = SubmitField('Update Item')
- Add utility function
get_owner_id_choices()
to populatechoices
forEditItemForm.owner_id
. - Add column
ItemModel.owner_id
, specified as aForeignKey
. - Add
relationship
forItemModel.owner
to join theUserModel
onitem.owner_id = user.id
.
from flask import url_for
from datetime import datetime
from .. import db
+from ..user.models import UserModel
+
+
+def get_owner_id_choices():
+ result = []
+ rows = UserModel.query.order_by(getattr( UserModel, 'keyname' ).asc())
+ for row in rows:
+ result.append((row.id, "%s (%s)" % (row.keyname,row.user_email)))
+ return result
...
mod_create = db.Column(db.DateTime, default=datetime.utcnow)
mod_update = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, index=True)
+ # 'back_populates' requires reciprocal relationship in UserModel ; 'backref' creates both sides
+ # @see http://docs.sqlalchemy.org/en/latest/orm/relationship_api.html
+ owner_id = db.Column(db.Integer, db.ForeignKey('user.id'))
+ #owner = db.relationship('UserModel', back_populates='items')
+ owner = db.relationship('UserModel', backref='items')
+
def to_json(self):
json_item = {
#'url': url_for('api.get_item', id=self.id),
'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,
}
return json_item
- Place
EditItemForm.owner_id
within the templateitem_edit.html
. - Switch panel style per
item.active
status.
-<div id="item_edit_panel" class="panel panel-info">
+<div id="item_edit_panel" class="panel {% if form.active.data %}panel-info{% else %}panel-warning{% endif %}">
...
{{ form.id }}
+ <div class="form-group">
+ {{ form.owner_id.label }}
+ {{ form.owner_id(class_='form-control') }}
+ </div>
- Replace display of
item.owner_id
withitem.owner.keyname
.
- {{ col }}
+ {% if col == 'owner_id' %}Owner{% else %}{{ col }}{% endif %}
...
{% for col in cols %}
- <td onclick="window.location='{{ url_for('.item_view', id=row.id) }}';">{{ row[col] }}</td>
+ <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>
+ {% else %}
+ {{ row[col] }}
+ {% endif %}
+ </td>
{% endfor %}
- Use
item.owner_id
to link associateduser.id
. - Use
item.owner
to display associateduser.keyname
anduser.user_email
. - Switch panel style per
item.active
status.
-<div id="item_list_panel" class="panel panel-info">
+<div id="item_list_panel" class="panel {% if item.active %}panel-info{% else %}panel-warning{% endif %}">
...
+<tr>
+<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>
+ {% else %}
+ None
+ {% endif %}
+</td>
+</tr>
+
</table>
- Set
item.owner_id = current_user.id
automatically initem_create()
. - Populate owner
choices
forEditItemForm
viaform.owner_id.choices = get_owner_id_choices()
. - Update sorting options in
item_list()
to accommodateowner
relationship column. - Update test function
hello_orm()
to reflect possiblejoin(UserModel)
.
-from flask_login import login_required
+from flask_login import current_user, login_required
...
-from .models import ItemModel
+from .models import ItemModel, get_owner_id_choices
...
form = CreatItemForm(item)
if form.validate_on_submit():
form.populate_obj(item)
+ item.owner_id = current_user.id
db.session.add(item)
db.session.commit()
...
def item_edit( id ):
item = ItemModel.query.get_or_404(id)
form = EditItemForm(item)
+ form.owner_id.choices = get_owner_id_choices()
if form.validate_on_submit():
del form.mod_create, form.mod_update
form.populate_obj(item)
...
-@item.route('/admin/item/list')
+@item.route('/admin/item/list', methods=['GET','POST'])
@get_list_opts('item_list_opts')
@login_required
def item_list():
....
- if S['sort'] in cols:
+ if S['sort'] == 'owner_id':
+ from ..user.models import UserModel
+ rows = rows.outerjoin(UserModel)
+ if S['order'] == 'desc':
+ rows = rows.order_by(getattr( UserModel, 'keyname' ).desc())
+ else:
+ rows = rows.order_by(getattr( UserModel, 'keyname' ).asc())
+ elif S['sort'] in cols:
if S['order'] == 'desc':
...
@item.route('/hello_orm')
def hello_orm():
- cols = ItemModel.__table__.columns.keys()
+
rows = db.session.query(ItemModel)
+ #rows = rows.filter(ItemModel.id == 1000)
+
+ #from ..user.models import UserModel
+ #rows = rows.join(UserModel)
+ #rows = rows.order_by(getattr( UserModel, 'keyname' ).asc())
+ #rows = rows.filter(UserModel.id == 1000)
+ #rows = rows.join('owner')
+ rows = rows.outerjoin('owner')
+ rows = rows.order_by("item.owner_id IS NULL, user.keyname ASC")
+ #rows = rows.filter("user.id = 1000")
+
+ i = 0
+ cols_item = None
+ cols_user = None
result = '<b>db.session.query(ItemModel)</b>'
- result += '<br/>| '
- for col in cols:
- result += '<b>'+str(col)+'</b> | '
for row in rows:
+ if cols_item == None and row.__table__.columns:
+ cols_item = row.__table__.columns.keys()
+ if cols_user == None and row.owner.__table__.columns:
+ cols_user = row.owner.__table__.columns.keys()
+ if i == 0:
+ result += '<br/>| '
+ for col in cols_item:
+ result += '<b>item.'+str(col)+'</b> | '
+ for col in cols_user:
+ result += '<b>user.'+str(col)+'</b> | '
+
result += '<br/>| '
- for col in cols:
+ for col in cols_item:
result += '%s | ' % getattr( row, col )
+ for col in cols_user:
+ result += '%s | ' % getattr( row.owner, col ) if row.owner else 'None | '
+
+ i += 1
+
return result
- Add
db.relationship()
notes. - Insure proper init of
UserModel.cnt_login
. - Add
items_count
to JSON representation, using theitems
relationship.
from .. import db, login_manager
+#from ..item.models import ItemModel
...
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):
...
'mod_login' : self.mod_login,
'mod_create': self.mod_create,
'mod_update': self.mod_update,
+ #'items_url': url_for('api.get_user_items', id=self.id),
+ 'item_count': self.items.count()
}
return json_user
def __repr__(self):
- return '<UserModel %r>' % (self.id)
+ return '<UserModel: id="%r", keyname="%r">' % (self.id, self.keyname)
+
+ def __str__(self):
+ return 'User: "%r"' % (self.keyname)
- Switch panel style per
user.active
status.
-<div id="item_edit_panel" class="panel panel-info">
+<div id="item_edit_panel" class="panel {% if form.active.data %}panel-info{% else %}panel-warning{% endif %}">
- Switch panel style per
user.active
status.
-<div id="item_list_panel" class="panel panel-info">
+<div id="item_list_panel" class="panel {% if user.active %}panel-info{% else %}panel-warning{% endif %}">
- Display list of Items owned by this user.
- Switch panel style per
user.active
status.
-<div id="item_list_panel" class="panel panel-info">
+<div id="item_list_panel" class="panel {% if user.active %}panel-info{% else %}panel-warning{% endif %}">
...
+<tr>
+<td>User Items</td>
+<td><ol>
+{% for item in user.items %}
+ <li><a href="{{ url_for('item.item_view', id=item.id) }}">{{ item['keyname'] }}</a></li>
+{% endfor %}
+</ol></td>
+</tr>
+
</table>
- Add
cnt_login
andmod_login
to list of fields not updated inuser_edit()
.
- del form.mod_create, form.mod_update
+ del form.cnt_login, form.mod_login, form.mod_create, form.mod_update
- Add testing note to
hello_db()
re possibleitem-to-user
join.
+ #stmt = "select * from user join item on user.id=item.owner_id"
Commit-v0.25 | Commit-v0.26 | Commit-v0.27
- 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