Giriş.
Herkese merhabalar. Django uygulamanızı yönetebilmeniz ve uygulama ile etkileşime geçebilme adına yerleşik komutlara sahiptir. Hangi seviyede bir proje geliştirdiğimizden bağımsız olarak her Django geliştiricisi bu komutları kullanmıştır.
Örneğin django-admin startproject ile yeni bir proje başlatmak, python manage.py runserver ile geliştirme sunucusunu ayağa kaldırmak gibi.
Bu komutların yanına projemizde bulunan uygulamaların ihtiyaçlarını karşılayacak veya yönetimsel yeni komutlarda ekleyebiliriz. Bu yazıda bu komutları nasıl oluşturup, kullanabileceğimizi göreceğiz. Hazırsanız başlayalım 🙂
Giriş olarak önce temel bir Django projesinde tanımlı tüm komutlara bir göz atalım.
$ python manage.py help Type 'manage.py help <subcommand>' for help on a specific subcommand. Available subcommands: [auth] changepassword createsuperuser [contenttypes] remove_stale_contenttypes [django] check compilemessages createcachetable dbshell diffsettings dumpdata flush inspectdb loaddata makemessages makemigrations migrate sendtestemail shell showmigrations sqlflush sqlmigrate sqlsequencereset squashmigrations startapp startproject test testserver [sessions] clearsessions [staticfiles] collectstatic findstatic runserver
Zaten aşina olduğunuz bir kaç komut dikkatinizi çekmiştir. Fakat burada dikkat etmemiz gereken nokta komutların ya genel Django iskeletinde ya da bir uygulama altında yaşaması.
Django komutları ararken veya çalıştırırken uygulamalar altındaki management/commands klasörünü tarar ve bulunan komutu çalıştırır. Aynı templates klasör yapısı gibi. Django bu klasör dışındaki komutları tanımayacaktır. Tekrar kullanılabilir veya yardımcı 3.parti bir uygulama yazmak istediğiniz zaman karışıklıkların önüne geçmek için sizi bu yapı içinde tutar. Devam edelim.
Şu görünümde bir demo projemiz olsun.
$ tree -I '__pyca*' . ├── manage.py ├── my_site │ ├── asgi.py │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── user ├── admin.py ├── apps.py ├── __init__.py ├── management │ └── commands │ └── useles_command.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py 5 directories, 14 files
Şu haliyle tanımlanmış fakat çalıştırılamayacak bir komutumuz var.
Basit bir komut yazmak.
Basitçe uygulamanın lokal tarihi bilgisini gösteren bir komut yazıp, çalıştıralım ve adım adım üstünden geçerek anlamaya çalışalım.
from django.core.management import BaseCommand from django.utils.formats import localize import datetime class Command(BaseCommand): help = "Shows the local date and time." def handle(self, *args, **kwargs): date = localize(datetime.datetime.now()) self.stdout.write(date)
$ ./manage.py useless_command Nov. 1, 2020, 10:20 p.m.
Django yönetim komutları aslında BaseCommand sınfından türetilen Command adına sahip sınıflardır. Komutun yapacağı ana işlem(bu örnekte lokal tarihin belirlenmesi) handle metodunda tanımlanır. Komut çalıştırıldığında metod varsa parametreler ile birlikte yürütülür. Oluşturulan sonuç standart outputa yazılır(opsiyonel).
Not. Her bir komut uygulama altında farklı bir dosyada ve mutlaka Command isimli bir sınıf tanımlanarak oluşturulmadır. Aksi halde Django size komutu bulamadığını söylecektir. Komutun ismi dosya ismi olacaktır.
Aslında komutlar sıradan bir Python scriptinden farklı gözükmüyor bu yüzden neden böyle bir kullanıma ihtiyaç duyduğumuzu düşünebilirsiniz. Evet bir bakıma haklı bir düşünce. Fakat komutlar sıradan Python scriptinden farklı olarak uygulamanızın tüm parçaları ile iletişime geçebilir. Örneğin veritabanı sorgularınızı veya diğer yardımcı fonksiyonlarınızı kullanabilirsiniz. Tüm komponentler içeriye aktarılabilir ve kullanılabilir durumdadır.
Parametreler ile çalışmak.
Mantığı kavradık fakat şu an elimizde olan komut pek yetenekli değil. Komutlarla birlikte kullanılması için zorunlu veya opsiyonel parametrelerin kullanımı için 2 adet yeni komut oluşturacağız ve üzerine konuşacağız.
- Birinci komut bizim için belirlediğimiz sayıda rastgele yetkili veya normal kullanıcılar oluşturacak.
- İkinci komut ise kayıt edilmiş son 10 kullanıcının tamamını ya da yetkili sıfatına sahip olanlarını geri dönecek.
Zorunlu parametreler.
your_app/management/commands/create_users.py
from django.contrib.auth import get_user_model import random from django.core.management import BaseCommand import uuid class Command(BaseCommand): help = "Creates staff or regular user" def add_arguments(self, parser): parser.add_argument("total", type=int, help="How many users will be created") def handle(self, *args, **kwargs): total_user = kwargs.get("total") staff_status = [False, True] for _ in range(total_user): user = get_user_model().objects.create_user( username=uuid.uuid4().hex[:10], password=uuid.uuid4().hex[:10], is_staff=random.choice(staff_status), ) self.stdout.write(f"{user.__str__()} created")
Django komutlar ile birlikte kullanılacak olan parametreleri yönetmek için standart bir Python kütüphanesi olan argparse kütüphanesini kullanır ve standart inputtan okuduğu değerleri komutlara geçirir.
total parametresi zorunlu ve kaç adet kullanıcı oluşturalacağını belirleyen parametre. Çalıştırır isek:
$ ./manage.py create_users 3 e76c831c2b created 6ba61a20f4 created 5577d050e4 created
Opsiyonel parametreler.
class Command(BaseCommand): help = "Creates staff or regular user" def add_arguments(self, parser): parser.add_argument("total", type=int, help="How many users will be created") # optional parser.add_argument("-p", "--prefix", type=str, help="Optional username prefix") def handle(self, *args, **kwargs): total_user = kwargs.get("total") prefix = kwargs.get("prefix") staff_status = [False, True] for _ in range(total_user): user = get_user_model().objects.create_user( username=uuid.uuid4().hex[:10] if prefix is None else f"{prefix}_{uuid.uuid4().hex[0:10]}", password=uuid.uuid4().hex[:10], is_staff=random.choice(staff_status), ) self.stdout.write(f"{user.__str__()} created")
Opsiyonel parametreler ile komutların çalışmasına müdahale edebiliriz. Herhangi bir değer sağlanmadığında değişken None dönecektir.
Bu örnekte dışarıdan alınan prefix parametresi oluşturulacak kullanıcı adının ön eki olarak eklenmekte.
$ ./manage.py create_users -p django 3 django_176ec710fe created django_05c9858e5f created django_cd2bf26144 created
Opsiyonel flag parametreler.
Bir diğer opsiyonel parametre tipi olan flag’leri ile de komutlarımızı çalıştırabiliriz. Flagler genellikle boolean değerleri komutlara geçirmek için kullanılır. Örneğin yukarıdaki komuta flag –staff flagi ekleyerek oluşturulacak tüm kullanıcıların yetkili tipinde olmasını belirtebiliriz.
Fakat biz bu kullanımı görme adına, yeni bir komut yazacağız. Bu komut varsayılan olarak kaydedilmiş son 10 kullanıcıyı dönecek. Ama –staff parametresi geçilir ise son 10 kullanıcı arasında bulunan yetkili hesapları dönecek.
your_app/management/commands/get_last_users.py
from django.core.management import BaseCommand, call_command from django.contrib.auth import get_user_model class Command(BaseCommand): help = "Returns the last ten users" def add_arguments(self, parser): parser.add_argument( "-s", "--staff", action="store_true", help="Filter staff users" ) def handle(self, *args, **kwargs): call_command("create_users", 10, prefix="flask") #equals to ./manage.py create_users 10 last_ten_users = get_user_model().objects.order_by("-date_joined")[:10] if kwargs.get("staff"): for user in last_ten_users: if user.is_staff: self.stdout.write(f"{user.__str__()} is_staff") else: for user in last_ten_users: self.stdout.write( f"{user.__str__()} {'is_staff' if user.is_staff else 'is regular.'}" )
–staff flagi komuta parametre olarak geçirilmez ise varsayılan olarak False değerine sahip olur.
Not. Belki dikkatinizi çekti belki çekmedi fakat Django komutlarınızı uygulamanız genelinde herhangi bir yerde çalıştırabilirsiniz. Örneğin uygulamanızın çıktı oluşturan bir komutu var ve test yazarken bu çıktıyı kullanmak zorundasınız. call_command yardımı ile dinamik olarak çıktıyı üretip testinizi kurgulamaya devam edebilirsiniz.
Yukarıdaki not bağlamında komutumuzu çalıştırdığımız da ilk önce 10 kullanıcı oluşturuyor ve daha sonra filtreleme işlemlerini gerçekleştiriyoruz.
$ ./manage.py get_last_users flask_3c27bcb6bb created flask_c6a851968a created flask_8a2d053a58 created flask_a7340376b1 created flask_7d1aaa66e8 created flask_447616e34a created flask_2b807b56ed created flask_d18ec13e30 created flask_616e4ed596 created flask_281dcd2525 created flask_281dcd2525 is_staff flask_616e4ed596 is_staff flask_d18ec13e30 is_staff flask_2b807b56ed is_staff flask_447616e34a is regular. flask_7d1aaa66e8 is_staff flask_a7340376b1 is_staff flask_8a2d053a58 is_staff flask_c6a851968a is_staff flask_3c27bcb6bb is regular.
Ek bilgi olarak:
Komplike komutlar yazdığınızda ayırt edilmeyi kolaylaştırması adına çıktılarınızı stillendirebilirsiniz.
your_app/management/commands/colors.py
from django.core.management.base import BaseCommand class Command(BaseCommand): help = "Show all styles" def handle(self, *args, **kwargs): self.stdout.write(self.style.ERROR("lorem ipsum dolor.")) self.stdout.write(self.style.NOTICE("lorem ipsum dolor.")) self.stdout.write(self.style.SUCCESS("lorem ipsum dolor.")) self.stdout.write(self.style.WARNING("lorem ipsum dolor.")) self.stdout.write(self.style.SQL_FIELD("lorem ipsum dolor.")) self.stdout.write(self.style.SQL_COLTYPE("lorem ipsum dolor.")) self.stdout.write(self.style.SQL_KEYWORD("lorem ipsum dolor.")) self.stdout.write(self.style.SQL_TABLE("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_INFO("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_SUCCESS("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_NOT_MODIFIED("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_REDIRECT("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_NOT_FOUND("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_BAD_REQUEST("lorem ipsum dolor.")) self.stdout.write(self.style.HTTP_SERVER_ERROR("lorem ipsum dolor.")) self.stdout.write(self.style.MIGRATE_HEADING("lorem ipsum dolor.")) self.stdout.write(self.style.MIGRATE_LABEL("lorem ipsum dolor."))
Son sözler.
Uzun ve keyifli bir yazı oldu. Artık Django yönetim komutları hakkında giriş seviyesinde dahi olsa bilgi sahibisiniz. Fakat daha komplike işlemler için başucu kaynaklarına göz atmakta her zaman fayda var. Görüşmek üzere!
Python argparse dökümantasyonu.
İlk Yorumu Siz Yapın