Django 실전편 ch9

김용녀·2022년 7월 26일
0

이번엔 포토 앱을 만들어볼것이다.(블로그나 북마크 외로 만드는것)
포토앱 화면의 경우 사진들을 묶음으로 구분해주는 앨범이 있다. 그리고 그 앨범은 사진들로 이루어져있다.이렇게 두개로 테이블을 나눌것이다.(album,photo)

이때, 하나의 앨범에 여러개의 사진이 들수있다. 따라서 앨범의 필드id와 photo의 필드 album관계로 (1:N) ForeignKey로 지정할수있다.

모델링은 다음과 같이 할 예정이다. 첫 두개는 같은 뷰와 템플릿을 이용한다.

Photo모델에는 두개의 클래스를 설계하려한다.

from django.db import models
from django.urls import reverse

from photo.fields import ThumbnailImageField


class Album(models.Model):
    name = models.CharField('NAME', max_length=30)
    description = models.CharField('One Line Description', max_length=100, blank=True)

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('photo:album_detail', args=(self.id,))


class Photo(models.Model):
    album = models.ForeignKey(Album, on_delete=models.CASCADE)
    title = models.CharField('TITLE', max_length=30)
    description = models.TextField('Photo Description', blank=True)
    image = ThumbnailImageField('IMAGE', upload_to='photo/%Y/%m',default='')
    upload_dt = models.DateTimeField('UPLOAD DATE', auto_now_add=True)

    class Meta:
        ordering = ('title',)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('photo:photo_detail', args=(self.id,))

get_absolute_url은 자신이 지칭하는 url을 반환한다.
Photo테이블의 album컬럼은 Album테이블에 연결된 외래키다. 여기선 reference역할을 한다.
image컬럼은 필드타입이 ThumbnailImageField인데, 이것은 장고 내장필드가 아니라 우리가 직접 만드는것이다.
meta내부클래스는 정렬기준 정의한다.

photo/admin.py

from django.contrib import admin

from photo.models import Album, Photo


class PhotoInline(admin.StackedInline):
    model = Photo #추가로 보여줄건 Photo다!
    extra = 2


@admin.register(Album)
class AlbumAdmin(admin.ModelAdmin):
    inlines = (PhotoInline,)
    list_display = ('id', 'name', 'description')


@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'upload_dt')

admin에서 stackedInline을 이용해 Album등록시 Photo도 같이 등록하게끔하였다.

커스텀 필드 ThumbnailImageFieldFile 작성***
import os
from PIL import Image  #파이썬 이미지처리 라이브러리 PIL
from django.db.models import ImageField
from django.db.models.fields.files import ImageFieldFile


class ThumbnailImageFieldFile(ImageFieldFile):
    def _add_thumb(self, s):
        parts = s.split('.')
        parts.insert(-1, 'thumb')
        if parts[-1].lower() not in ('jpeg', 'jpg'):
            parts[-1] = 'jpg'
        return '.'.join(parts)

    @property
    def thumb_path(self):
        return self._add_thumb(self.path)

    @property
    def thumb_url(self):
        return self._add_thumb(self.url)

    def save(self, name, content, save=True):
        super().save(name, content, save)

        img = Image.open(self.path)
        size = (self.field.thumb_width, self.field.thumb_height)
        img.thumbnail(size)
        background = Image.new('RGB', size, (255, 255, 255))
        box = (int((size[0]-img.size[0])/2), int((size[1]-img.size[1])/2))
        background.paste(img, box)
        background.save(self.thumb_path, 'JPEG')

    def delete(self, save=True):
        if os.path.exists(self.thumb_path):
            os.remove(self.thumb_path)
        super().delete(save)


class ThumbnailImageField(ImageField):
    attr_class = ThumbnailImageFieldFile

    def __init__(self, verbose_name=None, thumb_width=128, thumb_height=128, **kwargs):
        self.thumb_width, self.thumb_height = thumb_width, thumb_height
        super().__init__(verbose_name, **kwargs)

ThumbnailImageFieldFile는 파일시스템에 직접 파일을 쓰고 지우는 역할

이제 모델링,어드민, 필드까지 작성했으니 migrate하면된다.

이제 URLConf를 작성하면된다. 그런데 여기에 root url 파일에는

  • static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)를 작성하는데,
    settings.MEDIA_URL로 정의된 /media/. url요청이 오면 django.views.static.serve() 뷰함수가 처리하고, 이 뷰 함수에 document_root= settings.MEDIA_ROOT키워드 인자가 전달된다. static.serve()함수는 개발용이고 상용에는 httpd,nginx등 웹서버를 이용한다.

Photo의 URLConf도 작성하고나면 이제 View를 작성해야한다.
제네릭뷰에서 return 객체와 템플릿을 지정하지 않으면 모델을 컨텍스트 변수(list or object)로 return하고, 템플릿은 모델명_list(or detail).html로 반환한다.

***album_list.html***

{% extends "base.html" %}

{% block title %}album_list.html{% endblock %}

{% block extra-style %}
<style>
.thumbnail {
    border: 3px solid #ccc;
}
</style>
{% endblock %}

{% block content %}

    {% for item in object_list %}

    <div class="mt-5">
        <a class="h2" href="{% url 'photo:album_detail' item.id %}">
            {{ item.name }}</a>&emsp;
        <span class="font-italic h5">{{ item.description }}</span>
    </div>

    <hr style="margin: 0 0 20px 0;">

    <div class="row">
        {% for photo in item.photo_set.all|slice:":5" %}
        <div class="ml-5">
            <div class="thumbnail">
                <a href="{{ photo.get_absolute_url }}">
                    <img src="{{ photo.image.thumb_url }}" style="width: 100%;">
                </a>
            </div>
        </div>
        {% endfor %}
    </div>

    {% endfor %}

{% endblock %}

리스트뷰에서 컨텍스트변수로 앨범을 받는다.앨범 내에는 thumbnail(썸네일)변수가 있기에 썸네일 사진이 같이 출력된다.

***album_detail.html***

{% extends "base.html" %}

{% block title %}album_detail.html{% endblock %}

{% block extra-style %}
<style>
.thumbnail {
    border: 5px solid #ccc;
}
</style>
{% endblock %}

{% block content %}

    <div class="mt-5">
        <span class="h2">{{ object.name }}&emsp;</span>
        <span class="h5 font-italic">{{ object.description }}</span>
    </div>

    <hr style="margin: 0 0 20px 0;">

    <div class="row">

        {% for photo in object.photo_set.all %}
        <div class="col-md-3 mb-5">
            <div class="thumbnail">
                <a href="{{ photo.get_absolute_url }}">
                    <img src="{{ photo.image.thumb_url }}" style="width: 100%;">
                </a>
            </div>
            <ul>
                <li class="font-italic">{{ photo.title }}</li>
                <li class="font-italic">{{ photo.upload_dt|date:"Y-m-d" }}</li>
            </ul>
        </div>
        {% endfor %}
    </div>
{% endblock %}

album_list화면에서 앨범id를 누르면 album_detail로 넘어가게된다. 넘어올때 id값도 함께 넘어오기에 id를 기반으로 object(앨범)을 정하고 object의 photo를 for문을 통해 모두 출력해준다.

{% extends "base.html" %}
{% block title %}photo_detail.html{% endblock %}
{% block content %}

    <h2 class="mt-5">{{ object.title }}</h2>

    <div class="row">
        <div class="col-md-9">
            <a href="{{ object.image.url }}">
                <img src="{{ object.image.url }}" style="width: 100%;">
            </a>
        </div>

        <ul class="col-md-3 mt-3">
            <li class="h5">Photo Description</li>
                {% if object.description %}<p>{{ object.description|linebreaks }}</p>
                {% else %}<p>(blank)</p>{% endif %}
            <li class="h5">Date Uploaded</li>
                <p class="font-italic">{{ object.upload_dt }}</p>
            <li class="h5">Album Name</li>
                <p class="font-italic"><a href="{% url 'photo:album_detail' object.album.id %}"> {{ object.album.name }}</a>
                </p>
        </ul>
    </div>
{% endblock %}

photo_detail에서는 넘어오는 detail의 object(photo)를 기반으로 컬럼(description, date,name)을 출력해준다.

profile
어서오세요

0개의 댓글