Django'nun test veritabanı yalnızca bellekte nasıl çalıştırılır?


125

Django birim testlerimin çalışması uzun sürüyor, bu yüzden bunu hızlandırmanın yollarını arıyorum. Bir SSD kurmayı düşünüyorum , ancak bunun da dezavantajları olduğunu biliyorum. Elbette, kodumla yapabileceğim şeyler var, ancak yapısal bir düzeltme arıyorum. Veritabanının her seferinde yeniden oluşturulması / güneye taşınması gerektiğinden tek bir test çalıştırmak bile yavaştır. İşte benim fikrim ...

Test veritabanının her zaman oldukça küçük olacağını bildiğim için, neden sistemi tüm test veritabanını her zaman RAM'de tutacak şekilde yapılandıramıyorum? Diske asla dokunmayın. Bunu Django'da nasıl yapılandırırım? Üretimde kullandığım şey bu olduğundan MySQL'i kullanmaya devam etmeyi tercih ederim , ancak SQLite  3 veya başka bir şey bunu kolaylaştırırsa, o yoldan giderim.

SQLite veya MySQL'in tamamen bellekte çalışma seçeneği var mı? Bir RAM diski yapılandırmak ve ardından test veritabanını verilerini orada depolayacak şekilde yapılandırmak mümkün olmalıdır, ancak Django / MySQL'e belirli bir veritabanı için farklı bir veri dizini kullanmasını nasıl söyleyeceğimi bilmiyorum, özellikle de silinmeye devam ettiği için ve her koşuyu yeniden yarattı. (Mac FWIW kullanıyorum.)

Yanıtlar:


164

Testlerinizi çalıştırdığınızda veritabanı motorunuzu sqlite3 olarak ayarlarsanız, Django bellek içi bir veritabanı kullanacaktır .

settings.pyTestlerimi çalıştırırken motoru sqlite olarak ayarlamak için şu şekilde bir kod kullanıyorum :

if 'test' in sys.argv:
    DATABASE_ENGINE = 'sqlite3'

Veya Django 1.2'de:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'sqlite3'}

Ve son olarak Django 1.3 ve 1.4'te:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}

(Arka uca giden tam yol Django 1.3 ile kesinlikle gerekli değildir, ancak ayarı ileriye dönük uyumlu hale getirir.)

Güney göçlerinde sorun yaşamanız durumunda aşağıdaki satırı da ekleyebilirsiniz:

    SOUTH_TESTS_MIGRATE = False

9
Evet kesinlikle. Bunu cevabıma koymalıydım! SOUTH_TESTS_MIGRATE = False ile birleştirin ve testleriniz çok daha hızlı olmalı.
Etienne

7
Bu ise müthiş. daha yeni django kurulumlarında şu satırı kullanın: 'ENGINE': 'sqlite3' sys.argv'de 'test' ise başka 'django.db.backends.mysql',
mjallday

3
@Tomasz Zielinski - Hmm, neyi test ettiğinize bağlı. Ancak, en sonunda ve zaman zaman testleri gerçek veritabanınızla (Postgres, MySQL, Oracle ...) çalıştırmanız gerektiğine tamamen katılıyorum. Ancak testlerinizi sqlite ile bellek içinde çalıştırmak size çok zaman kazandırabilir.
Etienne

3
-1'i + 1'e çeviriyorum: şimdi gördüğüm gibi, hızlı çalıştırmalar için sqlite kullanmak ve örneğin son günlük testler için MySQL'e geçmek çok daha hızlı. (Oylamanın kilidini açmak için sahte bir düzenleme yapmam gerektiğini unutmayın)
Tomasz Zieliński

12
Bununla dikkat edin "test" in sys.argv; örneğin, istemediğinizde tetiklenebilir manage.py collectstatic -i test. sys.argv[1] == "test"bu problemin olmaması gereken daha kesin bir durumdur.
keturn

83

Genellikle testler için ayrı bir ayarlar dosyası oluştururum ve bunu test komutunda kullanırım örn.

python manage.py test --settings=mysite.test_settings myapp

İki faydası vardır:

  1. testSys.argv'de kontrol etmek zorunda değilsiniz veya böyle bir sihirli kelime test_settings.py, sadece

    from settings import *
    
    # make tests faster
    SOUTH_TESTS_MIGRATE = False
    DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}

    Veya test ayarlarını üretim ayarlarından temiz bir şekilde ayırarak ihtiyaçlarınız için daha fazla ince ayar yapabilirsiniz.

  2. Diğer bir yararı da, ince hatalardan kaçınarak sqlite3 yerine üretim veritabanı motoru ile test çalıştırabilmenizdir, böylece kullanımı geliştirirken

    python manage.py test --settings=mysite.test_settings myapp

    ve kodu işlemeden önce bir kez çalıştırın

    python manage.py test myapp

    sadece tüm testlerin gerçekten başarılı olduğundan emin olmak için.


2
Bu yaklaşımı beğendim. Bir sürü farklı ayar dosyam var ve bunları farklı sunucu ortamları için kullanıyorum, ancak farklı bir test veritabanı seçmek için bu yöntemi kullanmayı düşünmemiştim. Fikir için teşekkürler.
Alexis Bellido

Merhaba Anurag, bunu denedim ama ayarlarda bahsedilen diğer veritabanlarım da çalışıyor. Sebebini tam olarak bulamıyorum.
Bhupesh Pant

Güzel cevap. Kapsam dahilinde testler çalıştırırken bir ayarlar dosyasını nasıl belirteceğimi merak ediyorum.
Wtower

Bu iyi bir yaklaşım, ancak KURU değil. Django, testler yaptığınızı zaten biliyor. Bu bilgiye bir şekilde "bağlanabilir" olsaydınız, hazır olurdunuz. Ne yazık ki bunun yönetim komutunun genişletilmesini gerektirdiğine inanıyorum. Örneğin, manage.py her çağrıldığında MANAGEMENT_COMMAND ayarının geçerli komuta ayarlanmasıyla veya bu etkiye sahip bir şeyle, çerçevenin özünde bu jenerik yapmak muhtemelen mantıklı olacaktır.
DylanYoung

2
@DylanYoung, ana ayarları test_settings'e dahil ederek ve sadece test için istediğiniz şeyleri geçersiz kılarak kurutabilirsiniz.
Anurag Uniyal

22

MySQL, veritabanı yapılandırmanızda ( settings.py) şu şekilde yapılandırabileceğiniz "MEMORY" adlı bir depolama motorunu destekler :

    'USER': 'root',                      # Not used with sqlite3.
    'PASSWORD': '',                  # Not used with sqlite3.
    'OPTIONS': {
        "init_command": "SET storage_engine=MEMORY",
    }

MEMORY depolama motorunun blob / metin sütunlarını desteklemediğini unutmayın, bu nedenle bunu kullanıyorsanız django.db.models.TextFieldişinize yaramayacaktır.


5
Blob / metin sütunları için destek eksikliğinden bahsettiği için +1. Ayrıca işlemleri desteklemiyor gibi görünüyor ( dev.mysql.com/doc/refman/5.6/en/memory-storage-engine.html ).
Tuukka Mustonen

Bellek içi testleri gerçekten istiyorsanız, muhtemelen en azından işlemleri destekleyen sqlite ile gitmeniz daha iyi olacaktır.
atomic77

15

Ana soruna cevap veremem ama işleri hızlandırmak için yapabileceğin birkaç şey var.

İlk olarak, MySQL veritabanınızın InnoDB'yi kullanacak şekilde ayarlandığından emin olun. Daha sonra her testten önce db'nin durumunu geri almak için işlemleri kullanabilir, bu da benim deneyimime göre çok büyük bir hızlanmaya yol açtı. Settings.py (Django 1.2 sözdizimi) 'nizde bir veritabanı başlatma komutu iletebilirsiniz:

DATABASES = {
    'default': {
            'ENGINE':'django.db.backends.mysql',
            'HOST':'localhost',
            'NAME':'mydb',
            'USER':'whoever',
            'PASSWORD':'whatever',
            'OPTIONS':{"init_command": "SET storage_engine=INNODB" } 
        }
    }

İkinci olarak, her seferinde Güney göçlerini yürütmenize gerek yok. SOUTH_TESTS_MIGRATE = FalseSettings.py dosyanızda ayarlayın ve veritabanı düz syncdb ile oluşturulacak ve bu, tüm geçmiş geçişleri gerçekleştirmekten çok daha hızlı olacaktır.


Harika bir ipucu! Testlerimi 'den' 369 tests in 498.704se düşürdü 369 tests in 41.334s . Bu, 10 kattan daha hızlı!
Gabi Purcaru

Settings.py'de Django 1.7+ sürümündeki geçişler için eşdeğer bir anahtar var mı?
Edward Newell

@EdwardNewell Tam olarak değil. Ancak --keepveritabanını kalıcı hale getirmek için kullanabilirsiniz ve tüm geçişlerinizin her test çalıştırmasında yeniden uygulanmasını gerektirmez. Yeni geçişler çalışmaya devam edecek. Sık sık şubeler arasında geçiş yapıyorsanız, tutarsız bir duruma geçmek kolaydır (veritabanını test veritabanına değiştirip çalıştırarak geçiş yapmadan önce yeni geçişleri geri döndürebilirsiniz migrate, ancak biraz zahmetlidir).
DylanYoung

10

Çift ince ayar yapabilirsiniz:

  • işlem tablolarını kullanın: ilk fikstür durumu, her TestCase'den sonra veritabanı geri dönüşü kullanılarak ayarlanır.
  • veritabanı verilerinizi ramdisk'e koyun: veritabanı oluşturma söz konusu olduğunda çok şey kazanacaksınız ve ayrıca testi çalıştırmak daha hızlı olacaktır.

Her iki numarayı da kullanıyorum ve oldukça mutluyum.

Ubuntu'da MySQL için nasıl kurulur:

$ sudo service mysql stop
$ sudo cp -pRL /var/lib/mysql /dev/shm/mysql

$ vim /etc/mysql/my.cnf
# datadir = /dev/shm/mysql
$ sudo service mysql start

Dikkat edin, bu sadece test amaçlıdır, veritabanınızı bellekten yeniden başlattıktan sonra kaybolur!


Teşekkürler! benim için çalışıyor. Sqlite kullanamıyorum çünkü mysql'ye özgü özellikler kullanıyorum (tam metin dizinler). Ubuntu kullanıcıları için, apparmor yapılandırmanızı, mysqld'nin / dev / shm /
mysql'e

Ivan ve Potr'un haberine şerefe. AppArmor mysql profilini şimdilik devre dışı bıraktı
trojjer 13

Hmm. Mysqld'nin / dev / shm / mysql yoluna ve içeriğine erişimini sağlamak için yerel profili özelleştirmeyi denedim, ancak hizmet bazıları için yalnızca 'şikayet' modunda (aa-şikayet komutu) başlayabilir ve 'zorla' başlatamaz neden ... Başka bir forum için bir soru!
Anlayamadığım

4

Başka bir yaklaşım: RAM Disk kullanan bir tempfs'de çalışan başka bir MySQL örneğine sahip olmak. Bu blog gönderisindeki talimatlar: Django'da test için MySQL'i hızlandırma .

Avantajları:

  • Üretim sunucunuzun kullandığı veritabanının tamamen aynısını kullanıyorsunuz
  • varsayılan mysql yapılandırmanızı değiştirmenize gerek yok

2

Anurag'ın cevabını genişletmek için aynı test_settings'i oluşturarak ve manage.py'ye aşağıdakileri ekleyerek süreci basitleştirdim

if len(sys.argv) > 1 and sys.argv[1] == "test":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.test_settings")
else:
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

sys zaten içe aktarıldığından ve manage.py yalnızca komut satırı aracılığıyla kullanıldığından, daha temiz görünüyor, bu nedenle ayarları karıştırmaya gerek yok


2
Bununla dikkat edin "test" in sys.argv; örneğin, istemediğinizde tetiklenebilir manage.py collectstatic -i test. sys.argv[1] == "test"bu problemin olmaması gereken daha kesin bir durumdur.
keturn

2
@keturn bu şekilde ./manage.pybağımsız değişkenler olmadan çalışırken bir istisna oluşturur (örneğin, hangi eklentilerin mevcut olduğunu görmek için --help)
Antony Hatchkins

1
@AntonyHatchkins Bunu çözmek önemsiz:len(sys.argv) > 1 and sys.argv[1] == "test"
DylanYoung

1
@DylanYoung Evet, Alvin'in çözümüne eklemesini istediğim şey de buydu ama özellikle onu geliştirmekle ilgilenmiyor. Her neyse, yasal çözümden çok hızlı bir hack gibi görünüyor.
Antony Hatchkins

1
bir süredir bu cevaba bakmadım, parçacığı @ DylanYoung'ın gelişimini yansıtacak şekilde güncelledim
Alvin

0

Aşağıdakileri kendi setting.py

DATABASES['default']['ENGINE'] = 'django.db.backends.sqlite3'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.