Python etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Python etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

13 Ocak 2023 Cuma

SOLID prensiplerinden Open-Closed prensibinin python kodları eşliğinden yakından incelenmesi

Merhaba, 

Bugünkü yazımızda SOLID prensiplerinden 'Open-Closed Rrensibi' ni inceleyeceğiz.


Temsil edilen O harfi "Open-Closed Principle" (Açık-Kapalı Prensibi) anlamına gelir. Bu prensip, bir sınıfın veya modülün geliştirilmeye açık olmasının yanı sıra, uzantılar yapılarak kapatılması gerektiğini ifade eder. Bu, sınıfın veya modülün işlevselliğini genişletmek için değiştirilmemesi, ancak ekstra işlevsellik eklemek için uzantılar yapılması gerektiğini ifade eder.

Open-Closed Principle kullanmak, yazılımın daha esnek, daha test edilebilir ve daha az hata içerme olasılığına sahip olmasını sağlar. Özellikle, bu prensip ile yazılım kodunun değiştirilmesi gerektiğinde, mevcut kodun değiştirilmemesi ve yerine yeni kodun eklenmesiyle sorunlar çözülebilir. Bu, yazılımın daha uzun bir ömür yaşamasına ve daha az bakım gerektirmesine olanak tanır. Ayrıca, Open-Closed Principle kullanmak, yazılımın daha kolay test edilebilir olmasını sağlar çünkü sınıflar ve modüller arasındaki bağımlılıklar azaltılmış olur.

Open-Closed Principle kullanmazsanız, yazılım kodunuzun daha az esnek ve daha zor test edilebilir olmasına neden olabilir. Kodunuzun işlevselliğini genişletmek istediğinizde, mevcut kodun değiştirilmesi gerekebilir ve bu değişiklikler, başka yerlerde beklenmedik hatalara neden olabilir. Ayrıca, mevcut kodun değiştirilmesi, yazılımın daha hızlı çürümesine ve daha fazla bakım gerektirmesine neden olabilir.

Bunu bir python kodu ile açıklayalım :

 

class Rectangle: def __init__(self, width, height): self._width = width self._height = height def get_area(self): return self._width * self._height class Square(Rectangle): def __init__(self, side): super().__init__(side, side) class Circle: def __init__(self, radius): self._radius = radius def get_area(self): return 3.14 * (self._radius ** 2) class AreaCalculator: def __init__(self, shapes): self._shapes = shapes def get_total_area(self): total_area = 0 for shape in self._shapes: total_area += shape.get_area() return total_area


Burada, Rectangle, Square ve Circle sınıfları, get_area metodunu kullanarak alanlarını hesaplamak için tasarlanmıştır. AreaCalculator sınıfı ise, verilen şekillerin alanlarını toplamak için kullanılır. Bu örnekte, Rectangle ve Circle sınıfları "Open-Closed Principle" prensibine uymaktadır. Çünkü, bu sınıfların işlevselliği genişletilmek istendiğinde değiştirilmemektedir, ancak yeni sınıflar oluşturulmaktadır (örneğin Square sınıfı) ve bu sınıflar, genişletilmiş işlevselliği sağlamaktadır.

Bu örnekte, AreaCalculator sınıfı, verilen şekillerin alanlarını toplamak için kullanılmaktadır. Bu, AreaCalculator sınıfının, verilen şekillerin türlerinden bağımsız olarak çalışmasını sağlar. Bu, AreaCalculator sınıfının daha esnek ve daha az hata içerme olasılığına sahip olmasını sağlar.

Şimdi de bu prensibin ihlal edildiği bir örnek oluşturalım.

 

class Rectangle: def __init__(self, width, height): self._width = width self._height = height def get_area(self): return self._width * self._height class Square: def __init__(self, side): self._side = side def get_area(self): return self._side * self._side class Circle: def __init__(self, radius): self._radius = radius def get_area(self): return 3.14 * (self._radius ** 2) class AreaCalculator: def __init__(self, shapes): self._shapes = shapes def get_total_area(self): total_area = 0 for shape in self._shapes: if isinstance(shape, Rectangle) or isinstance(shape, Square): total_area += shape.get_area() return total_area


Burada, Rectangle, Square ve Circle sınıfları, get_area metodunu kullanarak alanlarını hesaplamak için tasarlanmıştır. AreaCalculator sınıfı ise, verilen şekillerin alanlarını toplamak için kullanılır. Bu örnekte, Rectangle ve Square sınıfları "Open-Closed Principle" prensibini ihlal etmektedir. Çünkü, AreaCalculator sınıfı, Rectangle ve Square sınıflarının alanlarını toplamak için kodunu değiştirmiştir. Bu değişiklikler, AreaCalculator sınıfının başka yerlerde beklenmedik hatalara neden olmasına olanak tanır.

Ayrıca, AreaCalculator sınıfı, Circle sınıfını dahil etmemektedir. Bu, AreaCalculator sınıfının daha az esnek ve daha zor test edilebilir olmasına neden olur.

Biraz daha detaya inersek :

 
def get_total_area(self): total_area = 0 for shape in self._shapes: if isinstance(shape, Rectangle) or isinstance(shape, Square): total_area += shape.get_area() return total_area

Bu kod bloğunda, AreaCalculator sınıfı, verilen şekillerin her birisini döngü içinde kontrol eder. Eğer şekil bir Rectangle sınıfı veya Square sınıfının bir örneği ise, şeklin alanını toplamaya ekler. Bu, AreaCalculator sınıfının sadece Rectangle ve Square sınıflarının alanlarını toplamasını sağlar.

Bu örnekte, AreaCalculator sınıfı, Rectangle ve Square sınıflarının alanlarını toplamak için kodunu değiştirmiştir. Bu değişiklikler, AreaCalculator sınıfının, daha sonra eklenen yeni şekil türlerini dahil etmemesi sonucunu doğurur. Örneğin, Circle sınıfının alanını toplamak istediğinizde, AreaCalculator sınıfının kodunu değiştirmek zorunda kalırsınız. Bu, AreaCalculator sınıfının daha az esnek ve daha zor test edilebilir olmasına neden olur.

Bu nedenle, Open-Closed Principle kullanmak, yazılımın daha esnek, daha test edilebilir ve daha az hata içerme olasılığına sahip olmasını sağlar.





Aşağıda bir başka kod parçacığında ihlalin nasıl olduğunu tekrar görelim.
 
class Shape: def __init__(self, type): self.type = type def draw(self): if self.type == "circle": # draw circle elif self.type == "square": # draw square # ...

Bu kod parçası, "open-close" prensibini ihlal eder çünkü "Shape" sınıfı, eklenen yeni bir şekil için her seferinde değiştirilmelidir. Örneğin, eğer yeni bir şekil tipi olarak "dikdörtgen" eklenirse, "Shape" sınıfının "draw" metodu değiştirilmelidir. Bu, "Shape" sınıfının açık olduğu anlamına gelir, ancak her yeni şekil için değiştirilmesi gerektiğinden kapalı değildir. Bu nedenle, "Shape" sınıfının değiştirilmesine gerek kalmadan yeni şekil tipleri eklenmelidir.

Teşekkürler
Cem Selmanoğulları

23 Ocak 2022 Pazar

Python ve Singleton Design Pattern (Singleton Tasarım Örüntüsü)

Merhaba,

Bu yazımda Singleton Design Pattern konusundan bahsederek python diliyle yazılmış bir örneği paylaşmak istiyorum. 

Tasarım örüntülerinde belirli bir problem için belirli bir çözüm sunulmaktadır. Bu sayede tasarım iyileştirilir ve takımın aynı dili konuşması sağlanır. Tasarım örüntüleri belirli bir işi yapmak yada çözüme ulaşmak için en iyi yöntemi sunar. Çatısı belirli yazılım örüntüleri kullanılarak oluşturulan yazılımların ilerleyen süreçlerde büyümesi, bakımının yapılması ve devamlılığının sağlanması daha kolay olacaktır.

Her tasarım örüntüsünün bir ihtiyaçtan yada bir sorunu çözme amacından ortaya çıktığını söyleyebiliriz. Singleton da bunlardan biridir. Creational Design Pattern (Yaratımsal Tasarım Örüntüsü) grubuna dahildir.



Bu tasarım örüntüsü çalışma zamanında yanlızca 1 obje yaratılmasını garanti eder. İkinci bir obje yaratılamayacaktır ve sadece bu obje çağırılıp kullanılacaktır. Kısaca tüm uygulama için bu sınıftan yanlızca bir nesne olacaktır. Bu nesneye ihtiyaç olduğunda her yerde aynı tek örnek çağırılacaktır. Böylece bir instance'a kontrollü erişim sağlayabiliriz. Singleton'da bir obje sadece ona ihtiyaç duyduğumuzda yaratılır. 

Bir sınıftan bir nesne oluşturabilirsiniz ve bu nesneyi program içinde değişik yerlerde defalarca kullanmak isteyebilirsiniz. Örneğin bir veritabanı bağlantı nesnesini bir kere oluşturup her veritabanı işlemi gerektiğinde bu nesneyi kullanabilirsiniz. Eğer burada Singleton deseni ile geliştirme yaptıysanız aynı veritabanı bağlantı sınıfından ikinci bir nesne üretilemeyecek ve her seferinde istek yapıldığında ilk yaratılan nesne örneği gelecek ve kullanılacaktır. Ayrıca bir nesneyi global bir değişkende tutmak da sıkıntılıdır, çünkü kodun herhangi bir yerinde o değişkenin üzerine yazılarak tüm kodun çökme ihtimali vardır. Singleton nesneyi global bir değişkene atamadan global bir şekilde istediğimiz yerde çağırıp kullanabilmemize izin verir. Böylece global bir kullanım sunarken başka bir kodun nesne üzerine yazması da engellenmiş olur. Kod Singleton'un statik metoduna erişebiliyorsa bu metod her çağırıldığında aynı nesne örneğinin getirilmesi sağlanacaktır.

Burada önemli noktalardan birisi de şudur. Singleton ile global değişkenler üzerinde daha sıkı bir kontrol sahibi olmuş oluruz. Singleton sınıfının kendisi dışında hiçbir kod parçası nesne örneğini değiştiremez.

Biraz da kodlar üzerinden anlamaya çalışalım. Öncelikle bir sınıf oluşturup bu sınıf ile sadece bir adet nesne üretilmesini garanti edecek basit bir kod parçacığı yazmak istiyorum.


class Singleton:
__instance = None

def __init__(self):
""" Virtually private constructor. """
if Singleton.__instance != None:
raise Exception("This class is a singleton!")
else:
Singleton.__instance = self

singObj1 = Singleton()
print(singObj1)

Yukarıdaki kodda sınıf üzerinden bir nesne yaratıyoruz. Nesne yaratıldığında sınıf içindeki __instance değişkenini kontrol ederek daha önce bu sınıftan bir nesne yaratılıp yaratılmadığını kontrol ediyoruz. Eğer daha önce nesne yaratılmadıysa nesnemiz yaratılıyor ve sınıf içindeki __instance değişkenine nesnemiz atanıyor. Ekrana bastırdığımızda da alttaki şekilde hatasız bir şekilde nesnenin yaratıldığını görüyoruz.

<__main__.Singleton object at 0x0000024535C47C10>

Şimdi alttaki şekilde ikinci bir obje yaratmaya çalışalım.


class Singleton:
__instance = None

def __init__(self):
""" Virtually private constructor. """
if Singleton.__instance != None:
raise Exception("This class is a singleton!")
else:
Singleton.__instance = self

singObj1 = Singleton()
print(singObj1)
singObj2 = Singleton()
print(singObj2)

İlk nesnenin yaratılmasına kadar herşey aynı. Artık sınıf içindeki __instance değişkenine nesnemiz atanmış durumda. İkinci kes bu sınıftan nesne üretmeye çalıştığımızda ise yine __init__ içindeki kontrol yapılacaktır. Bu sefer __instance değeri None olmayacaktır. Bu sebeple "This class is a singleton!" şeklinde bir exception fırlatacaktır. Burda dikkat ederseniz nesneyi global bir değişkende tuttuk.

Exception: This class is a singleton!

Bu örneği temel mantığı anlamanız için yaptık. Şimdi biraz daha kapsamlı bir örnek paylaşmak istiyorum. Bu örnekte singleton sınıfından bir nesne üreteceğiz ve global bir değişkene bu nesneyi atamadan her istediğimizde sınıfın metodu ile çağıracağız. Böylece nesneyi global değişkende tutarak çağırmanın getireceği risklerden de kurtulmuş olacağız. Birçok yerde paylaşılan standart bir örneği olduğu gibi paylaşarak açıklamak istiyorum.


class SingletonMeta(type):
"""
The Singleton class can be implemented in different ways in Python. Some
possible methods include: base class, decorator, metaclass. We will use the
metaclass because it is best suited for this purpose.
"""

_instances = {}

def __call__(cls, *args, **kwargs):
"""
Possible changes to the value of the `__init__` argument do not affect
the returned instance.
"""
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]


class Singleton(metaclass=SingletonMeta):
def some_business_logic(self):
"""
Finally, any singleton should define some business logic, which can be
executed on its instance.
"""
# ...


if __name__ == "__main__":
# The client code.

s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2)

if id(s1) == id(s2):
print("Singleton works, both variables contain the same instance.")
else:
print("Singleton failed, variables contain different instances.")

Bu kod örneğimizde sınıfımız içinde __call__ metodunun kullanıldığını göreceksiniz. Bu metod sınıfımızdan üretilecek nesne örneğinin istenildiği bir anda bir fonksiyon gibi çağırılarak getirilebilmesini sağlar.

Yani Singleton sınıfımızdan üretilecek nesne örneğini Singleton() ile defalarca çağırabiliriz ve her seferinde aynı nesne örneği gelecektir ve ikinci bir nesne oluşturulmayacaktır. Bu tamda Singleton Design Pattern'de istediğimiz şey.

Yine burada _instances isimli bir değişkenimiz var. Bu değişken sınıf ilk nesneyi ürettiğinde oluşturulan nesne örneğini tutacak ve her çağırıldığında aynı örneği verecektir. Kontrol __call__ metodu içinde yapılacak eğer daha önce nesne üretilmediyse nesne üretilecek ve ikinci kes nesne üretilmesine izin verilmeyecektir.

Koda bakarsanız s1 ve s2 de iki kere bu sınıf nesnesi çağırılmış. Peki her seferinde aynı nesne mi gelmiş ? Evet. Aşağıdaki çıktıya baktığınızda aynı nesne örneğinin geldiğini göreceksiniz. Singleton() çağrısını yüzlerce kez de yapsak artık hep aynı nesne örneği gelecektir.

<__main__.Singleton object at 0x000002B5D5F4FF70>

<__main__.Singleton object at 0x000002B5D5F4FF70>

Singleton works, both variables contain the same instance.


Teşekkürler,

Cem Selmanoğulları

22 Ocak 2022 Cumartesi

Python ile InfluxDB operasyon scripti

Merhaba,

Bugün InfluxDB ile ilgili çeşitli işlemleri yapabileceğiniz bir python script paylaşmak istiyorum.

Alttaki script ile InfluxDB üzerinde veritabanı yaratabilir, measurement yaratabilir, veri ekleyebilir, veritabanları ve measurementları listeletebilir, var olan bir measurement ı başka bir measurementa taşıyabilirsiniz. Henüz temel birtakım işlemleri yapan scriptimizi önümüzdeki süreçte daha da geliştirip InfluxDB ile alakalı birçok işlemi yapan bir araç haline getirmeyi planlamaktayım.

   


from influxdb import DataFrameClient, InfluxDBClient
from pandas import DataFrame
import pandas as pd
import numpy as np


class InfluxMain:

def __init__(self, host, port, user, password, dbname):
self.host, self.port = host, port
self.user, self.password = user, password
self.dbname = dbname
self.client = DataFrameClient(host, port, user, password, dbname)
self.__create_database__(dbname) # Creates if db does not exist

def __create_database__(self, db):
try:
print("Creating DB: " + db)
self.client.create_database(db)
except Exception as e:
print(e)

def insert_data(self, data, measurement, tag_columns):
self.__write_to_database__(data, measurement, tag_columns)

def drop_measurement(self, measurement):
print("Dropping measurement: " + measurement)
self.client.drop_measurement(measurement)

def __write_to_database__(self, data, measurement, tag_columns, protocol="line"):
try:
print("Create Measurement: " + measurement)
self.client.write_points(data, measurement, tag_columns=tag_columns, protocol=protocol, batch_size=10000)
print("Done!")
except Exception as e:
traceback.print_exc()


class InfluxAnalyser:

def __init__(self, host, port, user, password, dbname):
self.host, self.port = host, port
self.user, self.password = user, password
self.dbname = dbname
self.influxdb_client = InfluxDBClient(host, port, user, password, dbname)

def close_connection(self):
self.influxdb_client.close()

def get_databases(self, print_to_screen):
try:
df_databases = DataFrame(self.influxdb_client.query("SHOW DATABASES").get_points())
if print_to_screen == True:
print("\n| INFLUX DATABASES |\n")
for i in range(len(df_databases)): print("DB-" + str(i + 1), "> ", df_databases['name'].loc[i])
return df_databases
except Exception as e:
print(e)

def show_measurements(self):
try:
df_databases = self.get_databases(False)
for i in range(len(df_databases)):
db_name = df_databases['name'].loc[i]
print("\nDATABASE : " + db_name + "\n")
df_measurements = DataFrame(self.influxdb_client.query("show measurements on " + db_name).get_points())
print("Measurements >")
print(df_measurements)
except Exception as e:
print(e)

def migrate_measurement(self, source, target, influx, tag_columns, influx_index):
select = "select * from " + source
df = DataFrame(self.influxdb_client.query(select).get_points())
df['Index_Time'] = pd.to_datetime(df[influx_index])
df.set_index('Index_Time', inplace=True)
influx.insert_data(df, target, tag_columns)


if __name__ == "__main__":
host, port = "localhost", 8086
user, password = "", ""
database = "TestDB"
measurement = "TestMeasurement"

influx = InfluxMain(host, port, user, password, database) # Create DB if not exists, initiate connection object
# influx.drop_measurement(measurement)

# Test data : Dataframe with Python Dataframe
df = pd.DataFrame(columns=['Name', 'City', 'Market_Type', 'Par_Val', 'Core_Val'])
dfrow = {'Name': 'Mert', 'City': 'ist', 'Market_Type': 'marmar', 'Par_Val': '23443234', 'Core_Val': '7567567',
'Start_Time': '2021-01-01 00:10:33'}
df = df.append(dfrow, ignore_index=True)
df['Index_Time'] = pd.to_datetime(df['Start_Time'])
df.set_index('Index_Time', inplace=True) # Setting index time for influxdb measurement

tag_columns = ['Name', 'City', 'Market_Type'] # Define tag column names. Fields come with data.
influx.insert_data(df, measurement, tag_columns) # Create measurement if not exists, and add data

analyser = InfluxAnalyser(host, port, user, password, database)
analyser.get_databases(True)
analyser.show_measurements()
source, target, dbname = "TestMeasurement", "NewMeasurement", "TestDB"
influx_index = "Start_Time" # Influx DB table index time
analyser.migrate_measurement(source, target, influx, tag_columns, influx_index) # Read from source, write to target
analyser.close_connection()

20 Ocak 2022 Perşembe

Python metod ve fonksiyonları - Python Methods and Functions

Merhaba,

Sıklıkla karıştırılabilen bir konuya açıklık getirmek isterim. Python yazılım dilinde fonksiyon ve metod konseptleri farklı şeyleri ifade etmektedir. 

Metodlar sınıfların bir parçasıdır ve sınıf ile oluşturulan objenin durumunu değiştirebilir. Fonksiyonlar ise sınıflardan bağımsız tek başına çalışan ve çağrılabilen yapılardır. Bir objenin parçası değildirler.

Örnek ile açıklamak gerekirse.

class Toplam:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    # Metod Ornegi
    def islem(self):
        return self.a + self.b

#Fonksiyon Ornegi
def topla(a, b):
    return a + b

Toplam sınıfı içinde tanımlı islem() bir metoddur. Sınıf dışında tanımlı topla() ise bir fonksiyondur.







Teşekkürler,

Cem Selmanoğulları

17 Ocak 2022 Pazartesi

Python yazılım dilinde Dekoratör (Decorator) kullanımı

Merhaba,

Bugün Python yazılım dilinde Decorator kullanımına bir örnek paylaşmak istiyorum. Kısaca diğer fonksiyonları yada sınıfları geliştirip modifiye etmeye yarayan python objeleri şeklinde tanımlayabiliriz. 

Örnek vermek gerekirse, bir fonksiyonumuz olsun. Bu fonksiyon bir string döndürüyor olsun. Bu döndürülen stringin büyük harfle başlamasını istiyoruz. Bunu direkt fonksiyon içinde yapmamız mümkün, ama biz bunu her yazdığımız fonksiyon içinde tekrar tekrar yapacak mıyız ?  İşte bunun için her ihtiyacımız olduğunda decorator fonksiyonunu çağırarak bu işi yapmasını sağlayabiliriz. Alttaki örnekte capitalizer isimli bir decorator fonksiyonumuz var. birde string dönen fonksiyonumuz var. @capitilizer referansını stringer fonksiyonunun başına yazdığımızda artık capitilizer fonksiyonu bir nevi stringer fonksiyonunu sarmalayarak geliştirip dönen değeri büyük harfle başlayacak şekilde geliştirecektir.

def capitalizer(func):
def wrapper(string):
innerfunc = func(string)
capitalized = innerfunc.capitalize()
return capitalized
return wrapper

@capitalizer
def stringer(string):
return string

print(stringer("tHiS is a DeCORator EXAmple"))

Çıktı :  This is a decorator example


Bir başka örneği ise Python Django Framework üzerinden verebiliriz. Django otantikasyon sistemi view fonksiyonları ile birlikte kullanabileceğimiz bir @login_required dekoratörü sunmaktadır. Bu dekoratörün refere edildiği view eğer sisteme login olunduysa gösterilecek olunmadıysa gösterilmeyecektir.

Alttaki örnekte @login_required dekoratörünün nasıl uygulandığını görebilirsiniz. Login olmayanlara my_view gösterilmeyecektir.


from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...






Teşekkürler,

Cem Selmanoğulları

30 Aralık 2021 Perşembe

Python ile uzak makineye ssh bağlantısı yaparak komut çalıştırmak

Merhaba,

Python pexpect kütüphanesini kullanarak kullanıcı adı ve şifre ile uzak makineye (linux server) ssh ile bağlanıp komut çalıştıralım. 

from pexpect import pxssh
import getpass
try:
    s = pxssh.pxssh()
    hostname = "ip.ip.ip.ip"
    username = "root"
    password = "732he+?hs7"
    s.login(hostname, username, password)
    s.sendline('ll')  
    s.prompt()            
    print(s.before)        
    s.sendline('ls -l')
    s.prompt()
    print(s.before)
    s.sendline('df')
    s.prompt()
    print(s.before)
    s.logout()
except pxssh.ExceptionPxssh as e:
    print("pxssh login hata.")
    print(e)</code>

29 Kasım 2019 Cuma

Django ORM ile rahat hayat...

Merhaba,

Bugün Django ORM yapısından bahsetmek istiyorum.



Object-Relational Mapper (ORM), veritabanı ile object oriented bir şekilde ilişki kurmanızı sağlarak işleri epey kolaylaştıran bir yapıdır. Birçok işleminizi yapmak için SQL cümlecikleri yazarak sorgular atmanıza gerek yoktur. SQL cümlecikleri arka planda Django Framework tarafından yönetilecektir.

Örnek vermek gerekirse, bir tablo yaratmak için SQL ve Django ORM üzerinde alttaki işlemleri yapmanız gerekmektedir;



Yukarıdaki gösterimdeki mutlu ve mutsuz surat ifadeleri biraz iddialı gelebilir, fakat Django ORM i kullandıkça işlerin nekadar kolaylaştığını ve sürelerin nekadar kısaldığını görmemek mümkün değil. Yeni projelerin süreleri kısalırken daha önce geliştirilmiş projeleri anlama süresi de kısalmaktadır. Tabiri caiz ise bağımlılık yapabilecek bir yapı diyebiliriz.

Django ORM yazılımcının yapması gereken birçok işlemi üzerine alırken python yazılım dilinden aldığı güç ile daha okunaklı, anlaşılabilir ve yönetilebilir bir kod yapısı sunmaktadır.

Django Framework üzerindeki "models.py" dosyası, veritabanı ve ORM işlerini yönetmek için ayrılmıştır. Oluşturulmuş örnek bir tabloyu alttaki şekilde paylaşalım. Models.py içinde tanımlanmış bu class, oluşturulacak tablo alanlarının tüm özelliklerini vermektedir. Bunların yaratılması için gereken SQL cümleciklerini, yazılımcı hiçbir şekilde görmemekte ve müdahale etmemektedir. Sadece bu class oluşturulduğunda, Django sistemi aracılığı ile veritabanında bir tablo dinamik olarak  oluşturulacaktır.

Kısaca models.py içinde her class bir tabloyu temsil etmektedir. Bu class içindeki her bir özellik ise tablo içindeki bir sütuna denk gelmektedir.


class Courses(models.Model):
    uuid = models.UUIDField(default=uuid.uuid4, verbose_name=_("Unique Identifier"), unique=True)
    coursecode = models.AutoField(primary_key=True, verbose_name=_("Kurs Kodu"))
    coursename = models.ForeignKey(CourseNames, on_delete=models.PROTECT, verbose_name=_("Kurs"))
    teacher = models.ManyToManyField(Staff, blank=True,  related_name='teacher', verbose_name=_("Öğretmenler"))
    courseroom = models.ForeignKey(CourseRoom, on_delete=models.PROTECT, verbose_name=_("Kurs Yeri"))
    credit = models.DecimalField(verbose_name=_("Kredi"), default=0.0, max_digits=3, decimal_places=1)
    student = models.ManyToManyField(Student, blank=True, verbose_name=_("Öğrenci"))
    createdon = models.DateField(verbose_name=_("Oluşturulma Tarihi"))
    createdby = models.ForeignKey(Staff, on_delete=models.PROTECT,  related_name='creatorstaff', verbose_name=_("Kaydeden Personel"))
    coursenotes = models.TextField(null=True, blank=True, verbose_name=_("Kurs Notları"))
    crs_status = models.IntegerField(choices=STD_STATUS, default=1, verbose_name=_("Kurs Statusu"))
    courseprice = models.DecimalField(verbose_name=_("Kurs Ücreti"), default=0.0, max_digits=5, decimal_places=2)

    class Meta:
        permissions = (
            ("list_course", _("List Courses")),
        )
        ordering = ['coursename', 'coursecode']
    def __str__(self):
        return "KRS-%s > %s" % (self.coursecode, self.coursename)


Mysql veritabanı üzerinde oluşturulan tabloya bakarsak, alttaki şekilde oluşturulduğunu göreceğiz.


Tablo alanlarına bakarsanız diğer tablolar ile ilişkisel yapının da bu class içinde yönetildiğini göreceksiniz. ForeignKey ve ManytoManyField ilişkileri arka planda Django tarafından dinamik olarak yönetilmekte ve yazılımcının bu anlamda da herhangi bir efor sarfetmesine gerek kalmamaktadır.

Bu tabloya sorgu atmak alttaki şekilde tek bir satırla mümkün olacaktır.


course_objects = Courses.objects.all()


Bu sorgu sonucunda tablodaki tüm satırlar course_objects içinde gelecektir. Biraz daha detaya girecek olursak. Yukarıda çekilen tüm satırlar içinde gezerek ilişkisel yapıyı da kullanarak çok detaylı veriye hiçbir sql cümlesi yazmadan erişmek mümkündür.


for course in course_objects:
    course_student_object = course.student.filter(std_no=student_id)


Kısaca yukarıda yaptığımız işlemi açıklayalım. Courses isimli kursların tutulduğu bir tablomuz mevcut. Bu tablodaki alanlardan birisi öğrencileri içeriyor. Bu öğrenciler birden fazla öğrenci bu kursu alabileceği için ManytoManyField olarak tanımlanmış durumda. Öğrenciler Student isimli başka bir tabloda tutuluyor. Elimizde Courses ve Student isimli iki tablomuz mevcut. Django ORM bu iki tablo arasındaki ilişkiyi yönetebilmek için dinamik olarak bir ara tabloyu kendisi yaratıyor. Bu tabloya "project1app_courses_student" ismini veriyor.


Oluşturulan bu tabloya baktığımızda kurs ve öğrenci ilişkilerinin nasıl yönetildiğini görebilirsiniz.


for course in course_objects:
    course_student_object = course.student.filter(std_no=int(student_id))


Böylece sadece yukarıdaki kodu yazarak veritabanından belirli bir öğrencinin hangi kurslara kayıtlı olduğunu görebiliyoruz. "Hiçbir SQL cümleciği ile sorgu yazmadan".

Courses tablosuna yeni bir satır eklemek için ise alttaki şekilde bir kod satırı yeterli olacaktı.


Courses.objects.create()

Tek bir Django sorgusu ile birden fazla object yaratmak da mümkündür. Bunun için bulk_create özelliği kullanılır.


Book.objects.bulk_create([
   Book(title="Book of Django", author=author),
   Book(title="The Django Book", author=author),
   Book(title="The Third Book", author=author),
])


Silmek için ise alttaki satır yeterli olacaktır;


Courses.objects.filter(uuid=uuid).delete()

Başka bir örnekte, kullanıcı tablosu üzerinde yeni bir kullanıcı yaratmak için ise alttaki şekilde bir kod satırı yeterli olacaktır.


user = User.objects.create_user(username=username, first_name=first_name, last_name=last_name, 
                                email=email, password=password)


user.save()


Ali kullanıcı isimli bir kullanıcı objesini veritabanından getirmek alttaki kadar kolay olacaktır.


User.objects.get(username='Ali')

Objeleri sıralama işlemini alttaki şekilde yapabiliriz.


Courses.objects.order_by('coursecode')

Yine kaç adet kurs kaydı olduğunu öğrenmek için tek satır yeterli olacaktır.


course_objects_count = Courses.objects.count()

Courses tablosundan kursları filtrelemek için ise alttaki satırı kullanacağız.


active_courses = Courses.objects.filter(crs_status=1).values("coursecode","coursename_id__name")

Birden fazla sorguyu zincirleyerek karmaşık veritabanı sorguları ve işlemleri yapmak da mümkündür. Bu konuyu başka bir yazımda ele almaya çalışacağım.