Startproject
프로젝트(mypjt) 생성
django-admin startproject mypjt .
movies 앱 생성
python manage.py startapp movies
가상환경 설정
python3 -m venv venv
source venv/Scripts/activate
pip install-r requirements.txt
pip install bootstrap
requirements.txt로 django, django-extensions, bootstrap5 등을 설치했다. 부트스트랩 cdn으로 가져왔었는데, pip install으로 설치했다. settings.py에 bootstrap5를 등록해야하고, {% load bootstrap5 %} 으로 가져온다.
INSTALLED_APPS = [
'movies',
'django_extensions',
'bootstrap5',
installed_apps에 추가한다.
Model
from django.db import models
class Movie(models.Model):
title = models.CharField(max_length=20)
audience = models.IntegerField()
release_date = models.DateField()
genre = models.CharField(max_length=30)
score = models.FloatField()
poster_url = models.TextField()
description = models.TextField()
migration 진행한다.
python manage.py makemigrations
python manage.py migrate
URL
from django.urls import path
from . import views
app_name='movies'
urlpatterns = [
path('',views.index, name='index'),
path('create/',views.create, name='create'),
path('<int:pk>/', views.detail, name='detail'),
path('<int:pk>/update/',views.update, name = 'update'),
path('<int:pk>/delete/',views.delete, name='delete'),
]
Views
from django.shortcuts import render, redirect
from django.views.decorators.http import require_safe, require_POST,require_http_methods
@require_safe
def index(request):
return render(request,'movies/index.html',context)
@require_safe
def detail(request, pk):
return render(request, 'movies/detail.html',context)
@require_http_methods(['GET','POST'])
def create(request):
return render(request,'movies/create.html',context)
@require_http_methods(['GET','POST'])
def update(request, pk):
return render(request,'movies/update.html',context)
@require_POST
def delete(request, pk):
return redirect('movies:index')
views 기본형태
GET, POST 조건을 주기위해 decorators을 추가했다.
redirect, render을 구분해서 공부했다.
from .forms import MovieForm
from django.shortcuts import render, redirect
from .models import Movie
from django.views.decorators.http import require_safe, require_POST,require_http_methods
@require_safe
def index(request):
movies = Movie.objects.all()
context={
'movies':movies,
}
return render(request,'movies/index.html',context)
# Movie모델 객체에서 all()으로 모두 가져와서 movies로 보내줌.
@require_safe
def detail(request, pk):
movie=Movie.objects.get(pk=pk)
context={
'movie':movie,
}
return render(request, 'movies/detail.html',context)
# pk로 하나의 인스턴스를 movie로 보내줌.
@require_http_methods(['GET','POST'])
def create(request):
if request.method=="POST":
form = MovieForm(request.POST)
if form.is_valid():
movie = form.save()
return redirect('movies:detail', movie.pk)
form = MovieForm()
context={
'form':form,
}
return render(request,'movies/create.html',context)
# POST : 입력한 내용(request.POST)을 저장하고 save()가 반환한 생성 객체의 pk를
# detail로 redirect할 때 보내준다.
# GET : 영화 생성 입력 화면.
@require_http_methods(['GET','POST'])
def update(request, pk):
movie=Movie.objects.get(pk=pk)
if request.method=="POST":
form = MovieForm(request.POST, instance=movie)
if form.is_valid():
form.save()
return redirect('movies:detail', movie.pk)
else:
form = MovieForm(instance=movie)
context={
'form':form,
'movie':movie,
}
return render(request,'movies/update.html',context)
# POST : 수정한 내용 저장
# GET : 수정할 화면
@require_POST
def delete(request, pk):
if request.method=="POST":
movie=Movie.objects.get(pk=pk)
movie.delete()
return redirect('movies:index')
#삭제는 POST만!, delete()해준다.
Admin
admin.py
from django.contrib import admin
from .models import Movie
admin.site.register(Movie)
admin 사이트 등록 (모델)
Form
from django import forms
from .models import Movie
class MovieForm(forms.ModelForm):
class Meta:
model = Movie
fields = '__all__'
form 기본형태
forms.ModelForm을 상속받아 MovieForm을 만들었다.
class Meta: 에 model을 내가 원하는 모델로 변경해주었다.
from django import forms
from .models import Movie
choice=[('코미디','코미디'),('액션','액션'),('로맨스','로맨스'),('스릴러','스릴러')]
class MovieForm(forms.ModelForm):
title = forms.CharField(
widget=forms.TextInput(
attrs={
'placeholder':'Title'
}
))
audience = forms.FloatField(
widget=forms.NumberInput(
attrs={
'placeholder':'Audience'
}
)
)
genre = forms.ChoiceField(
widget=forms.Select,choices=choice)
score = forms.FloatField(
widget=forms.NumberInput(
attrs={'min':'0','max':'5','step':"0.5",
"placeholder":"Score"}))
release_date=forms.DateField(
widget = forms.DateInput(
attrs={
'type':'date'
}
)
)
poster_url = forms.CharField(
widget = forms.Textarea(
attrs={
'placeholder':'Poster url'
}
)
)
description = forms.CharField(
widget = forms.Textarea(
attrs={
'placeholder':'Description'
}
)
)
class Meta:
model = Movie
fields = '__all__'
- genre 필드
- ChoiceField를 이용해서 Select를 구현했다.
- score 필드
- numberinput, max, min, step 설정했다.
- release_date 필드
datefield - dateinput - type:date로 날짜형식을 달력으로 선택할 수 있도록 했다.
나머지는 기본적으로 placeholder를 넣어주기 위해서 widget, attrs를 사용했다.
Template
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
{% bootstrap_css %}
{% bootstrap_javascript %}
<style>
*{
margin:auto;
}
h1{
text-align: center;
font-weight: bold;
padding-top: 10px;
}
a {
text-decoration:none;
color:black;
}
a:hover{
color: blue;
}
</style>
</head>
<body>
{% block content %}
{% endblock content %}
</body>
</html>
base.html 에는 부트스트랩을 넣어주고, 기본적인 css를 추가했다.
index.html
{% extends 'base.html' %}
{% block content %}
<h1>INDEX</h1>
<div class="d-flex">
<a class="btn btn-success btn-sm justify-content-end" href="{% url 'movies:create'%}">영화 추가하기</a>
</div>
<hr>
<ul class="list-group m-4" >
{% for movie in movies %}
<li class="list-group-item d-flex justify-content-between align-items-start">
<div style="width:500px" class="ms-2 my-2 me-auto">
<div class="fw-bold"><a href="{% url 'movies:detail' movie.pk %}">{{movie.title}}</a>
</div>
</div>
<span class="badge bg-warning rounded-pill ">평점 {{movie.score}}</span>
</li>
{% empty %}
영화가 없습니다.
{% endfor %}
</ul>
{% endblock content %}
movies를 받아온 것을 for문을 돌려 movie로 하나씩 요소를 꺼내왔다.
detail.html
{% extends 'base.html' %}
{% block content %}
<h1>
DETAIL
</h1>
<hr>
<div class="card" style="width: 20rem;">
<img src="{{movie.poster_url}}" class="card-img-top" alt="...">
<div class="card-body m-0">
<h3 class="card-title fw-bold text-center">{{movie.title}}</h3>
<p class="card-text">Audience : {{movie.audience}}</p>
<p class="card-text">Release Dates : {{movie.release_date}}</p>
<p class="card-text">Genre : {{movie.genre}}</p>
<p class="card-text">Score : {{movie.score}}</p>
<p class="card-text overflow-auto bg-light p-2" style="height:300px" >{{movie.description}}</p>
<div class="d-flex mt-5">
<a href="{% url 'movies:index' %}" class="btn btn-secondary">목록</a>
<a href="{% url 'movies:update' movie.pk%}" class="btn btn-success">수정</a>
<form action="{% url 'movies:delete' movie.pk%}" method="POST" >
{% csrf_token %}
<input class="btn btn-danger " type="submit" value="삭제">
</form>
</div>
</div>
</div>
{% endblock content %}
pk를 가지고 받아온 movie 객체의 요소를 하나씩 꺼내서 모든 정보를 보여주는 화면.
create.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<h1>CREATE</h1>
<form action="{% url 'movies:create' %}" method="POST">
{% csrf_token %}
<div style="width:500px">
{%bootstrap_form form %}
<div class="container">
<input class="my-1 btn btn-primary col-12" type="submit" value="작성">
<a class="my-1 btn btn-secondary col-12" href="{% url 'movies:index' %}">목록으로</a>
</div>
</divc>
</div>
</form>
{% endblock content %}
모델폼의 form.as_p를 이용했었는데, 부트스트랩 bootstrap_form form을 이용하니 훨씬 깔끔했다.
버튼을 한줄에 꽉 차게 넣고싶어서 grid를 사용했다.
update.html
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<h1>UPDATE</h1>
<form action="{% url 'movies:update' movie.pk %}" method="POST">
{% csrf_token %}
<div style="width:500px">
{%bootstrap_form form %}
</form>
<div class="container">
<input class="my-1 btn btn-primary col-12" type="submit" value="수정">
<input class="my-1 btn btn-danger col-12" type="reset" value="초기화">
<a class="my-1 btn btn-secondary col-12" href="{% url 'movies:detail' movie.pk %}">뒤로가기</a>
</div>
{% endblock content %}
bootstrap_form을 이용했다.
프로젝트를 만들때 models, views, templates 의 흐름을 따라가면서 장고의 MTV모델을 이해했다.
장고 CRUD는 확실히 이해한 것 같아서 만족!
'Back-end > Django' 카테고리의 다른 글
Django M:N 관계 - ManyToManyField (0) | 2022.10.12 |
---|---|
Django : static files 관리 / image 업로드, 출력, 수정, resizing (0) | 2022.10.11 |
Django User 모델 참조 : settings.AUTH_USER_MODEL / get_user_model() (0) | 2022.10.05 |
Django Relationship field - 1:N , FK, article-comment 구현 (0) | 2022.10.05 |
RDB 관계 - 1:1, 1:N, M:N / 외래키(Foreign Key) (0) | 2022.10.05 |