프로젝트 이름은 crud, 앱 이름은 articles로 설정한다.
venv
python -m venv venv
: venv 환경 만들기source venv/Scripts/activate
: 가상환경 켜기pip install django
pip install -r requirements.txt
pip install pillow
pip freeze > requirements.txt
환경 저장gitignore 파일 만들기
cmd
django-admin startproject CRUD
python manage.py startapp articles
: articles 어플만들기articles
CRUD.settings.py
에서 INSTALLED_APPS 에서 articles 추가한글과 시간 설정
crud.settings.py
에서 BASE.HTML
만들기
CRUD.templates
내에 base.html 만들기
{% block content %}
{% endblock %}
CRUD.settings.py
내에 TEMPLATES 내에 DIRS
'DIRS': [BASE_DIR / 'crud' / 'templates'],
Bootstrap CDN 을 BASE.html에 넣기
파일 넣기
CRUD.static/stylesheets
경로 만들기 CRUD.static/javascript
경로 만들기base.html
에 가서
맨 위에 {% load static %}
<link rel="stylesheet" href="{% static 'stylesheets/bootstrap.css' %}">
<script src="{% static 'javascript/bootstrap.js' %}"></script>
CRUD.settings.py
STATICFILES_DIRS= [ BASE_DIR / '프로젝트이름' / 'static']
추가bootstrap5 모듈 적용해보기 - update 페이지에서 사용해봄
pip install django-bootstrap-v5
프로젝트.settings.py > INSTALLED_APPS
"bootstrap5"
추가하기articles.models.py
from django.db import models
# Create your models here.
class Article(models.Model) :
title = models.CharField(max_length=10) # charField : 길이에 제한이 있는 textfield에 대해서 사용
content = models.TextField()
image = models.ImageField(blank=True)
created_at = models.DateTimeField(auto_now_add=True) # auto_now_add = 작성일
updated_at = models.DateTimeField(auto_now=True) # auto_now = 수정일
데이터 베이스에 등록
python manage.py makemigrations
python manage.py migrate
articles.urls.py
만들기
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
path('', views.index, name = 'index'),
]
crud.urls.py
랑 articles.urls.py
랑 연결시키기
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('articles/', include('articles.urls')),
]
articles.views.py
from django.shortcuts import render
from .models import Article
# Create your views here.
def index(request) :
articles = Article.objects.order_by('-pk') # pk 역순 출력
context = {
'articles': articles,
}
return render(request, 'articles/index.html', context)
articles.templates/articles/index.html
만들기
{% extends 'base.html' %}
{% block content %}
<h1>Articles</h1>
<a href="{% url 'articles:create' %}">[CREATE]</a>
<hr>
{% for article in articles %}
<p>글 번호 : {{ article.pk }}</p>
<p>글 제목 : {{ article.title }}</p>
<a href="{% url 'articles:detail' article.pk %}">[DETAIL]</a>
<hr>
{% endfor %}
{% endblock %}
modelform 만들기
articles.forms.py
만들기from django.shortcuts import render, redirect
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
title = forms.CharField(
label='제목',
widget=forms.TextInput(
attrs={
'class': 'my-title form-control',
'placeholder': 'Enter the Title',
'maxlength': 10,
}
)
)
content = forms.CharField(
label='내용',
widget=forms.Textarea(
attrs={
'class': 'my-content form-control',
'placeholder': 'Enter the Content',
'rows': 5,
'cols': 30,
}
),
error_messages={
'required': '데이터를 입력해줄래...?',
}
)
class Meta:
model = Article
fields = '__all__'
# exclude = ('title',)
articles.urls.py
에 주소 추가
path('create/', views.create, name='create')
articles.views.py
에 메서드 추가
form = ArticleForm(request.POST, request.FILES)
매개변수 request.FILES 주의from django.views.decorators.http import require_http_methods
from .forms import ArticleForm
# 하나의 view 함수가 request의 method에 따라서 2가지 역할을 하게 됨
@require_http_methods(['GET', 'POST'])
def create(request):
# POST일 때
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
article = form.save()
return redirect('articles:detail', article.pk)
# GET일 때
else:
form = ArticleForm()
# 들여쓰기 주의
context = {
# 상황에 따른 2가지 모습
# 1. is_valid에서 내려온 form : 에러메세지를 포함한 form
# 2. else에서 내려온 form : 빈 form
'form': form,
}
return render(request, 'articles/create.html', context)
articles.templates/articles/create.html
만들기
{% extends 'base.html' %}
{% block content %}
<h1>CREATE</h1>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
<a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}
미디어 저장 장소 만들기
crud.settings.py
MEDIA_URL ='/media/'
: 서버가 Media 파일을 요청할 때 사용할 가상의 URLMEDIA_ROOT = BASE_DIR / 'media'
: Media 파일이 실제로 위치할 경로crud.urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# ... the rest of your URLconf goes here ...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# 이거는 static 파일들 경로가 추가되는것 - 자동으로 추가되어잇음
# + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
articles.urls.py
에 주소 추가
path('<int:pk>/', views.detail, name='detail'),
articles.views.py
에 메서드 추가
from django.views.decorators.http import require_safe
# require_safe는 get방식으로만 동작
@require_safe
def detail(request, pk):
# 몇번 글을 조회할건지 가져와야 함
article = Article.objects.get(pk=pk)
context = {
'article': article,
}
return render(request, 'articles/detail.html', context)
articles.templates/articles/detail.html
만들기
{{ article.image.url }}
{% extends 'base.html' %}
{% block content %}
<h2>DETAIL</h2>
<h3>{{ article.pk }} 번째 글</h3>
<hr>
<p>제목 : {{ article.title }}</p>
{% if article.image %}
<img src = "{{ article.image.url }}" alt="{{ article.image }}">
{% else %}
<p>이미지가 없습니다</p>
{% endif %}
<p>내용 : {{ article.content }}</p>
<p>작성시각 : {{ article.created_at }}</p>
<p>수정시각 : {{ article.updated_at }}</p>
<hr>
<a href="{% url 'articles:update' article.pk %}" class="btn btn-primary">[UPDATE]</a>
<form action="{% url 'articles:delete' article.pk %}" method="POST">
{% csrf_token %}
<button class="btn btn-danger">DELETE</button>
</form>
<a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}
articles.urls.py
에 주소 추가
path('<int:pk>/update/', views.update, name='update'),
articles.views.py
에 메서드 추가
@require_http_methods(['GET', 'POST'])
def update(request, pk):
article = Article.objects.get(pk=pk)
# update
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES, instance=article)
if form.is_valid():
form.save()
return redirect('articles:detail', article.pk)
# edit
else:
form = ArticleForm(instance=article)
context = {
'form': form,
'article': article,
}
return render(request, 'articles/update.html', context)
articles.templates/articles/update.html
만들기
{% extends 'base.html' %}
{% load bootstrap5 %}
{% block content %}
<h1>UPDATE</h1>
<form action="{% url 'articles:update' article.pk %}" method="POST" enctype="multipart/form-data" >
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
<hr>
<a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}
articles.urls.py
에 주소 추가
path('<int:pk>/delete/', views.delete, name='delete'),
articles.views.py
에 메서드 추가
@require_POST
def delete(request, pk):
# 삭제할 게시글 조회
article = Article.objects.get(pk=pk)
# 삭제 요청이 POST면 삭제, POST가 아니라면 DETAIL 페이지로 redirect
article.delete()
return redirect('articles:index')
cmd
python manage.py createsuperuser
articles.admin.py
from django.contrib import admin
from .models import Article # 현재 모델에서 article 테이블 가져오기
# Register your models here.
# Register your models here.
class ArticleAdmin(admin.ModelAdmin) :
list_display = ('pk', 'title', 'image','content', 'created_at', 'updated_at', ) # 현재 보일 column들을 tuple 혹은 list로 써주기
# admin site에 Article과 ArticleAdmin 을 등록하겠다.
admin.site.register(Article, ArticleAdmin)