Я думаю, что пытался попросить слишком много в моем предыдущем вопросе так извинения за это. Позвольте мне разметить свою ситуацию столь простым способом, как я могу на этот раз.
В основном у меня есть набор словарей, которые ссылаются на мои объекты, которые в свою очередь отображаются с помощью SQLAlchemy. Весь штраф со мной. Однако я хочу внести повторяющиеся изменения в содержание тех словарей. Проблема состоит в том, что выполнение так изменит объекты, они ссылаются на---, и использующий copy.copy () делает отрицательный результат, так как это только копирует ссылки, содержавшие в рамках словаря. Таким образом, даже если скопированный что-то, когда я пытаюсь, говорит print
содержание словаря, я только получу последние обновленные значения для объекта.
Поэтому я хотел использовать copy.deepcopy (), но это не работает с SQLAlchemy. Теперь я нахожусь в трудном положении, так как я должен скопировать определенные атрибуты своего объекта прежде, чем внести, сказали повторяющиеся изменения.
Таким образом, я должен использовать SQLAlchemy, и в то же время удостоверяются, что у меня может быть копия моих атрибутов объектов при внесении изменений, таким образом, я не изменяю сам ссылочный объект.
Совет, справка, предложения, и т.д.?
Edit:
Добавил некоторый код.
class Student(object):
def __init__(self, sid, name, allocated_proj_ref, allocated_rank):
self.sid = sid
self.name = name
self.allocated_proj_ref = None
self.allocated_rank = None
students_table = Table('studs', metadata,
Column('sid', Integer, primary_key=True),
Column('name', String),
Column('allocated_proj_ref', Integer, ForeignKey('projs.proj_id')),
Column('allocated_rank', Integer)
)
mapper(Student, students_table, properties={'proj' : relation(Project)})
students = {}
students[sid] = Student(sid, name, allocated_project, allocated_rank)
Таким образом атрибуты, которые я буду изменять, allocated_proj_ref
и allocated_rank
атрибуты. students_table
включается с помощью уникального студенческого идентификатора (sid
).
Question
Я хотел бы сохранить атрибуты, которые я изменяю выше - я имею в виду, это в основном, почему я решил использовать SQLA. Однако отображенный объект будет изменяться, который не рекомендуется. Таким образом, если я вношу изменения в doppelgänger, неотображенный объект... может я вносить те изменения и обновлять поля/таблицу для отображенного объекта.
В некотором смысле я следую за вторичным решением David, где я создаю другую версию Класса, который не отображается.
Я пытался использовать StudentDBRecord
решение, упомянутое ниже, но, получило ошибку!
File "Main.py", line 25, in
prefsTableFile = 'Database/prefs-table.txt')
File "/XXXX/DataReader.py", line 158, in readData
readProjectsFile(projectsFile)
File "/XXXX/DataReader.py", line 66, in readProjectsFile
supervisors[ee_id] = Supervisor(ee_id, name, original_quota, loading_limit)
File "", line 4, in __init__
raise exc.UnmappedClassError(class_)
sqlalchemy.orm.exc.UnmappedClassError: Class 'ProjectParties.Student' is not mapped
Делает это означает это Student
должен быть отображен?
Health warning!
Кто-то указал на действительно хорошую дополнительную проблему здесь. Посмотрите, даже если я звоню copy.deepcopy()
на неотображенном объекте, в этом случае, давайте предположим, что это - словарь студентов, который я определил выше, deepcopy делает копию из всего. Мой allocated_proj_ref
на самом деле a Project
объект, и у меня есть соответствие projects
словарь для этого.
Так я deepcopy оба students
и projects
- который я - он говорит, что у меня будут случаи где students
allocated_proj_ref
атрибут будет иметь проблемы с соответствием экземплярам в projects
словарь.
Таким образом я беру его, что я должен буду переопределить/переопределить (это - то, чем это называют не так ли?) deepcopy
в каждом использовании Класса def __deecopy__(self, memo):
или что-то как этот?
Я был бы я хотеть переопределить __deepcopy__
таким образом, что это игнорирует весь материал SQLA (которые являются
и
) но скопируйте все остальное, что это - часть отображенный класс.
Какие-либо предложения?
Если я правильно помню/думаю, в SQLAlchemy у вас обычно есть только один объект, соответствующий данной записи базы данных. Это делается для того, чтобы SQLAlchemy мог синхронизировать ваши объекты Python с базой данных, и наоборот (ну, не в том случае, если есть одновременные мутации БД извне Python, но это уже другая история). Итак, проблема заключается в том, что если вы скопируете один из этих сопоставленных объектов, то в результате получите два разных объекта, соответствующих одной и той же записи в базе данных. Если вы измените один из них, то они будут иметь разные значения, и база данных не сможет сопоставить их оба одновременно.
Я думаю, что вам нужно решить, хотите ли вы, чтобы запись в базе данных отражала изменения, которые вы делаете, когда изменяете атрибут вашей копии. Если да, то вам не следует вообще копировать объекты, а просто повторно использовать одни и те же экземпляры.
С другой стороны, если вы не хотите, чтобы оригинальная запись базы данных изменялась при обновлении копии, у вас есть другой выбор: должна ли копия стать новой строкой в базе данных? Или она вообще не должна быть сопоставлена с записью базы данных? В первом случае вы можете реализовать операцию копирования, создав новый экземпляр того же класса и скопировав значения, практически так же, как вы создавали исходный объект. Возможно, это будет сделано в методе __deepcopy__()
вашего сопоставленного класса SQLAlchemy. В последнем случае (без отображения) вам понадобится отдельный класс, который будет иметь все те же поля, но не будет отображаться с помощью SQLAlchemy. На самом деле, вероятно, было бы более разумно, если бы ваш сопоставленный с помощью SQLAlchemy класс был подклассом этого не сопоставленного класса, а сопоставление выполнялось бы только для подкласса.
EDIT: Хорошо, уточню, что я имел в виду под последним пунктом: сейчас у вас есть класс Student
, который используется для представления ваших студентов. Я предлагаю сделать Student
обычным классом без отображения,
class Student(object):
def __init__(self, sid, name, allocated_proj_ref, allocated_rank):
self.sid = sid
self.name = name
self.allocated_project = None
self.allocated_rank = None
и иметь подкласс, что-то вроде StudentDBRecord
, который будет отображаться на базу данных.
class StudentDBRecord(Student):
def __init__(self, student):
super(StudentDBRecord, self).__init__(student.sid, student.name,
student.allocated_proj_ref, student.allocated_rank)
# this call remains the same
students_table = Table('studs', metadata,
Column('sid', Integer, primary_key=True),
Column('name', String),
Column('allocated_proj_ref', Integer, ForeignKey('projs.proj_id')),
Column('allocated_rank', Integer)
)
# this changes
mapper(StudentDBRecord, students_table, properties={'proj' : relation(Project)})
Теперь вы будете реализовывать свой алгоритм оптимизации, используя экземпляры Student
, которые не отображены - поэтому при изменении атрибутов объектов Student
ничего не происходит с базой данных. Это означает, что вы можете спокойно использовать copy
или deepcopy
по мере необходимости. Когда все готово, вы можете изменить экземпляры Student
на экземпляры StudentDBRecord
, примерно так
students = ...dict with best solution...
student_records = [StudentDBRecord(s) for s in students.itervalues()]
session.commit()
Это создаст отображенные объекты, соответствующие всем вашим студентам в их оптимальном состоянии, и зафиксирует их в базе данных.
EDIT 2: Возможно, это не сработает. Быстрым решением будет скопировать конструктор Student
в StudentDBRecord
и сделать StudentDBRecord
расширением object
вместо этого. То есть, замените предыдущее определение StudentDBRecord
на следующее:
class StudentDBRecord(object):
def __init__(self, student):
self.sid = student.sid
self.name = student.name
self.allocated_project = student.allocated_project
self.allocated_rank = student.allocated_rank
Или, если вы хотите обобщить его:
class StudentDBRecord(object):
def __init__(self, student):
for attr in dir(student):
if not attr.startswith('__'):
setattr(self, attr, getattr(student, attr))
Это последнее определение будет копировать все неспециальные свойства Student
на StudentDBRecord
.
Вот еще один вариант, но я не уверен, что он применим к вашей проблеме:
lazy = 'connected'
или lazy = 'subquery'
, либо вызвать options (метод запроса eagerload (Relationship_property)
, либо просто доступ к требуемым свойствам для запуска их загрузки. Обновление : Вот пример кода концепции:
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relation, eagerload
metadata = MetaData()
Base = declarative_base(metadata=metadata, name='Base')
class Project(Base):
__tablename__ = 'projects'
id = Column(Integer, primary_key=True)
name = Column(String)
class Student(Base):
__tablename__ = 'students'
id = Column(Integer, primary_key=True)
project_id = Column(ForeignKey(Project.id))
project = relation(Project,
cascade='save-update, expunge, merge',
lazy='joined')
engine = create_engine('sqlite://', echo=True)
metadata.create_all(engine)
session = sessionmaker(bind=engine)()
proj = Project(name='a')
stud = Student(project=proj)
session.add(stud)
session.commit()
session.expunge_all()
assert session.query(Project.name).all()==[('a',)]
stud = session.query(Student).first()
# Use options() method if you didn't specify lazy for relations:
#stud = session.query(Student).options(eagerload(Student.project)).first()
session.expunge(stud)
assert stud not in session
assert stud.project not in session
stud.project.name = 'b'
session.commit() # Stores nothing
assert session.query(Project.name).all()==[('a',)]
stud = session.merge(stud)
session.commit()
assert session.query(Project.name).all()==[('b',)]