2013-04-11 25 views
7

私は3つのForeignKeyフィールドを持つシンプルなモデルを持っています。Django admin MySQL slow INNER JOIN

class Car(models.Model): 
    wheel = models.ForeignKey('Wheel', related_name='wheels') 
    created = models.DateTimeField(auto_now_add=True) 
    max_speed = models.PositiveSmallIntegerField(null=True) 
    dealer = models.ForeignKey('Dealer') 
    category = models.ForeignKey('Category') 

django管理者のリストビューでは、4つのクエリが表示されます。そのうちの1つは、3つのINNER JOINSを持つSELECTです。その1つのクエリは遅くする方法です。 INNER JOINをSTRAIGHT_JOINに置き換えると、問題が解決されます。管理者が作成したクエリを評価する直前に修正する方法はありますか?

答えて

4

私はそれがINNERと合流注文の場合はSTRAIGHT_JOINを使用しますが、DjangoのORMのために、JOIN INNERの修正を実装しました。私はDjangoのコア開発者と話をしました。私たちはこれを別のバックエンドとして今のところ決めることにしました。だからここでそれをチェックすることができます:https://pypi.python.org/pypi/django-mysql-fix

しかし、他の回避策があります。ジェームズの答えからスニペットを使用しますが、とselect_related置き換える:1 car_id IN(...)で車や3人をフェッチするために:それはINNER JOINをし、4つの別々のクエリを使用解除されます

qs = qs.select_related('').prefetch_related('wheel', 'dealer', 'category') 

更新: もう1つの回避策が見つかりました。 ForeignKeyフィールドにnull = Trueを指定すると、DjangoはINNER JOINではなくLEFT OUTER JOINを使用します。 LEFT OUTER JOINはこの場合パフォーマンスの問題なく動作しますが、私がまだ認識していないその他の問題に直面する可能性があります。

+1

'select_related( '')'は私のためには機能しませんでしたが、内部結合が起こらないようにしました。しかし、管理クラスで 'list_select_related = []'を指定すると、それはトリックでした! – rednaw

2

あなたはModelAdminクラスから継承されたあなたの管理者クラスの

def changelist_view(self, request, extra_context=None): 

方法

このような何か(ただし、この質問はかなり古いです)を上書きできます。 Django Admin: Getting a QuerySet filtered according to GET string, exactly as seen in the change list?

+0

私はChangeList.get_query_setへのアクセス権を持っていますが。 "RawQuerySet 'にはlen()"がありません "という文句があります。後でSQLにパッチを当てるためにクエリーセットが必要です。 – Titusz

+0

RawQuerySetのサブクラス化を試みることができると思います。 – singer

+0

ところで、あなたのデータベースのインデックスをチェックしましたか? 3つの結合はあまりあまりありません。 – singer

1

を[OK]を、私が見つけました管理者が生成したクエリにパッチを当てる方法。それは醜いですが、を動作するようです:

class CarChangeList(ChangeList): 

    def get_results(self, request): 
     """Override to patch ORM generated SQL""" 
     super(CarChangeList, self).get_results(request) 
     original_qs = self.result_list 
     sql = str(original_qs.query) 
     new_qs = Car.objects.raw(sql.replace('INNER JOIN', 'STRAIGHT_JOIN')) 

     def patch_len(self): 
      return original_qs.count() 
     new_qs.__class__.__len__ = patch_len 

     self.result_list = new_qs 


class CarAdmin(admin.ModelAdmin): 

    list_display = ('wheel', 'max_speed', 'dealer', 'category', 'created') 

    def get_changelist(self, request, **kwargs): 
     """Return custom Changelist""" 
     return CarChangeList 

admin.site.register(Rank, RankAdmin) 
+0

解決策は期待通りに機能しません。 .raw()は結合モデルへのマッピングを行いません。この場合、STRAIGHT_JOINでデータを要求し、モデルに存在しないすべてのフィールドを削除します。その後、関連するフィールドに対して別々の要求を行います。したがって、このオーバーライドではメリットはありません。 –

1

私は、MySQLに裏打ちされたときにはかなり単純な管理者リストのページが非常に遅かったDjangoの管理(バージョン1.4.9)で同じ問題に出くわしました。私の場合は

は、それはlist_displayどのフィールドが多対1の関係であればセットクエリに過度に広範なグローバルselect_related()を追加することChangeList.get_query_set()方法によって引き起こされました。適切なデータベース(咳PostgreSQLの咳)これは問題ではありませんが、いくつかの結合がこのようにトリガーされたのはもう一度MySQL用でした。

グローバルなselect_related()ディレクティブを、本当に必要なテーブルだけを結合した、よりターゲットを絞ったものに置き換えるのが最もクリーンな解決策でした。これは、明示的な関係名でselect_related()と呼ぶことでやりやすくなりました。

このアプローチでは、複数のフォローアップクエリのデータベース内結合を交換する可能性がありますが、MySQLが大きなクエリで窒息している場合は、小さなクエリが多くなることがあります。

はここで、多かれ少なかれ、私がやったことだ:

from django.contrib.admin.views.main import ChangeList 


class CarChangeList(ChangeList): 

    def get_query_set(self, request): 
     """ 
     Replace a global select_related() directive added by Django in 
     ChangeList.get_query_set() with a more limited one. 
     """ 
     qs = super(CarChangeList, self).get_query_set(request) 
     qs = qs.select_related('wheel') # Don't join on dealer or category 
     return qs 


class CarAdmin(admin.ModelAdmin): 

     def get_changelist(self, request, **kwargs): 
      return CarChangeList 
1

私はMySQLの管理クエリが遅く、最も簡単な解決策はクエリにSTRAIGHT_JOINを追加することだとわかりました。 .raw()に行くことを余儀なくされるのではなく、QuerySetにこれを追加する方法を考え出しました。これはadminとはうまく動作せず、django-mysqlの一部としてオープンソースになっています。その後、ちょうどあなたがすることができます

def get_queryset(self, request): 
    qs = super(MyAdmin, self).get_queryset(request) 
    return qs.straight_join() 
3

あなただけの内側の使用からジャンゴを防ぐためにlist_select_related =()を指定することが参加:私はCar.objectsでそれをパッチした場合

class CarAdmin(admin.ModelAdmin): 
    list_select_related =()