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.


Django html template içinden view sorgusu atmak

Merhaba, 

Bir Django template html dosyası içinden sayfadan ayrılmadan bir view fonksiyonu çağırarak dönüş değerini almak istiyorsanız alttaki kodları kullanmanız yeterli olacaktır. Kısaca açıklayacak olursak. Html içinde script tagleri arasındaki kod bir form select alanında bir seçim yapıldığında devreye girerek seçilen değer için get-student-courses view ına istek gönderiyor. Bu view ise gelen isteği değerlendirip bazı çıktıları üreterek bunları bir list içine yerleştirerek bir json.dump olarak HttpResponse olarak Html sayfasındaki scripte geri dönüyor. Script aldığı Json datasını parse ederek sorgulanan bilgileri scripte sağlamış oluyor. Django 2.2 ve Python 3.6 kullanılmıştır.


Html :

<-----script>
var select_student = function()
{
    var selectit = document.getElementById(this.id);
    var studentid = selectit.options[selectit.selectedIndex].value;    
    var elementidlist = this.id.split("-");
    var elementid = elementidlist[1];    
    
    $.get('/get-student-courses/', {student_id: studentid}, function(data){
        obj = JSON.parse(data);        
        var innerhtmlx = "";
        for (i = 0; i < obj.length; i++) {
            var res = obj[i].split("-");
            var coursecode = res[0];
            var coursename = res[1];
            innerhtmlx = innerhtmlx + "";  
        }
        var courseselect = document.getElementById('id_form-'+elementid+'-course');        
        courseselect.innerHTML = innerhtmlx;    
    });
}

for (i = 0; i < {{ formcount }}+1; i++) {
    document.getElementById('id_form-'+i+'-student').onchange = select_student;
}
<----- script="">

View :

def get_student_courses(request):
    student_id = 0    
    if request.method == 'GET':
        student_id = request.GET['student_id']
        courseobjects = Courses.objects.all()
        studentobject = Student.objects.get(std_no=student_id)
        list = []
        for course in courseobjects:
            coursestudentobject = course.student.filter(std_no=int(student_id))
            if coursestudentobject.count() != 0:
                crscode = course.coursecode
                crsname = CourseNames.objects.get(uuid=course.coursename_id)
                
                discount_course_count = StudentDiscount.objects.filter(course_id=crscode,student_id=student_id).count()
                if discount_course_count == 0:
                    list.append(str(crscode)+"-"+str(crsname))         
        jsonlist = json.dumps(list)    
        return HttpResponse(jsonlist)

Not : Alttaki slim jqueryi kullanmayın.

<-----script crossorigin="anonymous" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" src="https://code.jquery.com/jquery-3.3.1.slim.min.js"><----- script="">


Bunun yerine alttakini kullanın.

<-----script src="https://code.jquery.com/jquery-3.1.1.min.js"><----- script="">