2017-01-11 8 views
12

状況

Djangoのプロキシモデル別のデータベースに


は、我々は、機能の異なる種類のチケット支援システムからチケットを使用して、いくつかの異なるアプリケーションを持っています。

まず、チケットサポートシステムKayakoのモデルを表すいくつかのモデルを持つアプリケーションがあります。このアプリケーションは、それを利用する他のアプリケーションについては何も知ってはならず、可能な限り一般的なままにすべきです。このアプリケーションはKayakoの既存のテーブルを使用しているので、同じデータベース上で実行しています。このアプリケーションをkayakodbとしましょう。

1つのアプリケーションで、顧客データベースからチケットサポートシステムのチケットにリンクしています。これまでは、このシステムでは、チケットサポートシステム内でチケットを独自に表現し、kayakodbによって提供されるAPIを使用してチケットを照会しました。その後、顧客とドメインをリンクするチケットのこの表現を使用しました。しかしこれはあまりにも複雑であまり論理的ではありませんでした。そのため、プロキシモデルに切り替えて、顧客とドメインへのリンクを表すモデルをkayakodbに移動させました。このアプリケーションをsidebarとしましょう。

別の新しいアプリケーションでは、チケットサポートシステムからのチケットを通話とともに明確にまとめて表示しているため、サポート部門ではどの通話とチケットがどの顧客に関連しているかを簡単に確認できます。このシステムは、sidebarモデルによって提供される機能のいくつかが、新しいプロキシモデルが宣言する他のものとともにこのアプリケーションにも必要とされるので、プロキシモデルsidebarに対するプロキシモデルを有する。このプロジェクトをWOWと呼ぶことにしましょう。

sidebarWOWアプリケーションは、両方とも同じプロジェクト/リポジトリの一部です。このデータベースは、独自のデータベースを持つConeybeachと呼ばれます。しかし、kayakodbは全く関係のないプロジェクトです。それはpip経由でインストールする要件ファイルを介してConeybeachに含まれています。

問題


Djangoはもちろん、あるインストール kayakodb、ノー行くためのプロキシモデルの移行を作成し、新しいセットアップのためのマイグレーションを作成します。 kayakodbの新しいバージョンをインストールするときはいつでも、この移行を上書きします。 kayakodbは、どのモデルがそれを利用しているかを知るべきではありません。

コード


kayakodb内部 Ticketモデル:

class Ticket(models.Model): 
    """ 
    This model is a representation of the data stored in the "kayako" database table "swtickets". Minus a lot of stuff 
    we don't use. If you add a field make sure it has the same name as the field in kayako.swtickets. 
    """ 
    # Fields, functions and manager etc. 

    class Meta: 
     db_table = 'swtickets' 
     managed = False 

sidebar内部SidebarTicketプロキシモデル:

from kayakodb.models import Ticket  

class SidebarTicket(Ticket): 
    class Meta: 
     # Since this class is a wrapper we don't want to create a table for it. We only want to access the original 
     # model as we always do, but provide a different interface (when it comes to functions). Proxy models allow us 
     # to do this: https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models 
     proxy = True 

     # Don't look for this model in the sidebar tables, but in the kayakodb tables. 
     app_label = 'kayakodb' 

    # Some extra functions 

(からContactクラスTicketWrapper継承Hynekcによって要求されるようエル)。(私は承知しているなどの問題が限り、このモデルではありませんが)このモデルは、ベースTicketWrapperのためのモデルとのコールを表す別のモデルとして使用されます。

class Contact(models.Model): 
    type = None 

    class Meta: 
     abstract = True 

    def __getattr__(self, attr): 
     if attr in ['customers', 'add_customer_id', 'remove_all_customers', 'byters', 'domainnames', 'add_domain_name', 
        'remove_domain_name', 'add_text', 'remove_text', 'texts', 'creation_date', 'add_tag', 'get_tags', 
        'remove_tag', 'identifier']: 
      raise NotImplementedError('You should implement {}'.format(attr)) 
     raise AttributeError(attr) 

TicketWrapperプロキシモデルWOW内側:

from sidebar.models import SidebarTicket 

class TicketWrapper(Contact, SidebarTicket): 
    class Meta: 
     # Since this class is a wrapper we don't want to create a table for it. We only want to access the original 
     # model as we always do, but provide a different interface (when it comes to functions). Proxy models allow us 
     # to do this: https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models 
     proxy = True 

     # Don't look for this model in the WOW database, but in the kayakodb database. 
     app_label = 'kayakodb' 

    # Some extra functions 
私は目を指定しない試した
  • を試してみましたが、何

    両方のプロキシモデルの場合はapp_labelです。これにより適切な移行が行われますが、プロキシモデルはConeybeachデータベースのkayakodb.Ticketモデルを検索します。

  • 私はサブクラスのためにabstract = Trueを指定しようとしましたが、まだモデルのマネージャを利用できるようにしたいと思っていたので、これは確かではありませんでした。
  • 現在作成されている移行を実際のkayakodbプロジェクトに移行することを検討しましたが、これは良い解決策ではないと私は考えています。 kayakodbは、モデルの実装や使用されている場所については何も知らないようにしてください。
  • ./manage.py checkは0の問題を返します。

質問


は、どのように私は別のデータベースまたはプロジェクトに位置したモデルのためのプロキシモデルを作成することができますか?

編集


管理対象外であることを kayakodb.Ticketモデルを設定した後 WOWプロジェクトが kayakodbすべてモデルのマイグレーションを作成しようとします。結果:

Migrations for 'sidebar': 
    0004_auto_20170116_1210.py: 
    - Delete model Ticket 

Migrations for 'kayakodb': 
    0001_initial.py: 
    - Create model Staff 
    - Create model Tag 
    - Create model Ticket 
    - Create model TicketPost 
    - Create model TicketTag 
    - Create model TicketCustomer 
    - Create model TicketDomain 
    - Create proxy model SidebarTicket 
    - Alter unique_together for ticketdomain (1 constraint(s)) 
    - Alter unique_together for ticketcustomer (1 constraint(s)) 
    - Create proxy model TicketWrapper 
+0

@ e4c5はい、私はそれを知っています。それはうまくいっていますが、それが上書きされたときの結果です。 – Bono

+0

プロキシモデルが将来の移行で別のモデルによって参照される可能性があるため、あなた自身がそう言っています。後でそれを放棄することができれば、なぜそれは別の方法で移行を作成するのですか? – Bono

+0

あなたはそこにいます!近くの投票を取り消す – e4c5

答えて

4

ていると思うだろう既存のデータベースでは、すべてのモデルに対してmanaged = Falseを設定する必要があります。 しかし、これは間違ったアプリ(kayakodb)内に作成されたプロキシモデルの移行の問題を残しています。

ハッキーな解決策は、プロキシモデルのapp_labelをこのアプリケーションでは(この場合はsidebar)に移行し、このプロキシモデルを読み書きすることを指すルーターを作成することができるアプリケーションに変更することですkayakodb

など。プロキシモデル:

# in sidebar/models.py 

class SidebarTicket(KayakoTicket): 
    class Meta: 
     proxy = True 
     app_label = 'sidebar' 

し、それを使用するプロジェクト内のルータ:

from django.conf import settings 
from kayakodb.models import Ticket 

class ProxyDatabaseRouter(object): 
    def allow_proxy_to_different_db(self, obj_): 
     # check if this is a sidebar proxy to the Ticket model in kayakodb 
     return isinstance(obj_, Ticket) and obj_._meta.proxy and obj_._meta.app_label == 'sidebar' 

    def db_for_read(self, model, **hints): 
     if issubclass(model, Ticket) and model._meta.proxy and model._meta.app_label == 'sidebar': 
      return 'kayakodb' 
     # the rest of the method goes here 

    def db_for_write(self, model, **hints): 
     if issubclass(model, Ticket) and model._meta.proxy and model._meta.app_label == 'sidebar': 
      return 'kayakodb' 
     return None 
     # the rest of the method goes here 

    def allow_relation(self, obj1, obj2, **hints): 
     if self.allow_proxy_to_different_db(obj1) or self.allow_proxy_to_different_db(obj2): 
      return True 
     # the rest of the method goes here 
+0

SidebarTicketはプロキシだけなので、 'if model == SidebarTicket:'の条件はまだ無効ですが、データベースモデルがまだ十分に記述されていないため、正しい条件がわからないため、便利です。 – hynekcer

+1

@hynekcer私が言ったように、比較がいかに正確に行われたかは重要ではない、上の半分の擬似コードを考えてみよう。必要であれば DB読み出しおよび書き込みもモデルがルータに渡されているので問題されていない、 '(なし、モデル、「_CUSTOM_DATABASE」)ルータ方法は' GETATTRを返させる、モデル自体に格納されていることができませんメソッド。 –

+0

ルータのコードスニペットをより良いif節で更新し、 'allow_relation'メソッドが見つからない場合 –

4

TL; DRが、私は言及されていない単語routerのためのあなたの質問をgreppedので、私は@hynekcerがkayakodbがある場合に言ったように、あなたが探しているものがDatabase Routers

+0

質問が「別のデータベースまたはプロジェクトにあるモデルのプロキシモデルを作成するにはどうすればよいですか?」という回答は、「データベースルーターを使用する」ことができます。お手伝いを申し訳ありません。 – yedpodtrzitko

+0

申し訳ありませんが失礼であることを意味しませんでした。私は謝罪する。異なるデータベースのモデルに書き込むことは問題ではありません(私にとってはどんな場合でも)。問題は、マイグレーションが間違った場所に作成されていることです(質問を読んだ場合は、これを見たでしょう:))。質問を編集してDBルータに関する詳細情報を追加する場合は、何を教えてください(例を挙げてください)。私は私の下投票を取り除くことができるでしょう。純粋に質問そのものを見ると、私はこの_Could_が答えであることに同意します。 – Bono

4

TL;制御すべきではないDB内のすべてのモデルのためのDR使用

class Meta: 
    managed = False 

Djangoによって。 (即ちkayakodb)


PyPI Kayako別の言語で書かれたいくつかのKayakoアプリに(Djagoなし)PythonのAPIであることに留意すべきです。

KayakoとWOWは異なるデータベースにありますが、それは基本情報ではありません。 Djangoは、例えばその1つのモデルは、プライマリとセカンダリの2つのデータベースに存在します。最も重要なメタオプションは、この場合はmanaged = Falseです。 Integrating Django with a legacy databaseの場合です。 Python + Djangoで書かれていないプロジェクトや、移行をサポートしていないプロジェクトや、移行がまだ適用されていない情報を共有していないプロジェクトです。おそらく、新しいバージョンのKayakoがデータベースに新しいフィールドを追加する場合、そのフィールドを読む必要はなく、モデルに追加すると、Kayakoのアップグレードによって制御されるため、Djangoはフィールドをデータベースに追加する責任がありません。

データベースのルータを使用して、kayakodbアプリ(どこにも)のテーブルを作成せず、kayakodbデータベースにテーブルを作成することもできます。 Djangoのユーザーとグループのために:

ファイルmyrouter.pyまたは類似

class MyRouter(object): 
    allow_migrate(db, app_label, model_name=None, **hints): 
     if app_label == 'kayakodb' or db == 'kayakodb': 
      return False 

ファイルsettings.py

DATABASE_ROUTERS = ['path.to.myrouter.MyRouter',...] 
# ... if another router has been defined previously 

利点は、これが現在または将来のマイグレーションを無効にしていることですモデルですが、のコメントのどこかにあるテキストclass Meta: managed = Falseを追加することをお勧めします。最初にルータを読むことを忘れてしまいます。

Kayako APIの最小バージョンと最大バージョンのプロジェクトバージョンの依存関係を記述できますが、移行の形にはできません。


あなたのもう一つの問題は、 "A proxy model must inherit from exactly one non-abstract model class..." ということですが、ContactSidebarTicketから継承TicketWrapperプロキシモデル。ナンセンスのように見えますが、あなたはエラーが表示されないと思っていますTypeError: Proxy model 'TicketWrapper' has more than one non-abstract model base class.より多くのチケットで同じ連絡先を共有することができます。 (登録ユーザーではないのですか?問題の履歴中に彼のユーザープロフィールで何も変更できませんか?)多分継承ではなく、連絡するための外来キーでなければなりません。

+0

' Contact'はこの場合抽象的なDjangoモデルですので、問題ありません。私は 'kayakodb'モデルの' managed = False'を設定しました。しかし、 'WOW'プロジェクトでマイグレーションを作成しようとすると、' kayakodb'モデルすべてのマイグレーションを作成しようとします(アンマネージであっても)。動作する 'kayakodb'モデルのための移行を許可しないようにルータを設定するとき、これは私が必要とする(AFAIA)プロキシモデルの移行が作成されないことを意味します。ですから、私はまだkayakodbで指定されたモデルのプロキシモデル(またはそのモデルへの移行)を作成できないという問題に取り組んでいます。 – Bono

関連する問題