10

Tôi đang thiết kế một ứng dụng GUI nhỏ để bọc một sqlite DB (các hoạt động CRUD đơn giản). Tôi đã tạo ra ba mô hình SQLAlchemy (m_person, m_card.py, m_loan.py, tất cả trong một thư mục /models) mà trước đó đã có đoạn mã sau ở phía trên cùng của mỗi người:Có thể thực thi mã khi một mô-đun được nhập không?

from sqlalchemy import Table, Column, create_engine 
from sqlalchemy import Integer, ForeignKey, String, Unicode 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import backref, relation 

engine = create_engine("sqlite:///devdata.db", echo=True) 
declarative_base = declarative_base(engine) 
metadata = declarative_base.metadata 

này cảm thấy một chút sai (DRY) nên nó đã được đề xuất rằng tôi di chuyển tất cả các công cụ này ra cấp độ mô-đun (vào models/__init__.py).

from sqlalchemy import create_engine 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker 
from sqlalchemy import Table, Column, Boolean, Unicode 
from settings import setup 

engine = create_engine('sqlite:///' + setup.get_db_path(), echo=False) 
declarative_base = declarative_base(engine) 
metadata = declarative_base.metadata 

session = sessionmaker() 
session = session() 

..và nhập declarative_base như vậy:

from sqlalchemy import Table, Column, Unicode 
from models import declarative_base 


class Person(declarative_base): 
    """ 
    Person model 

    """ 
    __tablename__ = "people" 

    id = Column(Unicode(50), primary_key=True) 
    fname = Column(Unicode(50)) 
    sname = Column(Unicode(50)) 

Tuy nhiên tôi đã có rất nhiều phản hồi rằng thực thi mã như hàng nhập khẩu mô-đun như thế này là xấu? Tôi đang tìm kiếm một câu trả lời dứt khoát đúng cách để làm điều đó vì nó có vẻ như bằng cách cố gắng để loại bỏ sự lặp lại mã tôi đã giới thiệu một số thực hành xấu khác. Mọi phản hồi sẽ thực sự hữu ích.

(Dưới đây là get_db_path() phương pháp từ settings/setup.py cho đầy đủ như nó được gọi trong models/__init__.py mã trên.)

def get_db_path(): 

    import sys 
    from os import makedirs 
    from os.path import join, dirname, exists 
    from constants import DB_FILE, DB_FOLDER 

    if len(sys.argv) > 1: 
     db_path = sys.argv[1] 
    else: 
     #Check if application is running from exe or .py and adjust db path accordingly 
     if getattr(sys, 'frozen', False): 
      application_path = dirname(sys.executable) 
      db_path = join(application_path, DB_FOLDER, DB_FILE) 
     elif __file__: 
      application_path = dirname(__file__) 
      db_path = join(application_path, '..', DB_FOLDER, DB_FILE) 

    #Check db path exists and create it if not 
    def ensure_directory(db_path): 
     dir = dirname(db_path) 
     if not exists(dir): 
      makedirs(dir) 

    ensure_directory(db_path) 

    return db_path 
+1

2c của tôi, trong khi nói chung là OK để đặt mã init trong '__init __. Py' (đó là những gì nó có cho!), Đi xa như vậy để thiết lập URL cơ sở dữ liệu có vẻ sai với tôi. Nó có thể làm cho thử nghiệm khó khăn hơn. Dường như với tôi rằng những gì bạn nên làm là có một hàm 'init()' cho module. – cha0site

+0

Tôi nghĩ rằng vấn đề ở đây là lớp cơ sở 'declarative_base' được tạo động và dường như phụ thuộc vào cơ sở dữ liệu. Tôi không biết làm thế nào sqlalchemy hoạt động, nhưng là nó không thể xác định các lớp mô hình trước khi thực hiện kết nối DB ?? Tôi khó có thể tin được điều đó. –

+0

@NiklasB. 'Declarative_base' phụ thuộc vào" siêu dữ liệu "mà về cơ bản là động cơ sqlalchemy lõi. Nó không nhất thiết phải có bất kỳ mối quan hệ nào với cơ sở dữ liệu, mặc dù bạn có thể chọn cho nó một cái để đơn giản. – wberry

Trả lời

5

Một số framework phổ biến (Twisted là một ví dụ) thực hiện một thỏa thuận tốt về khởi tạo logic tại thời điểm nhập. Lợi ích của việc có thể xây dựng nội dung mô-đun động ở một mức giá, một trong số đó là các IDE không thể luôn luôn quyết định cái gì là "trong" mô-đun hay không.

Trong trường hợp cụ thể của bạn, bạn có thể muốn cấu trúc lại để công cụ cụ thể không được cung cấp vào thời gian nhập nhưng sau đó. Bạn có thể tạo siêu dữ liệu và lớp declarative_base của mình tại thời điểm nhập. Sau đó, trong thời gian bắt đầu, sau khi tất cả các lớp được xác định, bạn gọi số create_engine và liên kết kết quả với số sqlalchemy.orm.sessionmaker của bạn. Nhưng nếu nhu cầu của bạn rất đơn giản, bạn thậm chí có thể không cần phải đi xa như vậy.

Nói chung, tôi có thể nói đây không phải là Java hoặc C. Không có lý do gì để sợ làm những việc ở cấp mô-đun khác ngoài các hàm, lớp và hằng số xác định. Các lớp của bạn được tạo khi ứng dụng bắt đầu, sau khi ứng dụng bắt đầu. Một ít khỉ vá các lớp (trong cùng một mô-đun!), Hoặc tạo một hoặc hai bảng tra cứu toàn cầu, là OK theo ý kiến ​​của tôi nếu nó đơn giản hóa việc triển khai của bạn. Điều tôi chắc chắn sẽ tránh là bất kỳ mã nào trong mô-đun của bạn khiến đơn hàng nhập khẩu quan trọng đối với người dùng của bạn (khác với cách đơn giản là cung cấp logic được sử dụng), hoặc điều chỉnh hành vi của mã bên ngoài của bạn mô-đun. Sau đó, mô-đun của bạn trở thành ma thuật đen, được chấp nhận trong thế giới Perl (ala use strict;), nhưng tôi thấy không phải là "pythonic". Ví dụ: nếu mô-đun của bạn sửa đổi các thuộc tính của sys.stdout khi được nhập, tôi sẽ cho rằng hành vi đó thay vào đó nên được chuyển sang một hàm mà người dùng có thể gọi hoặc không.

2

Tôi cũng chạy vào tệp này và tạo tệp database.py với một lớp trình quản lý cơ sở dữ liệu, sau đó tôi đã tạo một đối tượng chung duy nhất. Bằng cách đó, lớp có thể đọc cài đặt từ tệp settings.py của tôi để định cấu hình cơ sở dữ liệu và lớp đầu tiên cần sử dụng đối tượng cơ sở (hoặc phiên/công cụ) sẽ khởi tạo đối tượng chung, sau đó mọi người chỉ sử dụng lại nó. Tôi cảm thấy thoải mái hơn khi có "từ dự án của tôi.cơ sở dữ liệu nhập khẩu DM" ở phía trên cùng của mỗi lớp sử dụng SQLAlchemy ORM, nơi DM là đối tượng cơ sở dữ liệu toàn cầu của tôi, và sau đó DM.getBase() để có được những đối tượng cơ sở

Đây là lớp database.py tôi:.

Session = scoped_session(sessionmaker(autoflush=True)) 

class DatabaseManager(): 
    """ 
    The top level database manager used by all the SQLAlchemy classes to fetch their session/declarative base. 
    """ 
    engine = None 
    base = None 

    def ready(self): 
     """Determines if the SQLAlchemy engine and base have been set, and if not, initializes them.""" 
     host='<database connection details>' 
     if self.engine and self.base: 
      return True 
     else: 
      try: 
       self.engine = create_engine(host, pool_recycle=3600) 
       self.base = declarative_base(bind=self.engine) 
       return True 
      except: 
       return False 

    def getSession(self): 
     """Returns the active SQLAlchemy session.""" 
     if self.ready(): 
      session = Session() 
      session.configure(bind=self.engine) 
      return session 
     else: 
      return None 

    def getBase(self): 
     """Returns the active SQLAlchemy base.""" 
     if self.ready(): 
      return self.base 
     else: 
      return None 

    def getEngine(self): 
     """Returns the active SQLAlchemy engine.""" 
     if self.ready(): 
      return self.engine 
     else: 
      return None 

DM = DatabaseManager() 
3

Về nguyên tắc không có gì sai với thực thi mã Python khi một module được nhập khẩu, trên thực tế là mỗi mô-đun Python duy nhất hoạt động cách. Xác định các thành viên mô-đun được thực thi mã, sau khi tất cả.

rằng Tuy nhiên, đặc biệt là của bạn trường hợp sử dụng, tôi khuyên bạn nên chống lại việc tạo một đối tượng phiên làm việc cơ sở dữ liệu singleton trong cơ sở mã của bạn. Bạn sẽ bị mất khả năng o làm nhiều việc, ví dụ đơn vị kiểm tra mô hình của bạn dựa vào một bộ nhớ SQLite trong bộ nhớ hoặc loại công cụ cơ sở dữ liệu khác.

Hãy xem documentation for declarative_base và lưu ý cách các ví dụ có thể tạo mô hình với lớp declarative_base được cung cấp chưa được gắn với một cơ sở dữ liệu. Lý tưởng nhất là bạn muốn làm điều đó và sau đó có một số loại hàm kết nối hoặc lớp sẽ quản lý việc tạo một phiên và sau đó liên kết nó với cơ sở.

+0

Nếu tôi hiểu chính xác, bạn đang khuyên không nên tạo một đối tượng phiên và sau đó chỉ cần chuyển nó xung quanh, nhưng thay vào đó để tạo một phiên mới mỗi lần tôi cần sử dụng nó? – johnharris85

+0

Không, bạn có thể tạo một và vượt qua nó, chỉ cần không tạo nó ở cấp mô-đun. Bạn có thể có một lớp được trả về bởi 'sessionmaker' hoặc một trong các trình bao bọc của nó ở cấp mô-đun, nhưng không có bất kỳ mức độ mô-đun nào của lớp đó. –

2

Không có gì sai khi thực thi mã tại thời điểm nhập.

Nguyên tắc đang thực hiện đủ để mô-đun của bạn có thể sử dụng được, nhưng không quá nhiều để nhập mô-đun là không cần thiết chậm và không quá giới hạn những gì có thể được thực hiện với nó. Thông thường, điều này có nghĩa là xác định các lớp, chức năng và tên toàn cầu (thực tế là cấp mô-đun của một từ khóa sai), cũng như nhập bất kỳ thứ gì mà các lớp, chức năng, v.v. của bạn cần phải hoạt động.

Điều này thường không liên quan đến việc tạo kết nối tới cơ sở dữ liệu, trang web hoặc tài nguyên động bên ngoài khác, mà là cung cấp chức năng để thiết lập các kết nối đó khi người dùng mô-đun sẵn sàng làm như vậy.