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 :).

5 yorum:

  1. obj.image = join("product_images", new_image_name) bu satırda el alışkanlığından join kullanmışım :) burada verilen yol dosya yolu değil url yolu o yüzden "product_images/" + new_image_name şeklinde kullanmalıydım.

    YanıtlayınSil
  2. Selam,

    upload_to member member'inin degerini string yerine fonksiyon olarak yazilirsa django dosyayi kaydetmeden once bu dosya yolunu bu fonksiyona soruyor.soyle;

    ====
    class Image(Model):
    file = ImageField(upload_to=get_image_path)
    ====
    # yeni image instance i yaratildiginda, dosya tam kaydetilecekken bu fonksiyon instance objesi ve filename stringiyle cagrilacak
    def get_image_path(instance,filename):
    .....
    return "%s%s"%(instance.pk,extension)
    ===

    azer

    YanıtlayınSil
  3. mazeretim var o yüzden mazur görün "slug" tabirini kullanmışsın bu tam olarak neye karşıt geliyor neyi anlatıyor?
    herhangi bir şey mi demek yoksa - işaretiyle ayrı yazılmış kelimeleremi deniyor?
    Bazı yerlerde yine karşıma çıktı öyle baka kaldım :D

    YanıtlayınSil
  4. Django dökümantasyonunda slug'tan şöyle bahsediliyor.
    http://docs.djangoproject.com/en/dev/glossary/#term-slug

    YanıtlayınSil
  5. @Azer : Bahsi geçen, file upload'larının dinamik olarak yapılması olayı henüz yapılmamış.

    http://code.djangoproject.com/ticket/4113 adresinde bu konu "Design decision needed" konumunda bekliyor. Atladığım (ya da atladığın) bir şey mi var?

    YanıtlayınSil