Для представления отношения «многие ко многим» между таблицами sites
и channels
существует третья, site_channels
, с дополнительным полем url
. Это модели и форма:
site.py:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from datetime import datetime
from slugify import slugify
from sqlalchemy import event
from sqlalchemy.ext.associationproxy import association_proxy
from common import db, slug_unique, UTCDateTime
from site_channel import _SiteChannel
class Site(db.Model):
__tablename__ = "sites"
id = db.Column(db.Integer, primary_key=True)
slug = db.Column(db.String(200), nullable=False, index=True)
name = db.Column(
db.String(200), nullable=False, index=True,
info={'label': 'Nome', 'validators': slug_unique}
)
title = db.Column(
db.String(200), nullable=False, info={'label': u'Título'}
)
channels_relationship = db.relationship(
_SiteChannel,
back_populates="site",
primaryjoin="and_(_SiteChannel.site_id == Site.id)",
cascade='all, delete, delete-orphan'
)
channels = association_proxy(
'channels_relationship', 'channel', creator=lambda x: _SiteChannel(channel=x)
)
updated_at = db.Column(UTCDateTime, nullable=False)
@property
def identifier(self):
return "%s/%s" % (self.owner.slug, self.slug)
@classmethod
def validate_site_slug(cls, target, value, oldvalue, initiator):
if not target.slug and value:
target.slug = slugify(value)
return value
def to_dict(self):
return {
'name': self.name,
'title': self.title,
'slug': self.slug,
}
@classmethod
def update_timestamp(cls, mapper, connection, target):
target.updated_at = datetime.utcnow()
return target
def channels_list(self):
return [chan_rel.to_dict() for chan_rel in self.channels_relationship]
event.listen(Site, 'before_insert', Site.update_timestamp, retval=False)
event.listen(Site, 'before_update', Site.update_timestamp, retval=False)
event.listen(Site.name, 'set', Site.validate_site_slug, retval=True)
channel.py:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from common import db
class _SiteChannel(db.Model):
__tablename__ = "site_channels"
site_id = db.Column(db.Integer, db.ForeignKey('sites.id'), primary_key=True)
channel_id = db.Column(db.Integer, db.ForeignKey('channels.id'), primary_key=True)
url = db.Column(db.String(200), nullable=False, info={'label': 'URL'})
site = db.relationship("Site")
channel = db.relationship("Channel")
def to_dict(self):
return {
'id': self.channel.id,
'url_part': self.channel.url_part,
'name': self.channel.name,
'url': self.url,
'site_id': self.site.id,
'site_name': self.site.name,
'site_title': self.site.title,
'site_slug': self.site.slug,
}
site_channel.py:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sqlalchemy.ext.associationproxy import association_proxy
from site_channel import _SiteChannel
from common import db
class Channel(db.Model):
__tablename__ = "channels"
id = db.Column(db.Integer, primary_key=True)
url_part = db.Column(db.String(200), nullable=False, info={'label': 'Label'})
name = db.Column(db.String(200), nullable=False, info={'label': 'Nome'})
sites_relationship = db.relationship(
'_SiteChannel',
back_populates="channel",
primaryjoin="and_(_SiteChannel.channel_id == Channel.id)",
cascade='all, delete, delete-orphan'
)
sites = association_proxy(
'sites_relationship', 'site', creator=lambda x: _SiteChannel(site=x)
)
def to_dict(self):
return {
'id': self.id,
'url_part': self.url_part,
'name': self.name,
}
@classmethod
def search_by_name(cls, name, exclude_ids=[]):
query = cls.query.filter(cls.name.like("%" + name + "%"))
if exclude_ids:
query = query.filter(~cls.id.in_(exclude_ids))
return query
forms.py:
#!/usr/bin/python
# -*- coding:utf-8 -*-
from flask.ext.wtf import Form
from wtforms_alchemy import model_form_factory
from wtforms.ext.sqlalchemy.fields import (
QuerySelectMultipleField
)
from common import db
from site import Site
from channel import Channel
BaseModelForm = model_form_factory(Form)
class ModelForm(BaseModelForm):
@classmethod
def get_session(self):
return db.session
class SiteForm(ModelForm):
channels = QuerySelectMultipleField(
query_factory=lambda: Channel.query.all(),
get_label='name'
)
class Meta:
model = Site
exclude = ['slug', 'updated_at']
При попытке отправить форму, как и ожидалось, возникает это исключение:
OperationalError: (OperationalError) (1048, "Column 'url' cannot be null") 'INSERT INTO site_channels (site_id, channel_id, url) VALUES (%s, %s, %s)' (1L, 1L, None)
Есть подсказки, как правильно писать form.py
?
Спасибо!
{{ form.title }}
…{{ form.slug }}
… и т. Д. Мне удалось заставить его работать после того, как освоил SQLAlchemy и WTForms. Я отправлю его как можно скорее. - person scorphus   schedule 21.10.2014