2012-10-24 23 views
50

Tôi đang cố gắng tạo "ứng dụng mô-đun" trong Flask bằng Bản thiết kế. Sử dụng Flask-SQLAlchemy trong các mô hình Blueprint mà không cần tham chiếu đến ứng dụng

Khi tạo mô hình, tuy nhiên, tôi đang gặp vấn đề phải tham chiếu ứng dụng để nhận được db -object do Flask-SQLAlchemy cung cấp. Tôi muốn để có thể sử dụng một số bản thiết kế với nhiều hơn một ứng dụng (tương tự như cách ứng dụng Django có thể được sử dụng), vì vậy đây không phải là một giải pháp tốt. *

  • Có thể làm một switcharoo, và có Blueprint tạo ra cá thể db, sau đó ứng dụng đó sẽ nhập cùng với phần còn lại của kế hoạch chi tiết. Nhưng sau đó, bất kỳ kế hoạch chi tiết nào khác muốn tạo mô hình cần phải nhập từ rằng kế hoạch chi tiết thay vì ứng dụng.

Câu hỏi của tôi là như sau:

  • Có cách nào để cho Blueprints xác định mô hình mà không cần bất kỳ nhận thức về ứng dụng họ đang được sử dụng trong sau này - và có một vài Blueprints đến với nhau? Bằng cách này, tôi có nghĩa là phải nhập mô-đun/gói ứng dụng từ Kế hoạch chi tiết của bạn.
  • Tôi có sai lầm ngay từ đầu không? Bản thiết kế Blueprint không có nghĩa là độc lập với ứng dụng và có thể phân phối lại được (ứng dụng Django à)?
    • Nếu không, thì mẫu nào nên bạn sử dụng để tạo thứ gì đó như thế? Flask mở rộng? Bạn có nên đơn giản là không làm điều đó - và có thể tập trung tất cả các mô hình/lược đồ Ruby on Rails?

Sửa: Tôi đã suy nghĩ về vấn đề này bản thân mình bây giờ, và điều này có thể có liên quan đến SQLAlchemy hơn Flask bởi vì bạn phải có declarative_base() mô hình khi tuyên bố. Và đó là phải đến từ đâu đó, dù sao đi nữa!

Có lẽ giải pháp tốt nhất là để lược đồ của dự án của bạn được xác định ở một nơi và phát tán nó xung quanh, như Ruby on Rails. Các định nghĩa lớp SQLAlchemy khai báo thực sự giống schema.rb hơn models.py của Django. Tôi tưởng tượng điều này cũng sẽ giúp việc sử dụng di chuyển dễ dàng hơn (từ alembic hoặc sqlalchemy-migrate).


Tôi đã yêu cầu cung cấp một ví dụ, vì vậy chúng ta hãy làm một cái gì đó đơn giản: Giả sử tôi có một kế hoạch chi tiết mô tả "flatpages" - đơn giản, "tĩnh" nội dung lưu trữ trong cơ sở dữ liệu. Nó sử dụng một bảng chỉ với tên viết tắt (cho các URL), một tiêu đề và một phần nội dung.Đây là simple_pages/__init__.py:

from flask import Blueprint, render_template 
from .models import Page 

flat_pages = Blueprint('flat_pages', __name__, template_folder='templates') 

@flat_pages.route('/<page>') 
def show(page): 
    page_object = Page.query.filter_by(name=page).first() 
    return render_template('pages/{}.html'.format(page), page=page_object) 

Sau đó, nó sẽ được tốt đẹp để cho kế hoạch chi tiết này xác định mô hình riêng của mình (điều này trong simple_page/models.py):

# TODO Somehow get ahold of a `db` instance without referencing the app 
# I might get used in! 

class Page(db.Model): 
    name = db.Column(db.String(255), primary_key=True) 
    title = db.Column(db.String(255)) 
    content = db.Column(db.String(255)) 

    def __init__(self, name, title, content): 
     self.name = name 
     self.title = title 
     self.content = content 

Câu hỏi này có liên quan đến:

Và nhiều thứ khác, nhưng tất cả các câu trả lời dường như dựa vào nhập phiên bản db của ứng dụng hoặc làm ngược lại. Trang wiki "Large app how to" cũng sử dụng mẫu "nhập ứng dụng của bạn trong kế hoạch chi tiết của bạn".

* Vì tài liệu chính thức cho thấy cách tạo tuyến đường, chế độ xem, mẫu và nội dung trong Kế hoạch chi tiết mà không quan tâm đến ứng dụng "in", tôi đã giả định rằng Blueprints nói chung có thể sử dụng lại trên các ứng dụng. Tuy nhiên, mô đun này không có vẻ là rằng hữu ích mà không có các mô hình độc lập.

Vì các bản thiết kế có thể được nối vào ứng dụng nhiều lần, có thể đơn giản là phương pháp tiếp cận sai để có mô hình trong Bản thiết kế?

+0

Bản thiết kế bạn đang tạo là ứng dụng có thể cắm * cần * để xác định các mô hình mà họ sử dụng? Hoặc họ có thể đơn giản là * đưa ra * mô hình từ ứng dụng? Nếu trước đây, bạn có thể đưa ra một ví dụ về một loại kế hoạch chi tiết có thể tái sử dụng mà bạn đang cố gắng tạo ra mà cần phải xác định các mô hình riêng của mình không? –

+1

Tôi đã cung cấp một ví dụ đơn giản, như bạn đã hỏi. Thật khó để nói về "cần" trong bối cảnh này: Nó không "cần" để xác định các mô hình riêng của nó nếu đó là cách cơ bản sai để tiếp cận nó, nhưng tôi đã bị ấn tượng rằng bạn nên "làm như thế nào". Tuy nhiên, tôi có thể hiểu nếu Flask được sử dụng tốt hơn như RoR trong đó giản đồ được xác định trong ứng dụng của bạn và "được cung cấp" cho các plugin khác nhau. Tôi đoán trong trường hợp đó, bạn sẽ sử dụng cấu hình để cung cấp? – vicvicvic

+0

Tại sao [giải pháp này] (http://stackoverflow.com/a/9695045/114833) không hoạt động trong trường hợp của bạn? Nó không tham chiếu ứng dụng trong mô hình hoặc mô-đun kế hoạch chi tiết. –

Trả lời

27

Tôi tin rằng câu trả lời đúng nhất là các bản thiết kế mô-đun không nên liên quan trực tiếp đến việc truy cập dữ liệu, mà thay vào đó dựa vào ứng dụng cung cấp triển khai tương thích.

Vì vậy, hãy cho bản thiết kế ví dụ của bạn.

from flask import current_app, Blueprint, render_template 

flat_pages = Blueprint('flat_pages', __name__, template_folder='templates') 

@flat_pages.record 
def record(state): 
    db = state.app.config.get("flat_pages.db") 

    if db is None: 
     raise Exception("This blueprint expects you to provide " 
         "database access through flat_pages.db") 

@flat_pages.route('/<page>') 
def show(page): 
    db = current_app.config["flat_pages.db"] 
    page_object = db.find_page_by_name(page) 
    return render_template('pages/{}.html'.format(page), page=page_object) 

Từ đó, không có gì ngăn cản bạn cung cấp triển khai mặc định.

def setup_default_flat_pages_db(db): 
    class Page(db.Model): 
     name = db.Column(db.String(255), primary_key=True) 
     title = db.Column(db.String(255)) 
     content = db.Column(db.String(255)) 

     def __init__(self, name, title, content): 
      self.name = name 
      self.title = title 
      self.content = content 

    class FlatPagesDBO(object): 
     def find_page_by_name(self, name): 
      return Page.query.filter_by(name=name).first() 

    return FlatPagesDBO() 

Và trong cấu hình của bạn.

app.config["flat_pages.db"] = setup_default_flat_pages_db(db) 

ở trên có thể được thực hiện sạch hơn bằng cách không dựa vào thừa kế trực tiếp từ db.Model và thay vào đó chỉ cần sử dụng một declarative_base vani từ SQLAlchemy, nhưng điều này sẽ đại diện cho các ý chính của nó.

+0

Rất thú vị! Tôi không biết nếu đây là cách bạn "nên làm điều đó" nhưng vì dường như không có một cách đúng để giải quyết vấn đề, tôi sẽ chấp nhận câu trả lời của bạn. – vicvicvic

0

Bạn hỏi "Có phải Blueprints không có nghĩa là không phụ thuộc vào ứng dụng và được phân phối (à la ứng dụng Django)?"

Câu trả lời là có. Bản thiết kế không giống với Django App.

Nếu bạn muốn sử dụng các ứng dụng/cấu hình khác nhau, thì bạn cần phải sử dụng "Gửi ứng dụng" chứ không phải bản thiết kế. Đọc [1]: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]

Ngoài ra, các liên kết ở đây [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]

Nó nói rõ ràng và tôi trích dẫn "Một kế hoạch chi tiết trong Flask không phải là một ứng dụng pluggable vì nó là không thực sự là một ứng dụng - đó là một tập hợp các hoạt động có thể được đăng ký trên một ứng dụng, thậm chí nhiều lần Tại sao không có nhiều đối tượng ứng dụng? ở lớp WSGI. "

+2

Phải, tôi đánh giá cao chúng không giống hệt Ứng dụng Django. Nhưng mô tả này không, với tôi, nói rằng chúng không phải là * độc lập * cũng không * phân phối lại *. Dường như với tôi rằng Blueprint * * có nghĩa là (nói chung) được đăng ký trên nhiều ứng dụng. Một ứng dụng Django không thực sự giống như một ứng dụng Flask - nó không đi kèm với cấu hình riêng của nó hoặc tách WSGI. – vicvicvic

1

Tôi có nhu cầu tương tự về việc tạo Bản thiết kế hoàn toàn theo mô-đun và không có tham chiếu đến Ứng dụng. Tôi đã đưa ra một giải pháp có thể sạch sẽ nhưng tôi không chắc nó chính xác như thế nào và những hạn chế của nó là gì.

Ý tưởng là tạo đối tượng db riêng biệt (db = SQLAlchemy()) bên trong kế hoạch chi tiết và gọi phương thức init_app()create_all() từ nơi ứng dụng gốc được tạo. Dưới đây là một số mã mẫu để hiển thị cách cấu trúc của dự án: Ứng dụng được gọi là jobs và kế hoạch chi tiết được gọi là status và được lưu trữ bên trong thư mục bản thiết kế.

blueprints.status.models.py

from flask_sqlalchemy import SQLAlchemy 
db = SQLAlchemy() # <--- The db object belonging to the blueprint 

class Status(db.Model): 
    __tablename__ = 'status' 
    id = db.Column(db.Integer, primary_key=True) 
    job_id = db.Column(db.Integer) 
    status = db.Column(db.String(120)) 

models.py

from flask_sqlalchemy import SQLAlchemy 
db = SQLAlchemy() # <--- The db object belonging to the root app 

class Job(db.Model): 
    __tablename__ = 'job' 
    id = db.Column(db.Integer, primary_key=True) 
    state = db.Column(db.String(120) 

factory.py

from .blueprints.status.models import db as status_db # blueprint db 
from .blueprints.status.routes import status_handler # blueprint handler 
from .models import db as root_db      # root db 
from flask import Flask 

def create_app(): 
    app = Flask(__name__) 

    # Create database resources. 
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////path/to/app.db' 
    root_db.init_app(app) 
    status_db.init_app(app)  # <--- Init blueprint db object. 
    with app.app_context(): 
     root_db.create_all() 
     status_db.create_all() # <--- Create blueprint db. 

    # Register blueprint routes. 
    app.register_blueprint(status_handler, url_prefix="/status") 

    return app 

Tôi đã thử nghiệm nó với gunicorn với gevent lao động và nó hoạt động. Tôi đã hỏi một câu hỏi riêng về tính chắc chắn của giải pháp tại đây: Create one SQLAlchemy instance per blueprint and call create_all multiple times

Các vấn đề liên quan