03 - Admin Paneli

April 22, 2018 5 minute read

Model objemizi oluşturduktan sonra hemen veri girişi yapsak, denesek, kontrol etsek nasıl olurdu? Django’nun en güzel yanlarından biri olan Django Admin’e el atalım mı? Önce modelimizde biraz değişiklik yapmamız gerek.

Özet

  • İkinci bir migration script oluşturma denemesi yapacağız.
  • Model katmanında ilk sinyal denememizi yapacağız.
  • Palette modelimizi Django Admin panelinde göreceğiz.
  • Resim yükleyebilmek için form sayfası, resmin renk kodlarını görüntüleyebilmek için detay sayfası hazırlayacağız.
  • Bu yazıyla ilgili kodlara buradan erişebilirsiniz.

Bir Migration Daha

Önceki konuyu pekiştirmek için Palette modelimizde birkaç değişiklik yapıp yeni bir migration script oluşturalım. Önce is_deleted adında yeni bir field ekleyelim:

# hello_palette/models.py
...
class Palette(models.Model):
    ...
    colors = models.TextField(verbose_name=_("Colors"), editable=False)  # Bunu admin panelinden düzenlenemez hale getirdik.
    is_deleted = models.BooleanField(verbose_name=_("Is deleted?"), blank=True, default=False)  # işte yeni field'imiz.
    ...

    def __str__(self):  # Bunu hatırlayın, admin panelinde kullanacağız.
        return self.image.name
# terminal
$ python manage.py makemigrations hello_palette
Migrations for 'hello_palette':
  hello_palette/migrations/0002_auto_20180422_0757.py
    - Add field is_deleted to palette
    - Alter field colors on palette
# terminal
$ python manage.py migrate hello_palette
Operations to perform:
  Apply all migrations: hello_palette
Running migrations:
  Applying hello_palette.0002_auto_20180422_0757... OK

Neden colors field’i düzenlenemez yapıldı? Çünkü Admin panelinden veya herhangi bir arayüzden bu fieldin değiştirilmesini, doldurulmasını istemiyoruz. Bu uygulamada amacımız bir resim dosyasının renk paletini otomatik olarak oluşturmak. hello_palette uygulaması içindeki color_parser.py bize bu konuda yardımcı olacak; ancak colorific adında bir bağımlılığı var, onu kurmayı unutmayalım:

# requirements.txt
...
colorific==0.3

Peki bu color_parser modülümüzü nasıl kullanacağız? Biz istiyoruz ki yeni bir resim yüklendiğinde, ilk kez kaydedildiğinde renk paleti oluşturulsun. Yani, bizim bir model objesinin kaydetme aşamasını yakalamamız ve o an ek bir işlem daha yaptırmamız gerekiyor. İşte bunu biz sinyal metodlarıyla1 yapıyoruz:

# hello_palette/models.py
...
from django.dispatch import receiver
from hello_palette.color_parser import ColorParser
...
@receiver(models.signals.post_save, sender=Palette)
def parse_colors(sender, instance, created, **kwargs):
    if not created:
        return  # don't do anything

    instance.colors = ColorParser(image_path=instance.image.path).parse_colors()
    instance.save()

Eğer ilk kez bir Palette objesi kaydediliyor veya oluşturuluyorsa, colors field’imizin içi HEX kodlarıyla doldurulacak. Şimdi admin paneline geçmeden önce son bir şey daha yapmamız gerekiyor.

Media Ayarlarımız

Static ve Media arasındaki temel fark; static adı üzerinde statiktir, genellikle frontend dosyalarımızı ve içeriği veya konumu değişmeyen dosyalar için kullanırız. Ancak bir de dışarıdan uygulama için yüklediğimiz dosyalar var. Bunlar media dosyalarıdır ve bunlar için ayrıca ayar yapmamız gerekiyor:

# settings.py
...
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# django_palette/urls.py
from django.conf import settings
from django.conf.urls.static import static
...

urlpatterns = [
    ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Django Admin

Bir proje oluşturduğunuzda Django Admin kurulu geliyor. Bizim şuan tek yapmamız gereken, bir tam yetkili kullanıcı oluşturmak. Bu sayede Admin paneline giriş yapabileceğiz:

# terminal
$ python manage.py createsuperuser
Username (leave blank to use 'gokmen'):
Email address: [email protected]
Password:
Password (again):
Superuser created successfully.

Geliştirme sunucumuzu çalıştırıp https://localhost:8000/admin/ adresine gidelim ve girişimizi yapalım:

Django admin panel

Admin panelini ilk açtığınızda sadece Users ve Groups olacak, Palette modelimizi de ekleyebilmek için bizim bir admin kaydı yapmamız gerekiyor:

# hello_palette/admin.py
from django.contrib import admin
from hello_palette.models import Palette


@admin.register(Palette)
class PaletteAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'colors', 'created_at', 'is_deleted')
    list_filter = ('is_deleted',)
    date_hierarchy = 'created_at'

Şimdi buna bir göz atalım, biz dedik ki:

  • Resim dosyası adı, renkler, ve resmin yüklenme tarihini admin panelinde kolon olarak göster. __str__ metodunu modelimizde görebilirsiniz, tekrar inceleyin, resim dosyası adını döndürüyor.
  • Resmin silinip silinmediğine göre filtrelememe izin ver. Dışarıdan bir kullanıcının gerçekten veri silmesine izin vermeyeceğiz, soft delete.
  • Resimlerin yüklenme tarihine göre filtrelememe de izin ver.

Böylece admin panelinde yeni bir Palette objesi eklemek istediğimizde biraz daha özelleştirilmiş bir panel göreceğiz:

Palette in admin

Renk kodlarımızı görebiliyoruz! Fakat admin paneli normal kullanıcılar için değil, biz burada sadece modelimizi test etmek istedik. Normal yetkisiz kullanıcılar için MTV konusunu hatırlayarak birkaç View yazmamız gerekiyor.

MTV

Bir kullanıcının bir resim yükleyip renk paletini oluşturması için bir form sayfası, bir de sonucu gösterebileceğimiz detay sayfasına ihtiyacımız var, önce View katmanı hazırmış gibi url’leri yazıyoruz:

# hello_django/urls.py
...
urlpatterns = [
    path('', view=HomeView.as_view(), name='home'),
    path('palette/', include('hello_palette.urls', namespace='palette')),  # namespace'in palette olmasına dikkat
    ...

Palette uygulamamıza http://localhost:8000/palette/ üzerinden erişebileceğiz.

# hello_palette/urls.py
from django.urls import path
from hello_palette.views import PaletteDetailView, PaletteFormView

app_name = 'palette'

urlpatterns = [
    path('', view=PaletteFormView.as_view(), name='new'),  # namespace `palette`, name `new`
    path('<int:pk>/', view=PaletteDetailView.as_view(), name='detail'),  # namespace `palette`, name `detail`
]

PaletteFormView’in daha önceki View katmanı örneğinden farklı bir URL yapısı yok, ancak PaletteDetailView için pk değerine ihtiyacımız var. Bu değer ile istediğimiz Palette model objesine erişebileceğiz. PaletteFormView önceki örnekten farklı olarak bir forma2 ihtiyacı var:

from django import forms
from hello_palette.models import Palette


class PaletteForm(forms.ModelForm):
    class Meta:
        model = Palette  # ModelForm'un güzelliği, model fieldlarımızdan form üretmek.
        exclude = ('is_deleted',)  # bu field sadece admin için gerekli, kullanıcı için gereksiz.

View katmanımız:

# hello_palette/views.py
from django.shortcuts import get_object_or_404, redirect
from django.views.generic import DetailView, FormView
from hello_palette.models import Palette
from hello_palette.forms import PaletteForm


class PaletteFormView(FormView):
    template_name = 'palette/new.html'  # template dosyamızı sonra oluşturacağız.
    form_class = PaletteForm

    def form_valid(self, form):
        palette = form.save()  # form başarılıysa, kaydet.
        return redirect('palette:detail', palette.pk)  # namespace ve name'ları işte burada kullanıyoruz.


class PaletteDetailView(DetailView):
    template_name = 'palette/detail.html'  # template dosyamızı sonra oluşturacağız.

    def get_object(self, queryset=None):
        pk = self.kwargs.get('pk')  # URL'deki pk'i hatırladınız mı?
        return get_object_or_404(Palette, pk=pk, is_deleted=False)

Model katmanı tamam, View katmanı tamam, şimdi Template katmanımızı tamamlamamız gerekiyor:

<!-- templates/palette/new.html -->
...
<body>
    <!-- enctype resim dosyası yükleyebilmek için -->
    <form action="" method="POST" enctype="multipart/form-data">
        {% csrf_token %} <!-- Cross Site Request Forgery Protection -->
        <ul>
            {{ form.as_ul }} <!-- PaletteForm burada -->
        </ul>
        <button type="submit">Upload</button>
    </form>
</body>
...
<!-- templates/palette/detail.html -->
...
<body>
    <div>
        <img src="{{ palette.image.url }}" height=250 />
    </div>

    <p>{{ palette.colors }}</p>
</body>
...

Şimdi resim yükleyip renk kodu almayı deneyebiliriz, bunun için super user olmaya ihtiyacımız yok. Biraz işlevsiz bir arayüz; ancak MTV için yeterli:

Palette form view

Palette detail view

View katmanımızı repo’dan inceleyin, orada bir Delete View örneği daha göreceksiniz. Bir sonraki yazımızda Template dosyalarımızın üzerinden bir kez daha geçelim.


  1. Signals konusunda daha fazla bilgi için Django’nun kendi dökümantasyonuna gözatmanızı öneririm. [return]
  2. CSRF, sunucuya sahte isteklerin gönderilmesini veya gerçek isteklerin gönderilirken değiştirilmesini engellemek için bir tür yöntemdir. Django dökümantasyonunda bu konuyla ilgili detaylı bilgi var. [return]