12/08/2018, 13:08

SqlAlchemy trong python (part 3)

Tổng quan Trong những bài viết trước, chúng ta đã so sánh giữa SQLAlchemy và những Python O R M khác. Trong bài viết này , chúng ta sẽ đưa đến một cái nhìn rõ ràng hơn về SQLAlchemy O R M và những phép toán thông qua những ví dụ cụ thể. Không chỉ có SQLAlchemy O R M mới có khả ...

Tổng quan

  • Trong những bài viết trước, chúng ta đã so sánh giữa SQLAlchemy và những Python O R M khác. Trong bài viết này , chúng ta sẽ đưa đến một cái nhìn rõ ràng hơn về SQLAlchemy O R M và những phép toán thông qua những ví dụ cụ thể.

  • Không chỉ có SQLAlchemy O R M mới có khả năng cung cấp con đường thực hiện map đối tượng trong database với đối tượng Python. Nó chỉ cung cấp một cách thuận tiện hơn cho những Pythonic viết những query API. Để tìm một cái gì đó trong một SQLAlchemy database, điều thú vị khi sử dụng O R M này là kết quả trả về là những Python Object.

  • SQLALchemy Expression Language cung cấp một hệ thống cho người lập trình viết những câu lệnh SQL sử dung cấu trúc Python. , những hàm khởi tạo được mô hình hóa và được nhúng vào database và hoàn toàn đóng. Trong khi thực hiện sẽ che dấu giữa database backend và user. Hàm khởi tạo, tạo ra các tiến trình với một cấu trúc nhất quán, chúng dễ dàng cho việc sử dụng với những tính năng phụ trợ. Trước đó, những Expression Language cung cấp một con đường cho người lập trình viết những tính năng phụ trợ của mình., trong khi vẫn sử dụng được những lợi ích của các tính năng phụ trợ có sẵn nếu họ muốn.

  • Expression Language và ORM đều thực hiện việc Object Relational Mapper.Cả hai đều map các model với bảng và các mối quan hệ trong database được thể hiện thành mối quan hệ giữa các đối tượng , những lớp khác nhau trong python, và cũng như đề cập từ trước, các thuộc tính sẽ được map với các column.v.v

[code]

>>> from sqlalchemy import Column, String, Integer, ForeignKey
>>> from sqlalchemy.orm import relationship, backref
>>> from sqlalchemy.ext.declarative import declarative_base
>>>
>>>
>>> Base = declarative_base()
>>>
>>>
>>> class Department(Base):
...     __tablename__ = 'department'
...     id = Column(Integer, primary_key=True)
...     name = Column(String)
...
>>>
>>> class Employee(Base):
...     __tablename__ = 'employee'
...     id = Column(Integer, primary_key=True)
...     name = Column(String)
...     department_id = Column(Integer, ForeignKey('department.id'))
...     department = relationship(Department, backref=backref('employees', uselist=True))
...
>>>
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///')
>>>
>>> from sqlalchemy.orm import sessionmaker
>>> session = sessionmaker()
>>> session.configure(bind=engine)
>>> Base.metadata.create_all(engine)

[/code]

Ở trong ví dụ này, chúng ta đã tạo ra trong memory một sqlite database với 2 bảng 'department' và 'employees'. Cột 'employee.department_id' là foreign key tới cột 'department.id' và quan hệ 'deparment.employees' bao gồm tăt cả những 'employees' trong một 'department'. Để kiểm tra việc setup, chúng ta đơn giản chèn vào một số record và truy vấn chúng sử dụng SQLAlchemy O R M :

[code]

>>> john = Employee(name='john')
>>> it_department = Department(name='IT')
>>> john.department = it_department
>>> s = session()
>>> s.add(john)
>>> s.add(it_department)
>>> s.commit()
>>> it = s.query(Department).filter(Department.name == 'IT').one()
>>> it.employees
[]
>>> it.employees[0].name
u'john'

[/code]

Bạn có thể nhìn thấy kết quả của việc chèn thêm 1 employee vào bảng employee vào 1 IT department. bây giờ chúng ta sẽ tạo ra một số câu truy vấn sử dụng expression language.

[code]

>>> from sqlalchemy import select
>>> find_it = select([Department.id]).where(Department.name == 'IT')
>>> rs = s.execute(find_it)
>>> rs

>>> rs.fetchone()
(1,)
>>> rs.fetchone()  # Trả về một đối tượng đầu, nếu có, hoặc trả về None, nếu không có kết quả.
>>> rs.fetchone()  # Nếu fetchone trả về None, ta lại lấy thêm 1 kết quả trả về tiếp theo.

>>> find_john = select([Employee.id]).where(Employee.department_id == 1)
>>> rs = s.execute(find_john)

>>> rs.fetchone()  # Ta lấy được Employee ID của John
(1,)
>>> rs.fetchone()

[/code]

Đây là một ví dụ về cấu trúc ở lower-level của Expression Language., nó có vẻ viết như câu SQL một cách thủ công, nhưng đây là con đường của Pythonic.

Quan hệ nhiều nhiều (many-to-many) giữa departments và employees

Trong ví dụ trước, nó có vẻ đơn giản, một employee thuộc về duy nhất một department. Điều gì xảy ra nếu một employee có thể thuộc về nhiều departments? Sử dụng foreign Key là không đủ thể hiện mối quan hệ này .

Tất nhiên, một foreign key là không đủ, ở model có quan hệ many-to-many, giữa deparment và employee. chúng ta có thể tạo ra quan hệ mới với 2 foreign key , 1 liên kết đến department.id , cái khác với employee.id

[code]

>>> from sqlalchemy import Column, String, Integer, ForeignKey
>>> from sqlalchemy.orm import relationship, backref
>>> from sqlalchemy.ext.declarative import declarative_base
>>>
>>>
>>> Base = declarative_base()
>>>
>>>
>>> class Department(Base):
...     __tablename__ = 'department'
...     id = Column(Integer, primary_key=True)
...     name = Column(String)
...     employees = relationship('Employee', secondary='department_employee')
...
>>>
>>> class Employee(Base):
...     __tablename__ = 'employee'
...     id = Column(Integer, primary_key=True)
...     name = Column(String)
...     departments = relationship('Department', secondary='department_employee')
...
>>>
>>> class DepartmentEmployee(Base):
...     __tablename__ = 'department_employee'
...     department_id = Column(Integer, ForeignKey('department.id'), primary_key=True)
...     employee_id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
...
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///')
>>> from sqlalchemy.orm import sessionmaker
>>> session = sessionmaker()
>>> session.configure(bind=engine)
>>> Base.metadata.create_all(engine)
>>>
>>> s = session()
>>> john = Employee(name='john')
>>> s.add(john)
>>> it_department = Department(name='IT')
>>> it_department.employees.append(john)
>>> s.add(it_department)
>>> s.commit()

[/code]

ở ví dụ trên, chúng ta đã tạo ra mối quan hệ giữa các bảng sử dụng 2 foreign key. mối quan hệ tạo bảng 'department_employee'. có thể liên kết đến 'department' và 'employee'. mối quan hệ Department.employees và Employee.departments là mối qua hệ giữa các bảng với nhau. Một ma trận ảo xảy ra với tham số trung gian, chúng ta sẽ tạo ra hàm relationship() giữa Department và Employee ở các lớp model.

Chúng ta có thể kiểm tra sự cài đặt của chúng ta dựa trên một số câu query sau:

[code]

>>> john = s.query(Employee).filter(Employee.name == 'john').one()
>>> john.departments
[]
>>> john.departments[0].name
u'IT'
>>> it = s.query(Department).filter(Department.name == 'IT').one()
>>> it.employees
[]
>>> it.employees[0].name

[/code]

Giờ chúng ta sẽ chèn thêm một bản ghi employee vào 1 department vào database:

[code]

>>> marry = Employee(name='marry')
>>> financial_department = Department(name='financial')
>>> financial_department.employees.append(marry)
>>> s.add(marry)
>>> s.add(financial_department)
>>> s.commit()

[/code]

Để tìm tất cả các employee trong IT department, chúng ta có thể viết như sau (sử dụng O R M):

[code]

>>> s.query(Employee).filter(Employee.departments.any(Department.name == 'IT')).one().name
u'john'

[/code]

Hoặc sử dụng Expression Languge:

[code]

>>> find_employees = select([DepartmentEmployee.employee_id]).select_from(Department.__table__.join(DepartmentEmployee)).where(Department.name == 'IT')
>>> rs = s.execute(find_employees)
>>> rs.fetchone()
(1,)
>>> rs.fetchone()

[/code]

Chúng ta tiếp tục gán cho một employee, vốn ở IT department vào một đối tượng pepartments khác

[code]

>>> s.refresh(marry)
>>> s.refresh(it)
>>> it.employees
[]
>>> it.employees.append(marry)
>>> s.commit()
>>> it.employees
[, ]

[/code]

Để thực hiện tìm mary ... hoặc các employees thuộc về ít nhất 2 department, chúng ta sử dụng group_by và having trong O R M query:

[code]

>>> from sqlalchemy import func
>>> s.query(Employee).join(Employee.departments).group_by(Employee.id).having(func.count(Department.id) > 1).one().name

[/code]

Tương tự ở trong O R M query, chúng ta có thể sử dụng grouyp_by và having trong một Expression Language querery:

[code]

>>> find_marry = select([Employee.id]).select_from(Employee.__table__.join(DepartmentEmployee)).group_by(Employee.id).having(func.count(DepartmentEmployee.department_id) > 1)
>>> s.execute(find_marry)
>>> rs = _
>>> rs.fetchall()
[(2,)]

[/code]

Nhớ close session khi chúng ta làm xong các công việc :

[code]

>>> s.close()

[/code]

Tổng kết

  • Theo đánh giá chủ quan của bản thân tôi, thì thực sự khi xây dựng một database quan hệ sử dụng các câu lệnh sqlalchemy là khá đơn điệu, phức tạp. Chúng phù hợp hơn trong việc truy vấn kết quả trong database. Việc định nghĩa model chúng ta có thể sử dụng những cấu trúc có sẵn được hỗ trợ trong framework phát triển. Ví dụ nếu sử dụng Django, ta vẫn có thể sử dụng các câu trúc định nghĩa model của Django. Tuy nhiên, việc kết xuất dữ liệu, ta lại dùng sqlalchemy. Điều này phù hợp với mục tiêu của Sqlalchemy, là sử dụng các tính năng phụ trợ có sẵn, đồng thời cũng có thể tự viết ra những tính năng phụ trợ sao cho phù hơp. Ở phần kế tiếp, tôi sẽ trình bày một ví dụ cụ thể sử dụng ý tưởng này.

  • Tuy nhiên, theo đánh giá của bản thân là người phát triển, ý kiến tổng kết của tôi còn nhiều điểm thiển cận và chưa chính xác. Rất mong sự đóng góp của mọi người.

0