22 Şubat 2009 Pazar

Listenin kopyasını almak

Python da her eşitlik referans gösterir. Bu iddalı lafın anlamını şöyle ifade edeyim.
Örneğin şekildeki gibi bir liste = başka bir liste şeklinde kopya almaya çalıştığınızda şöyle bir gariplik (aslında güzellik) ile karşılaşırsınız.

>>> a = ["a","b","c"]
>>> b = a
>>> b.append("d")
>>> print a, b
['a', 'b', 'c', 'd'] ['a', 'b', 'c', 'd']


Nesnelere gelince yine aynı mantık, nesne = başkabirnesne dediğinizde referans göstermiş oluyorsrunuz :


>>> class obj:
... def __init__(self):
... self.counter = 0
... def inc(self):
... self.counter += 1
...
>>> a = obj()
>>> b = a
>>> a.inc()
>>> print a.counter
1
>>> print b.counter
1


Bir nesnenin ya da değişkenin kopyasını almak istediğinizde çeşitli yöntemler var, sanırım verilerin (list, tuple, dict vs) kopyasını alırken karşılaştığım en yakışıklı yöntem şu :

>>> a = ["a","b","c"]
>>> b = list(a)
>>> print a , b
['a', 'b', 'c'] ['a', 'b', 'c']
>>> a.append("d")
>>> print a, b
['a', 'b', 'c', 'd'] ['a', 'b', 'c']


Peki bir nesnenin kopyasını almak istiyorsak? bunun için copy adında bir modül yapmışlar :

>>> class obj:
... def __init__(self):
... self.counter = 0
... def inc(self):
... self.counter += 1
...
>>> a = obj()
>>> from copy import copy
>>> b = copy(a)
>>> a.inc()
>>> b.inc()
>>> b.inc()
>>> b.inc()
>>> print a.counter, b.counter
1 3
Eğer copy modülünden deepcopy fonksionunu import eder kullanırsak, nesneyi özyinelemeli olarak kopyalıyormuş. Herkese iyi hafta sonları

17 Şubat 2009 Salı

Django'da ImageField'ı kayıt esnasında yeniden adlandırmak.

Elimde şu şekilde bir model var :

products/models.py
class Product(models.Model):
    type = models.PositiveSmallIntegerField(choices = PRODUCT_TYPES,)
    category = models.ForeignKey(Category)
    name = models.CharField(max_length=50)
    slug = models.SlugField()

    image = models.ImageField(upload_to = "product_images")
    def __unicode__(self):
        return self.name


Yapmak istediğim ise şu : yönetici panelinde bu modeli kaydederken verilen dosyanın soyadı değişmeden isminin slugfield olarak değiştirilip kaydedilmesi.

Örnek vermek gerekirse adı "Vestel Cep Televizyonu" olan bir ürünü image alanı img13.jpg dosyası ile kaydettiğimi düşünelim. Bu durumda image, "product_images/vestel-cep-televizyonu.jpg" olarak kaydedilmeli (soyadı olan jpg aynı kalıyor, dosyanın adı slug ile eşitleniyor)

Bunu yapabilmek için düşünmeye başladığımda admin site'sinin, save_model fonksiyonunu override edebildiğimizi öğrendim ve şöyle bir şey yazdım :

from os.path import dirname, join, splitext
from shutil import move as rename

class ProductAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("name",)}
    def save_model(self, request, obj, form, change):
        extension = splitext(obj.image.path)
        new_image_path = join(dirname(obj.image.path), obj.slug + extension)
        rename(obj.image.path, new_image_path)
        obj.image.path = new_image_path
        obj.save()


Fakat bu şöyle bir hataya yol açıyordu :

File "/home/horselogy/django/mobil8/../mobil8/products/admin.py" in save_model
36. obj.image.path = new_image_path

Exception Type: AttributeError at /admin/products/product/2/
Exception Value: can't set attribute


Saatlerce path değişkenini set edemiyorum diye saç baş yolduktan sonra fark ettim ki tek yapmam gereken obj.image = "uploaded/path" şeklinde yolu göstermem. Çünkü name ve path değerleri bu şekilde otomatik olarak set ediliyor. Neyse son hali ile şu şekilde olayı gerçekleştirdim

products/admin.py
class ProductAdmin(admin.ModelAdmin):
    prepopulated_fields = {"slug": ("name",)}
    def save_model(self, request, obj, form, change):
        extension = splitext(obj.image.path)[-1]
        new_image_name = obj.slug + extension
        new_path = join(dirname(obj.image.path), new_image_name)
        rename(obj.image.path,new_path)
        obj.image = join("product_images", new_image_name)
        obj.save()


Bir dili anlarken çok zor bir dönem geçer, en aptal şey için bile saatlerinizi harcarsınız. Sonrası çorap söküğü gibi gelir her şey kolaylaşır. Sanırım bende Django konusunda bu dönemden geçiyorum. Sizin dönemlerinizin sancısız olmasını diliyor yazıyı burada bitiriyorum :).