Уведомления

Группа в Telegram: @pythonsu

#1 Июнь 15, 2011 22:57:00

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

Не в базах данных потому, что не уверен касается ли это именно баз или проектирования в общем
С базами данных пробую первый раз.

Пытаюсь сделать программку на SQLAlchemy.
Находясь на форуме, придумал тренировочное задание - хранить список тем одной доски форума.
Читаю темы из json-файла:

data.json

[
{
"msg_count": 12, # Количество сообщений в теме.
"number": 7005, # Номер темы на форуме (предполагаю возможность неуникальности, потому не делаю первичным ключом).
"name": "Topic number one.", # Название темы.
"dates": "2011.05.05", # Дата создания (пусть пока в виде строки).
"starter": "knkd" # Топикстартер.
},
{
"msg_count": 21,
"number": 5007,
"name": "Topic number two.",
"dates": "2011.01.01 ",
"starter": "ne_knkd"
},
{
"msg_count": 212,
"number": 5700,
"name": "Topic number three.",
"dates": "2011.09.09 ",
"starter": "ne_knkd_to"
}
]
Изначально программу делаю в самом простом варианте - с хранием имён пользователей в виде строки.
Всё работает. База создаётся и заполняется значениями.
Хотя способ проверки уникальности номера темы, возможно не самый лучший.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import json
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Topic(Base):
__tablename__ = 'topics'

id = Column(Integer, primary_key=True, nullable=False)

name = Column(String(128))
starter = Column(String(30))
date = Column(String(30), nullable=True)
number = Column(Integer)
msg_count = Column(Integer)

def __init__(self, data):
self.name = data["name"]
self.starter = data["starter"]
self.date = data["dates"]
self.number = data["number"]
self.msg_count = data["msg_count"]

def __repr__(self):
return "<Topic ({0}, {1}, {2})>".format(self.number,
self.msg_count, self.page_count)

def update(self, topic):
self.name = topic.name
self.starter = topic.starter
self.date = topic.date
self.number = topic.number
self.msg_count = topic.msg_count


def main():
engine = create_engine('sqlite:///./data.sqlite', echo=False)

metadata = Base.metadata
metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

data_list = json.load(open("data.json"))

for topic in data_list:
topic_object = Topic(topic)
dupl = session.query(Topic).filter_by(number=topic["number"]).first()

if dupl == None:
session.add(topic_object)
else:
dupl.update(topic_object)

session.commit()

if __name__ == '__main__':
main()
А теперь хочу отдельную таблицу с пользователями и хранить в таблице с темами только идентификатор.
Вопрос в том - где именно контролировать, есть ли такой пользователь в таблице?

Создавать отдельную функцию со всеми проверками?
Тогда слишком много логики будет за пределами соответствующего класса…

Проверять внутри конструктора класса Topic?
Тогда придётся передавать в конструктор сессию, а правильно ли это?

Может в алхимии это делается как-то по другому?

Код нерабочий, заготовка! (SQL-код создаётся адекватный, но логика не доделана)
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import json
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
__tablename__ = 'users'

id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(30), nullable=False, primary_key=True)

def __init__(self, user_name):
self.name = user_name

def __repr__(self, name):
return "<User {0}, {1}>".format(self.name, self.id)


class Topic(Base):
__tablename__ = 'topics'

id = Column(Integer, primary_key=True, nullable=False)

name = Column(String(128))
#starter = Column(String(30))
starter = Column(ForeignKey("users.id"))
date = Column(String(30), nullable=True)
number = Column(Integer)
msg_count = Column(Integer)

def __init__(self, data):
self.name = data["name"]
self.date = data["dates"]
self.number = data["number"]
self.msg_count = data["msg_count"]

#starter = data["starter"]
#user = session.query(User).filter_by(name=starter).first()


def __repr__(self):
return "<Topic ({0}, {1})>".format(self.number, self.starter)

def update(self, topic):
self.name = topic.name
self.starter = topic.starter
self.date = topic.date
self.number = topic.number
self.msg_count = topic.msg_count


def main():
engine = create_engine('sqlite:///data.sqlite', echo=False)

metadata = Base.metadata
metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

data_list = json.load(open("data.json"))

for topic in data_list:
topic_object = Topic(topic)
dupl = session.query(Topic).filter_by(number=topic["number"]).first()

if dupl == None:
session.add(topic_object)
else:
dupl.update(topic_object)

session.commit()

if __name__ == '__main__':
main()
непонятно…



Отредактировано (Июнь 15, 2011 22:58:59)

Офлайн

#2 Июнь 16, 2011 21:10:29

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

Получилось так.
Всё ещё не ясно - правильно ли хранить ссылку на сессию в каждом объекте?
Какие это может вызвать проблемы?

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import json
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class User(Base):
__tablename__ = 'users'

id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
name = Column(String(30), nullable=False, autoincrement=False, unique=True)

def __init__(self, session, name):
self.session = session
self.name = name

def __repr__(self):
return "<User {0}>".format(self.name)

def add(self):
dupl = self.session.query(User).filter_by(name=self.name).first()
if dupl == None:
self.session.add(self)


class Topic(Base):
__tablename__ = 'topics'

id = Column(Integer, primary_key=True, nullable=False)

name = Column(String(128), nullable=False)
dates = Column(String(30), nullable=True)
number = Column(Integer, nullable=False)
msg_count = Column(Integer, nullable=False)
page_count = Column(Integer, nullable=False)
starter = Column(ForeignKey("users.id"), nullable=False)

def __init__(self, session, data):
self._session = session

starter = data["starter"]
user = session.query(User).filter_by(name=starter).first()
if user == None:
user = User(session, starter)
user.add()

self.name = data["name"]
self.dates = data["dates"]
self.number = data["number"]
self.msg_count = data["msg_count"]
self.page_count = data["page_count"]
self.starter = session.query(User).filter_by(name=starter).first().id #user.id

def __repr__(self):
return "<Topic ({0}, {1}, {2})>".format(self.number,
self.msg_count, self.page_count)

def update(self, topic):
self.name = topic.name
self.starter = topic.starter
self.dates = topic.dates
self.number = topic.number
self.msg_count = topic.msg_count
self.page_count = topic.page_count

def add(self):
dupl = self._session.query(Topic).filter_by(number=self.number).first()

if dupl == None:
self._session.add(self)
else:
dupl.update(self)


def main():
engine = create_engine('sqlite:///data.sqlite', echo=False)

Session = sessionmaker(bind=engine)
session = Session()

metadata = Base.metadata
metadata.create_all(engine)

data_list = json.load(open("data.json"))

for topic in data_list:
topic_object = Topic(session, topic)
topic_object.add()

session.commit()

if __name__ == '__main__':
main()



Офлайн

#3 Июнь 17, 2011 22:18:26

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

Нда. Получается что я делаю руками то, что должна делать алхимия.
Но как работает релатионшип я так и не понял :(



Офлайн

#4 Июнь 18, 2011 01:43:36

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

SQLAlchemy и вопросы проектирования...

Все еще не понимаю ваше затруднение



Офлайн

#5 Июнь 18, 2011 17:34:23

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

Андрей Светлов
Все еще не понимаю ваше затруднение
1) Правильно ли отдавать сессию в каждый объект?
2) Как задать отношения без вот этого:
        user = session.query(User).filter_by(name=starter).first()
if user == None:
user = User(session, starter)
user.add()
...
dupl = self.session.query(User).filter_by(name=self.name).first()
if dupl == None:
self.session.add(self)
или это тоже вполне нормально?



Офлайн

#6 Июнь 20, 2011 01:54:42

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

SQLAlchemy и вопросы проектирования...

Вчитался внимательней. Сессию параметром конструктора давать не следует. Просто потому, что при поднятии из базы конструктор не вызывается, насколько помню.
Передавать сессию явно в метод — это нормально, если такое требуется.
Ваша же задача решается через obj = session.merge(obj)



Офлайн

#7 Июнь 20, 2011 21:10:28

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

Андрей Светлов
Вчитался внимательней. Сессию параметром конструктора давать не следует. Просто потому, что при поднятии из базы конструктор не вызывается, насколько помню.
Передавать сессию явно в метод — это нормально
Понятно, спасибо.
Я подозревал что это ненормально.

Андрей Светлов
Ваша же задача решается через obj = session.merge(obj)
А это не понял.
Merge просто позволит добавлять пользователя без предварительной проверки?



Офлайн

#8 Июнь 20, 2011 21:41:33

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

SQLAlchemy и вопросы проектирования...

Прочитайте что в доке по этому методу пишут.
Если совсем просто — то он добавит объект в сессию.
Если при этом по primary key в базе уже есть объект — вернется поднятый из базы.
Иначе копия объекта будет зарегистрирована в сессии.
В любом случае все последующие действия нужно проводить с объектом, возвращенным из .merge



Офлайн

#9 Июнь 20, 2011 21:59:04

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

Андрей Светлов
Если совсем просто — то он добавит объект в сессию.
Если при этом по primary key в базе уже есть объект — вернется поднятый из базы.
Это по факту всего лишь более короткий способ сделать вот так?

user = session.query(User).filter_by(name=starter).first()
if user == None:
user = User(session, starter)
user.add()



Офлайн

#10 Июнь 20, 2011 22:26:25

knkd
От:
Зарегистрирован: 2009-06-14
Сообщения: 225
Репутация: +  0  -
Профиль   Отправить e-mail  

SQLAlchemy и вопросы проектирования...

В примере из английской википедии:

from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation, sessionmaker

Base = declarative_base()

class Movie(Base):
__tablename__ = 'movies'

id = Column(Integer, primary_key=True)
title = Column(String(255), nullable=False)
year = Column(Integer)
directed_by = Column(Integer, ForeignKey('directors.id'))

director = relation("Director", backref='movies', lazy=False)

def __init__(self, title=None, year=None):
self.title = title
self.year = year
def __repr__(self):
return "Movie(%r, %r, %r)" % (self.title, self.year, self.director)

class Director(Base):
__tablename__ = 'directors'

id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False, unique=True)

def __init__(self, name=None):
self.name = name

def __repr__(self):
return "Director(%r)" % (self.name)

engine = create_engine('sqlite:///this.sqlite')
Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

m1 = Movie("Star Trek", 2009)
m1.director = Director("JJ Abrams")

d2 = Director("George Lucas")
d2.movies = [Movie("Star Wars", 1977), Movie("THX 1138", 1971)]

try:
session.add(m1)
session.add(d2)
session.commit()
except:
session.rollback()

alldata = session.query(Movie).all()
for somedata in alldata:
print somedata
хм…



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version