Album 및 Photo 2개의 테이블 각각의 콘텐츠 편집 기능을 구현한다. 이전의 Add와 Change 기능 구현과 비슷하지만 Album과 Photo 테이블이 1:N 관계로 연결되어 있는 점을 고려해야 한다.
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로 표현한다.
# /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를 코딩해야 한다.
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 속성을 지정하지 않았으므로 디폴트 템플릿명을 사용한다.
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 레코드를 생성 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 테이블의 레코드를 변경하기 위해 기존 레코드의 리스트를 보여주는 화면
{% 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 %}
{% 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 %}
Album 레코드를 수정하기 위한 폼을 보여주는 화면
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 %}
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로 배우는 파이썬 웹 프로그래밍(실전편) - 김석훈님