콘텐츠 편집 기능 - Photo 앱

jurin·2021년 12월 21일
0

Album 및 Photo 2개의 테이블 각각의 콘텐츠 편집 기능을 구현한다. 이전의 Add와 Change 기능 구현과 비슷하지만 Album과 Photo 테이블이 1:N 관계로 연결되어 있는 점을 고려해야 한다.

URL 설계

모델 코딩

Album 및 Photo 테이블에 owner 필드를 추가해준다.

owner = models.ForeignKey('auth.User', on_delete=models.CASCADE, 
                              verbose_name='OWNER', blank=True, null=True)

Album과 User 테이블 간 관계 및 Photo와 User 테이블 간 관계는 모두 N:1 관계이므로, ForeignKey로 표현한다.

모델을 변경했으므로 DB에 반영

URLconf 코딩

    # /photo/album/add/
    path('album/add/', views.AlbumPhotoCV.as_view(), name='album_add'),

    # /photo/album/change/
    path('album/change/', views.AlbumChangeLV.as_view(), name='album_change'),

    # /photo/album/99/update/
    path('album/<int:pk>/update/', views.AlbumPhotoUV.as_view(), name='album_update'),

    # /photo/album/99/delete/
    path('album/<int:pk>/delete/', views.AlbumDelV.as_view(), name='album_delete'),

    # /photo/photo/add/
    path('photo/add/', views.PhotoCV.as_view(), name='photo_add'),

    # /photo/photo/change/
    path('photo/change/', views.PhotoChangeLV.as_view(), name='photo_change'),

    # /photo/photo/99/update/
    path('photo/<int:pk>/update/', views.PhotoUV.as_view(), name='photo_update'),

    # /photo/photo/99/delete/
    path('photo/<int:pk>/delete/', views.PhotoDelV.as_view(), name='photo_delete'),

11장의 urls.py 파일과 거의 동일하지만 AlbumPhotoCV, AlbumPhotoUV 뷰가 인라인 모델 폼을 처리하는 뷰라는 점이 다르다. 즉 앨범과 사진을 한꺼번에 처리할 수 있는 폼을 출력하는 뷰이다.

뷰 코딩

11장에서는 모델폼을 사용하니까 따로 폼을 정의할 필요가 없었는데 이번엔 폼셋을 정의해야 해서 forms.py를 코딩해야 한다.

forms.py

from django.forms import inlineformset_factory

from photo.models import Album, Photo

PhotoInlineFormSet = inlineformset_factory(Album, Photo,
                                           fields=['image', 'title', 'description'],
                                           extra=2)

URLconf에서 정의한 뷰를 코딩한다.

템플릿 코딩하기

뷰 정의 시 template_name 속성을 지정하지 않았으므로 디폴트 템플릿명을 사용한다.

  • 메뉴 수정을 위한 base.html
  • Photo 모델에 대한 템플릿
  • 인라인 모델 폼셋이 들어 있는 Album 모델에 대한 템플릿

base.html

Add - Album, Photo 링크 추가
Change - Album, Photo 링크 추가

CreateView와 UpdateView는 템플릿명이 같으므로 같은 링크 추가

<a class="dropdown-item" href="{% url 'photo:album_add'%}">Album</a>
<a class="dropdown-item" href="{% url 'photo:photo_add'%}">Photo</a>

photo/photo_form.html

Photo 레코드를 생성 or 수정하기 위한 폼을 보여주는 화면

  • 앨범을 선택하는 드롭다운 박스 위젯 - Album과 Photo 테이블이 1:N 관계이므로 Photo 테이블에 대한 폼인데도 Album 테이블의 레코드를 선택할 수 있도록 해준다.

  • 사진 레코드를 생성하는 폼이지만 사진이 소속될 앨범을 새로 생성할 수 있도록 해준다.

{% extends 'base.html' %}

{% load widget_tweaks %}

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

{% block content %}
    <h1>Photo Create/Update - {{ user }}</h1>
    <p class="font-italic">This is a creation or update form for your photo.</p>

{% if form.errors %}
    <div class="alert alert-danger">
        <div class="font-weight-bold">
            Wrong! Please correct the error(s) below.
        </div>
        {{ form.errors }}
    </div>
{% endif %}

{% if form.is_multipart %}
    <form enctype="multipart/form-data" action="" method="post" class="card pt-3">
{% else %}
    <form action="." method="post" class="card pt-3">
{% endif %}
{% csrf_token %}
    <div class="form-group row">
        {{ form.album|add_label_class:"col-form-label col-sm-2 ml-3 font-weight-bold" }}
        <div class="col-sm-2">
            {{ form.album|add_class:"form-control" }}
        </div>
        <div class="col-sm-2 my-auto">
            <a href="{% url 'photo:album_add' %}" class="btn btn-outline-primary btn-sm">
                Add Album
            </a>
        </div>
    </div>
    
    <div class="form-group row">
        {{ form.title|add_label_class:"col-form-label col-sm-2 ml-3 font-weight-bold" }}
        <div class="col-sm-5">
            {{ form.title|add_class:"form-control"|attr:"autofocus" }}
        </div>
    </div>
    
    <div class="form-group row">
        {{ form.image|add_label_class:"col-form-label col-sm-2 ml-3 font-weight-bold" }}
        <div class="col-sm-5">
            {{ form.image|add_class:"form-control-file" }}
        </div>
    </div>
    
    <div class="form-group row">
        {{ form.description|add_label_class:"col-form-label col-sm-2 ml-3 font-weight-bold" }}
        <div class="col-sm-8">
            {{ form.description|add_class:"form-control-file"|attr:"rows:3" }}
        </div>
    </div>
    
    <div class="form-group">
        <div class="offset-sm-2 col-sm-5">
            <input type="submit" value="Submit" class="btn btn-info"/>
        </div>
    </div>

</form>
{% endblock %}

post_form.html 파일과 거의 같고 업로드 기능이 들어 있다는 점만 다르다. 사진이나 일반 파일을 업로드 하는 경우에는 enctype 속성을 multipart/form-data로 지정해야 한다.

is_multipart() 메소드는 폼이나 폼셋을 미리 체크해 multipart 인코딩이 필요한지 여부를 알려준다. 반환값이 True면 enctype=multipart/form-data로 지정해야 한다. 여기서는 폼에 이미지 필드가 있으니까 True를 반환한다.

photo/photo_change_list.html

Photo 테이블의 레코드를 변경하기 위해 기존 레코드의 리스트를 보여주는 화면

{% extends 'base.html' %}

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

{% block content %}
    <h1>
        Photo Change - {{ user }}
    </h1>
    <br>

    <table class="table table-striped table-bordered table-condensed">

        <thead>
            <tr class="table-info">
                <th>Album</th>
                <th>Title</th>
                <th>Description</th>
                <th>Owner</th>
                <th>Update</th>
                <th>Delete</th>
            </tr>
        </thead>

        <tbody>
        {% for item in object_list %}
            <tr>
                <td>{{ item.album }}</td>
                <td>{{ item.title }}</td>
                <td>{{ item.description }}</td>
                <td>{{ item.owner }}</td>
                <td><a href="{% url 'photo:photo_update' item.id %}">Update</a></td>
                <td><a href="{% url 'photo:photo_delete' item.id %}">Delete</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

{% endblock %}

photo/photo_confirm_delete.html

{% extends 'base.html' %}

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

{% block content %}
    <h1>
        Photo Delete
    </h1>
    <br>

    <form action="." method="post">
        {% csrf_token %}
        <p>Are you sure you want to delete "{{ object }}" ?</p>
        <input type="submit" value="Confirm" class="btn btn-danger btn-sm" />
    </form>

</div>
{% endblock %}

photo/album_form.html

Album 레코드를 수정하기 위한 폼을 보여주는 화면

photo/album_change_list.html

Album 테이블의 레코드를 변경하기 위해 기존 레코드의 리스트를 보여주는 화면.

{% extends 'base.html' %}

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

{% block content %}
    <h1>
        Album Change - {{ user }}
    </h1>
    <br>

    <table class="table table-striped table-bordered table-condensed">

        <thead>
            <tr class="table-info">
                <th>Name</th>
                <th>Description</th>
                <th>Owner</th>
                <th>Update</th>
                <th>Delete</th>
            </tr>
        </thead>

        <tbody>
        {% for item in object_list %}
            <tr>
                <td>{{ item.name }}</td>
                <td>{{ item.description }}</td>
                <td>{{ item.owner }}</td>
                <td><a href="{% url 'photo:album_update' item.id %}">Update</a></td>
                <td><a href="{% url 'photo:album_delete' item.id %}">Delete</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>

{% endblock %}

photo/album_confirm_delete.html

Album 테이블의 레코드를 삭제하기 전 확인하는 화면

{% extends 'base.html' %}

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

{% block content %}
    <h1>
        Album Delete 
        <small class="font-italic">
            (including related photos)
        </small>
    </h1>
    <br>

    <form action="." method="post">
        {% csrf_token %}
        <p>Are you sure you want to delete "{{ object }}" ?</p>
        <input type="submit" value="Confirm" class="btn btn-danger btn-sm" />
    </form>

</div>
{% endblock %}




출처: Django로 배우는 파이썬 웹 프로그래밍(실전편) - 김석훈님

profile
anaooauc1236@naver.com

0개의 댓글