DATABAS
- 여러 사람이 공유하여 사용할 목적으로 체계화하여 관리하는 데이터의 집합이다.
RDB(관계형 데이터베이스)
- 관계형 모델을 기반으로 하는 전통적인 데이터베이스
RDBMS
- 관계형 데이터베이스를 관리하는 시스템
- SQLite
- ORACLE
- SQLServer
- MySQL
- 등...
- 여러 사람이 동시에 수정할 수 있는 거대한 스프레드시트의 집합(시험)
- 게시글(Article)
- 스키마를 기반으로 만들어지는 실제 데이터가 ROW
관계형 데이터베이스?
데이터베이스에는 많은 테이블이 존재할수 있다.
그리고 그 테이블 사이에는 관계가 지어질수있다.
이유는? 현실 세계를 표현하기위해!!!
게시글(댓글)
약속 1.
하나의 필드에는 하나의 값만 저장할 것.
필드가 너무 늘어난다, 매번 스키마가 변해야한다.
약속 2.
컬럼 이름은 고유하게 하나만 만들 것
약속 3.
RDB에서 가능하면 레코드 값의 중복은 피할 것
1:N 관계
하나의 게시글은 여러개의 댓글을 가질 수 있다.
foreignKey
models.ForeignKey(필수1, 필수2)
필수1 : 참조하려는 모델 객체(class이름)
필수2: "on_delete="가 필요합니다.
( CASCADE, PROTECT ) 기입 가능
SET_NULL (NULL=TRUE)
SET_DEFAULT(디폴트 필요)
우리는 CASCADE를 주로사용하게 된다.
Code
빈공간에서 vs code를 열어줍니다.
django-admin startproject config .
python manage.py startapp articles
그리고 settings.py의 installed_apps 에 articles 를 추가해줍니다.
다음 models.py 로 이동 class만들어주기
추가적으로 게시글이 지워지면 댓글도 같이지워져야겠습니다.
만약에 부모 테이블이 지워졌을때 댓글만 남게 되면 어떻게되나?
이를 orphan 이라한다..(고아)
이런일이 없도록 CASCADE라는 것을 추가시켜줍니다.
(만약 그대로 지킬려면 protect를 사용해줍니다.)
(https://docs.djangoproject.com/en/3.1/ref/models/fields/#arguments)
from django.db import models
# Create your models here.
class Article(models.Model):
# 1 : Parent table
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
# N : Child Table
content = models.TextField()
# article = models.Foreignkey('Article', on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE) # article의 pk 저장
다음 settings.py 에서 shell_plus 를 사용할수있도록 셋팅
INSTALLED_APPS = [
'articles',
'django_extensions',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
다음 쉘 플러스를 켜줍니다.
python manage.py shell_plus
comment = Comment()
comment.context='댓글1'
article = Article.objects.create(title='1번게시글', content='1번 내용')
comment.article = article
comment.save()
comment.article
comment.article.title
2번만들기
comment2 = Comment()
comment2.context='댓글2'
article = Article.objects.create(title='2번게시글', content='2번 내용')
comment2.article = article
comment2.save()
위에는 N: 1을 표시하여 확인한것이고
아래는 1:N 을 소환하는것
article.comment_set.all()
쿼리값으로 나올 것입니다.
Article.comment_set.all()
저 녀석은 되지않는다
인스턴스 == 실제 게시글 data이다.
Class == 스키마 == 독단적인
ex) 1번 게시글의 2번째 댓글의 내용은?
article.comment_set.all()[1].content
article.comment_set.get(pk=2).content
3번째 댓글
comment3 = Comment()
comment3.content = '3'
comment3.article = article
comment3.save()
article.comment_set.all()
이시점부터는 https://docs.djangoproject.com/en/3.1/topics/db/queries/를 이용해야한다.
댓글달기 coding
articles
forms.py
from django import forms
from .models import Article, Comment
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = '__all__'
# exclude = ('title',)
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content',) # comma please...!
models.py
from django.db import models
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=10)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return f'/articles/{self.pk}/'
class Comment(models.Model):
content = models.TextField() #NOT NULL
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
)
urls.py
path('<int:article_pk>/comments/', views.create_comment, name='create_comment'),
views.py
def detail(request, pk):
article = get_object_or_404(Article, pk=pk)
form = CommentForm() # Comment 폼 생성
comments = article.comment_set.all()
context = {
'article': article,
'form':form,
}
return render(request, 'articles/detail.html', context)
def create_comment(request, article_pk):
#1. 댓글이 가리킬 게시글을 가져옵니다.
article = Article.objects.get(pk=article_pk)
#2. 사용자 입력값 (댓글)을 꺼내서
form = CommentForm(request.POST)
#3. 유효성 검사를 하고
if form.is_valid():
#4. 저장합니다.
#commit=False 설정시 시점에 DB 저장 x
# pk 값이 없는 comment를 반환
comment = form.save(commit=False) # 이 시점에 DB 반영
comment.article = article
comment.save()
#redirect('articles:detal', article.pk)
#article 사용시 models에서 get absolute_url 사용해야합니다.
return redirect(article)
detail.html
{% extends 'base.html' %}
{% block content %}
<h2>DETAIL</h2>
<h3>{{ article.pk }} 번째 글</h3>
<hr>
<p>제목 : {{ article.title }}</p>
<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>
<hr>
<form action="{% url 'articles:create_comment' article.pk %}" method="POST">
{% csrf_token %}
{{ form }}
<button>댓글 작성</button>
</form>
<hr>
{% for comment in comments %}
<div>{{ comment.content }}</div>
{% endfor %}
{% endblock %}
for 문 comments를 article.comment_set.all 로해도됨
오후 이어서(로그인후 댓글)
@require_POST를 사용하게된다면
유효성 검사가 통과하지 못하는 에러를 폼에 담아
보여주기 위한 것이라 적었지만 현재 로그인시에만 댓글적을수있도록
@require_POST
def create_comment(request, article_pk):
if not request.user.is_authenticated:
return redirect('accounts:login')
#1. 댓글이 가리킬 게시글을 가져옵니다.
article = Article.objects.get(pk=article_pk)
#2. 사용자 입력값 (댓글)을 꺼내서
form = CommentForm(request.POST)
#3. 유효성 검사를 하고
if form.is_valid():
#4. 저장합니다.
#commit=False 설정시 시점에 DB 저장 x
# pk 값이 없는 comment를 반환
comment = form.save(commit=False) # 이 시점에 DB 반영
comment.article = article
comment.save()
#redirect('articles:detail', article.pk)
#article 사용시 models에서 get absolute_url 사용해야합니다.
return redirect(article)
Custom User
- 프로젝트 시작 전에 accounts 앱을 만든다.
- 커스텀 User 모델을 생성한다.
- settings.py에 AUTH_USER_MODEL을 변경한다.
- 프로젝트 시작.
accounts models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
pass
crud settings.py
맨 아래 추가해주기
AUTH_USER_MODEL = 'accounts.User'
makemigrations
migrate
게시글 작성자 넣기
게시글 유저
1 N > 좋아요,
N 1 > 댓글
articles models.py
from django.db import models
from django.conf import settings
# User 모델을 가져오는 방법
# 1. get_user_model() => models.py 제외
# 2. settings.AUTH_USER_MODEL => models.py에서만 사용
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=10)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def get_absolute_url(self):
return f'/articles/{self.pk}/'
class Comment(models.Model):
content = models.TextField() #NOT NULL
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
)
python manage.py makemigrations
python manage.py migrate
홈페이지를 켜본다. (회원가입시 오류걸림 ㅋㅋ)
accounts forms.py
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.contrib.auth import get_user_model
User = get_user_model() # 활성화 유저 모델
class CustomUserCreationForm(UserCreationForm):
'''
UserCreationForm을 커스텀마이징 하는 이유는
UserCreationForm이 장고 내장 유저모델을 가르키기 떄문입니다
'''
class Meta(UserCreationForm.Meta):
model = User
fields= UserCreationForm.Meta.fields + ('email',)
class CustomUserChangeForm(UserChangeForm):
password = None
class Meta:
model = User
fields = ('email','first_name','last_name')
account views.py
signup 함수를 바꿔준다 커스텀유저크리에시션으로
CustomUserCreationForm 이친구 꼭 임포트 추가시켜주세용
def signup(request):
if request.method == 'POST':
form = CustomUserCreationForm(request.POST)
if form.is_valid():
user = form.save()
auth_login(request, user) # 로그인 == 세션 생성
# 회원가입 축하 메세지를
#request에 담아서 같이 보냅니다.
messages.add_message(
request, messages.SUCCESS, '회원가입을 축하합니다!'
)
return redirect('articles:index')
else:
form = CustomUserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
다음 이상이있으면
articles의 views.py 이상있는지 체크, forms.py 이상있는지체크
제출에 이상이있으면
url > views.py >
account views.py 바뀐것.
def create(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
# commit=False
# 실제 DB에 저장은 하지 않지만
# form에 담긴 인스턴스를 반환합니다.
article = form.save(commit=False) #<<<
article.author = request.user#<<<
article.save() # DB 저장 #<<<< 진짜 저장
return redirect(article) #<<< ㅎㅇ
else:
form = ArticleForm()
context = {
'form': form,
}
return render(request, 'articles/create.html', context)
articles index.html (글쓴이 추가)
{% extends 'base.html' %}
{% block content %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<h1>Articles</h1>
<a href="{% url 'articles:create' %}">[CREATE]</a>
<hr>
{% for article in articles %}
<p>글쓴이 : {{ article.author.username }}</p>
<p>글 번호 : {{ article.pk }}</p>
<p>글 제목 : {{ article.title }}</p>
<p>글 내용 : {{ article.content }}</p>
<a href="{% url 'articles:detail' article.pk %}">[DETAIL]</a>
<hr>
{% endfor %}
{% endblock %}
쓴사람만 수정 및 삭제가 가능하도록 하기
detail.html
{% extends 'base.html' %}
{% block content %}
<h2>DETAIL</h2>
<h3>{{ article.pk }} 번째 글</h3>
<hr>
<p>제목 : {{ article.title }}</p>
<p>내용 : {{ article.content }}</p>
<p>작성시각 : {{ article.created_at }}</p>
<p>수정시각 : {{ article.updated_at }}</p>
<hr>
{% if article.author == request.user %}
<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>
{% endif %}
<hr>
<form action="{% url 'articles:create_comment' article.pk %}" method="POST">
{% csrf_token %}
{{ form }}
<button>댓글 작성</button>
</form>
<hr>
{% for comment in comments %}
<div>{{ comment.content }}</div>
{% endfor %}
{% endblock %}
views.py
(삭제 방어)
@require_POST
def delete(request, pk):
article = get_object_or_404(Article, pk=pk)
if article.author == request.user:<<<< 이친구
article.delete()
return redirect('articles:index')
(업데이트 방어)
@require_http_methods(['GET','POST'])
def update(request, pk):
article = get_object_or_404(Article, pk=pk)
if article.author != request.user:
return redirect('articles:index')
if request.method == 'POST':
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
form.save()
return redirect('articles:detail', article.pk)
else:
form = ArticleForm(instance=article)
context = {
'form': form,
'article': article,
}
return render(request, 'articles/update.html', context)
오류부분
해결 방법 - views.py 에서 댓글을 저장을해줘야한다
def comment_create(request, pk):
#댓글 저장
article = get_object_or_404(Article, pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)#<<<<<<<<<<<<<<<<
comment.article = article#<<<<<<<<<<<<<<<<
#comment.article_id = pk#<<<<<<<<<<<<<<<<
comment.save()
return redirect('articles:detail', pk)
context = {
'form', form,
}
return render(request, 'articles/detail.html', context)
모든 댓글을 가져오면안된다. ( views) 댓글을 보여주게하기위한것
def detail(request, pk):
article = get_object_or_404(Article, pk=pk)
form = CommentForm() # Comment 폼 생성
comments = article.comment_set.all()#"article." 을 꼭 붙여줘라
context = {
'article': article,
'form':form,
}
return render(request, 'articles/detail.html', context)
삭제하기
detail.html
{% extends 'base.html' %}
{% block content %}
<h2>DETAIL</h2>
<h3>{{ article.pk }} 번째 글</h3>
<hr>
<p>제목 : {{ article.title }}</p>
<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>
<hr>
<ul>
{% for comment in comments %}
<li>{{ comment.content }}</li>
<form action="#" method="POST">
{% csrf_token %}
<button>X</button>
</form>
{% endfor %}
</ul>
<form action="{% url 'articles:comment_create' article.pk %}" method="POST">
{% csrf_token %}
{{ comment_form.as_p }}
<button>댓글작성</button>
</form>
{% endblock %}
urls.py(restful 한 표현 방법)
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
path('', views.index, name='index'),
path('create/', views.create, name='create'),
path('<int:pk>/', views.detail, name='detail'),
path('<int:pk>/delete/', views.delete, name='delete'),
path('<int:pk>/update/', views.update, name='update'),
path('<int:pk>/comment/',views.comment_create, name='comment_create'),
path('<int:pk>/articles/<int:comment_pk>/comment/delete/', views.comment_delete, name='comment_delete'),
]
views.py ( comment, delete 넣기)
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_safe, require_http_methods, require_POST
from .models import Article, Comment #<<<<<<<<<<
from .forms import ArticleForm,CommentForm
# Create your views here.
@require_safe
def index(request):
articles = Article.objects.order_by('-pk')
context = {
'articles': articles,
}
return render(request, 'articles/index.html', context)
@require_http_methods(['GET', 'POST'])
def create(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save()
return redirect('articles:detail', article.pk)
else:
form = ArticleForm()
context = {
'form': form,
}
return render(request, 'articles/create.html', context)
@require_safe
def detail(request, pk):
article = get_object_or_404(Article, pk=pk)
comment_form = CommentForm()
context = {
'article': article,
'comment_form':comment_form,
'comments':comments,
}
return render(request, 'articles/detail.html', context)
@require_POST
def delete(request, pk):
article = get_object_or_404(Article, pk=pk)
article.delete()
return redirect('articles:index')
@require_http_methods(['GET', 'POST'])
def update(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == 'POST':
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
form.save()
return redirect('articles:detail', article.pk)
else:
form = ArticleForm(instance=article)
context = {
'form': form,
'article': article,
}
return render(request, 'articles/update.html', context)
def comment_create(request, pk):
#댓글 저장
article = get_object_or_404(Article, pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.article = article
#comment.article_id = pk
comment.save()
return redirect('articles:detail', pk)
context = {
'form', form,
}
return render(request, 'articles/detail.html', context)
@require_POST #<<<<<<<<<<<<<<<<<<<<<<<<
def comment_delete(request, pk, comment_pk):
comment = get_object_or_404(Comment, pk=comment_pk)
comment.delete()
return redirect('articles:detail', pk)
유저관련
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
pass
이 에러가 나타나는 의미는 기존 user가 있는데 왜 또 user를 만드냐?
그럼 뒤도 안돌아보고 settings.py 에서 맨밑에
AUTH_USER_MODEL = 'accounts.User'
추가시켜준다. 그리고 마이그레이션 오류만 뜰것이다.
makemigrtions ㄱ
그 후 프로젝트 중간이니까 데이터베이스를 삭제해줘야한다.(서버꺼놓구해..)
migrate ㄱ
그럼 유저 커스텀 끘
누가적은지 확인하기위해서 아티크 모델에서
+from django.conf import settings
유저객체 = settings.AUTH_USER_MODEL
user = models.ForeignKey( 유저객체, on_delete = models.CASCADE)
위에는 models.py 에서만!!!!!
사용하고 그 외에는get_user_model() 을 사용한다
'SSAFY > Django' 카테고리의 다른 글
[Django] 댓글 삭제 (0) | 2021.03.26 |
---|---|
[Django] SQL 과 장고 ORM 사용 방법 (0) | 2021.03.26 |
[Django] 회원가입 만들기 (0) | 2021.03.22 |
[Django]Form (0) | 2021.03.21 |
[DJANGO] STATIC FILES (0) | 2021.03.19 |