์ผ๋๋ค ๊ด๊ณ๋ ๊ฒ์๊ธ-๋๊ธ ๊ด๊ณ๋ผ๊ณ ์ดํดํ๋ฉด ์ฝ์ต๋๋ค. ํ ๊ฐ์ ๊ฒ์๊ธ์ ์ฌ๋ฌ ๋๊ธ์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ์ผ๋๋ค ๊ด๊ณ์ ๋๋ค. ์ฐ๋ฆฌ๊ฐ ๋ง๋ค์๋ Blog๋ชจ๋ธ๊ณผ ๊ฐ์ Post๋ชจ๋ธ์ ๋ง๋ค์๋ค๋ฉด, ๋๊ธ์ ํด๋นํ๋ Comment๋ชจ๋ธ์ ์ด๊ฒฐํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ด ํ๋ฉด ๋ฉ๋๋ค.
class Post(models.Model):
...
class Comment(models.Model):
post = models.Foreignkey(Post, on_delete = models.CASCADE)
ORM์ ์ด์ฉํด ์๋ ๋ชจ๋ธ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ
- N->1:
comment.post
- 1->N:
post.comment_set
,post.comment_set.all()
,post.comment_set.count()
N์์๋ ๊ด๊ณ๋์ด ์๋ ๋ชจ๋ธ์ด ํ๋์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ์ง๋ง, 1์ธ ์ชฝ์ ์ฌ๋ฌ ๊ฐ์ฒด์ ์ฐ๊ด๋์ด์๊ธฐ ๋๋ฌธ์ set์ ํตํด ์ ๊ทผํด์ผํฉ๋๋ค.
๊ฒ์๊ธ์ด ์ญ์ ๋๋ค๋ฉด ๊ทธ ๊ฒ์๊ธ์ ์๋ ๋๊ธ์ ์ด๋ป๊ฒ ๋ ๊น์? ์ด๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง์ ๋ํ ์ค์ ์ ํ๊ธฐ ์ํด on_delete ์ธ์๋ฅผ ์์ฑํด์ผํฉ๋๋ค.
on_delete์ค์ : 1์ชฝ์ ๋ฐ์ด์ฒ ์ญ์ ์ N์ชฝ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ
- CASCADE: ๋ชจ๋ N์ชฝ์ ๋ฐ์ดํฐ ์ญ์
- PROTECT: 1์ชฝ์ ๋ฐ์ดํฐ๊ฐ ์ญ์ ๋์ง ์๋๋ก ๋ณดํธ.
- SET_NULL: null๊ฐ์ ๋์ฒด, ํ๋์ default=True์ต์ ์ ์ถ๊ฐํด์ผํจ.
- SET: ๋์ฒดํ ๊ฐ์ด๋ ํจ์๋ฅผ ์ง์
- DO_NOTHING: ์๋ฌด๊ฒ๋ ํ์ง ์์, ๋ค๋ง DB์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๋ฆ
์ผ๋์ผ ๊ด๊ณ๋ ์ ์ -ํ๋กํ ๊ด๊ณ๋ก ์ดํดํ๋ฉด ์ฝ์ต๋๋ค. ์๋ก ๋์๋๋ ๊ฒ์ด ์ ์ผํ ๊ด๊ณ์ ๋๋ค. ์ฃผ๋ก Django์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์๋์ด ์๋ User ๋ชจ๋ธ๊ณผ ์ด๋ฅผ ์ปค์คํ ํด ์๋ก ๋ง๋๋ Profile๋ชจ๋ธ์ ์ฐ๊ฒฐํ ๋ ์ฌ์ฉํฉ๋๋ค.
class User(AbstractBasaeUser):
...
class Profile(models.Model):
user = models.OneTooneField(User, on_delete=models.CASCADE)
๋ง์ฐฌ๊ฐ์ง๋ก ๋์ ํด๋์ค๊ฐ ์ญ์ ๋ ๊ฒฝ์ฐ ์ด์๊ฐ ๋ฐ์ํ๋ฏ๋ก on_delete์ค์ ์ ํด์ค๋๋ค.
์๋ ๋ชจ๋ธ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ user.profile
, profile.user
์
๋๋ค. ์๋ก ๋์๋๋ ๋ชจ๋ธ์ด ์ ์ผํ๋ฏ๋ก ๋ฐ๋ก ์ ๊ทผํ ์ ์์ต๋๋ค.
๋ค๋๋ค ๊ด๊ณ๋ ํฌ์คํธ-ํ๊ทธ ๊ด๊ณ๋ก ์ดํดํ๋ฉด ์ฝ์ต๋๋ค. ์ธ์คํ๊ทธ๋จ ๊ฒ์๊ธ์ ์๊ฐํด๋ณด๋ฉด ๊ฒ์๊ธ์ ์ฌ๋ฌ๊ฐ์ ํด์ํ๊ทธ๋ฅผ ๊ฐ์ง ์ ์๊ณ , ํ๋์ ํด์ํ๊ทธ์ ํด๋น๋๋ ๊ฒ์๊ธ์ด ์ฌ๋ฌ๊ฐ ์ผ ์ ์๋ ๊ฒ์ ๋๋ค.
๋ค๋๋ค ๊ด๊ณ๋ ์ด๋ ๋ชจ๋ธ์ ์ ํํด ์์ฑํด์ฃผ์ด๋ ๋ฌด๋ฐฉํ์ง๋ง ์ถ๊ฐ ๋๋ ๋ชจ๋ธ์ ์ ์ํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๋ค๋ง, ๋จผ์ ์ ์ธ๋ ๋ชจ๋ธ์์ ๋ค๋๋ค ๊ด๊ณ๋ฅผ ์ ์ํด์ฃผ๋ ๊ฒฝ์ฐ์๋ ๋ฌธ์์ด('Tag')๋ก ํํํฉ๋๋ค.
class Post(models.Model):
tag = models.ManyToManyField('Tag', blank=True)
class Tag(models.Model):
...
class Post(models.Model):
...
class Tag(models.Model):
post = models.MnyToManyField(Post, blank=True)
์ง์ ์ ์ ๋ชจ๋ธ์ ์์ฑํ์ฌ ํ์๊ฐ์
, ๋ก๊ทธ์ธ์ ๊ตฌํํ ์๋ ์์ง๋ง ์ฐ๋ฆฌ๋ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋๋ก ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ django-allauth
๋ฅผ ์ด์ฉํด๋ณด๊ฒ ์ต๋๋ค.
$ pip install django-allauth
๋จผ์ settings.py์ ์ค์ ์ ์ถ๊ฐํด์ค๋๋ค.
INSTALLED_APPS = [
...
# ์ฌ์ดํธ ํ๋ ์์ํฌ, ์ฌ์ดํธ ๊ธฐ๋ณธ ์ ๋ณด๋ฅผ ์ค์ ํ ์ ์๋ค.
'django.contrib.sites',
# allauth ๊ด๋ จ ์ฑ: ์ผ๋ฐ ๊ณ์ ๊ด๋ฆฌ, ์์
๊ณ์ ๊ด๋ฆฌ
'allauth',
'allauth.account',
'allauth.socialaccount',
]
...
AUTHENTICATION_BACKENDS=[
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]
# admin ํ์ด์ง ๊ด๋ฆฌ ๋ฒํธ, ๋จ์ผ ์๋ฒ๋ 1์ ์
๋ ฅํ๋ฉด ๋๋ค.
SITE_ID=1
# ๋ก๊ทธ์ธ ๋๋ฉด ์ด๋ํ๋ ํ์ด์ง, ๋ง์ฝ ๊ธ ๋ชฉ๋ก์ผ๋ก ์ด๋ํ๊ณ ์ถ๋ค๋ฉด '/posts/'
# ๋ฉ์ธํ๋ฉด์ผ๋ก ์ด๋
LOGIN_REDIRECT_URL='/'
urls.py์์ url์ฐ๊ฒฐ๋ ํด์ค๋๋ค.
urlpatterns = [
...
path('accounts/', include('allauth.urls')),
]
๊ทธ ๋ค์์ migrate๋ฅผ ํด์ค๋๋ค.
$ python manage.py migrate
์ฐ๋ฆฌ๊ฐ ๋ชจ๋ธ์ ๋ง๋ค์ง๋ ์์์ง๋ง, django-allauth ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ชจ๋ธ์ ์ ์ฉ์์ผ์ฃผ๊ธฐ ์ํด ํด์ฃผ์ด์ผ ํฉ๋๋ค.
basic/templates/shared/_navbar ๋ค๋ธ๋ฐ์ ๋ก๊ทธ์ธ๊ณผ ํ์๊ฐ์ ์ ์ถ๊ฐํด์ฃผ๊ฒ ์ต๋๋ค.
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Account
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
{% if user.is_authenticated %} # ๋ก๊ทธ์ธํ ์ ์ ๋ผ๋ฉด
<li><a class="dropdown-item" href="{%url 'account_logout' %}">Logout</a></li>
{% else %} # ๋ก๊ทธ์ธํ์ง ์์๋ค๋ฉด
<li><a class="dropdown-item" href="{% url 'account_login' %}">Login</a></li>
<li><a class="dropdown-item" href="{%url 'account_signup' %}">Signup</a></li>
{% endif %}
</ul>
</li>
๋ก๊ทธ์ธํ์ ๊ฒฝ์ฐ์๋ ๋ก๊ทธ์์์ ๋ณด์ฌ์ฃผ๋๋ก, ๋ก๊ทธ์ธํ์ง ์์๋ค๋ฉด ๋ก๊ทธ์ธ๊ณผ ํ์๊ฐ์ ์ ๋ณด์ฌ์ฃผ๋๋ก if๋ฌธ ์ฒ๋ฆฌ๋ฅผ ํด์ค๋๋ค.
๋ก๊ทธ์ธํ์ง ์์๋ ๊ธ์ ๋ณผ ์ ์๋ ๊ฒ์ด ์๊ด์๋ค๋ฉด ๊ตณ์ด ํ์ง ์์๋ ๋์ง๋ง, ๋ก๊ทธ์ธํ ํ์๋ค๋ง ๊ธ์ ๋ณผ ์ ์๋๋ก ์ค์ ํด์ฃผ๊ณ ์ถ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ๋ฉด ๋ฉ๋๋ค. main/templates/main/posts.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="container">
{% if user.is_authenticated %}
<h1>๊ธ ๋ชฉ๋ก</h1>
<div>
{% for blog in blogs %}
<div>
<h3>{{blog.title}}</h3>
{{blog.writer}}<br>
{{blog.summary}}
<a href="{%url 'main:detail' blog.id%}">...more</a>
<br><hr>
</div>
{%endfor%}
</div>
{% else %}
<h3>๋ก๊ทธ์ธ ํ ์ด์ฉ ๊ฐ๋ฅ</h3>
<a href="{% url 'account_login' %}" class="btn btn-warning">๋ก๊ทธ์ธ</a>
<a href="{% url 'account_signup' %}" class="btn btn-warning">ํ์๊ฐ์
</a>
{% endif %}
</div>
{% endblock %}
๐ก ๋ก๊ทธ์ธ, ๋ก๊ทธ์์, ํ์๊ฐ์ ๋ฑ์ ํ์ด์ง ์ปค์คํ ๋ฐฉ๋ฒ
๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ํ์ด์ง๊ฐ ์์์ง๋ ์์ต๋๋ค. ์ด๋ฅผ ์ปค์คํ ํ ์ ์๋๋ฐ, ์๋ ์ฌ์ดํธ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
- https://ngee.tistory.com/2161
- https://github.com/pennersr/django-allauth/tree/master/allauth/templates/account
์ด์ ์๋ ๊ธ ์์ฑ์๋ฅผ ์ฐ๋ฆฌ๊ฐ ์๊ธฐ๋ก ์ ๋ ฅํ๋๋ก ์ค์ ํ์์ต๋๋ค. ์ด ๋ถ๋ถ์ user ๋ชจ๋ธ์ ์ ๋ณด์ ์ฐ๊ฒฐํ์ฌ ๋ก๊ทธ์ธํ ์ ์ ๋ฅผใน ์์ฑ์๋ก ๋ฐ์์ฌ ์ ์๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค. ๋ชจ๋ธ์ ์์ ํด์ผํ๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด์ ์์ฑํ๋ ๊ธ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฏ๋ก db๋ฅผ ์ง์์ฃผ๊ณ ์์ํ๊ฒ ์ต๋๋ค.
db.sqlite3 ํ์ผ์ ์ญ์ ํด์ฃผ์ธ์.
main/templates/main/new.html
<p>์์ฑ์ : <input type="text" name="writer"></p>
main/templates/main/edit.html
<p>์์ฑ์ : <input type="text" name="writer" value="{{blog.writer}}"></p>
์์ ์์ฑ์ input ํ๊ทธ๋ฅผ ์ง์์ฃผ์ธ์.
def create(request):
...
new_blog.writer = request.user
def update(request, id):
...
update_blog.writer = request.user
create์ updateํจ์์์ ์์ฑ์ ๋ถ๋ถ์ ํ์ฌ ์์ ์ ์ํ์ค์ธ ์ ์ ๋ฅผ ์ ์ฅํ๋๋ก ๋ฐ๊พธ์ด์ค๋๋ค.
from django.contrib.auth.models import User
class Blog(models.Model):
...
writer = models.ForeignKey(User, on_delete=models.CASCADE)
๋จผ์ django-allauth์ User๋ชจ๋ธ์ import ํด์ค๋๋ค. ๊ทธ๋ฆฌ๊ณ Blog๋ชจ๋ธ์ writer์์ฑ์ ์ด User๋ชจ๋ธ๊ณผ ์ฐ๊ฒฐํด์ฃผ์ด์ผํฉ๋๋ค. ๊ฒ์๊ธ์๋ ํ ๋ช
์ ์์ฑ์๋ง ์๊ณ , ์์ฑ์๋ ์ฌ๋ฌ ๊ฒ์๊ธ์ ์์ฑํ ์ ์์ผ๋ ์ผ๋๋ค ๊ด๊ณ์
๋๋ค. ๋งจ ์ฒ์์ ๋ชจ๋ธ ๊ด๊ณ ๊ฐ๋
๋ ๋ดค๋ ๊ฒ ์ฒ๋ผ ์ผ๋๋ค ๊ด๊ณ๋ '๋ค'์ชฝ์ ํด๋นํ๋ ๋ชจ๋ธ์ ForeignKey๋ก ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋ฉ๋๋ค. ๊ฒ์๊ธ์ด ์ง์์ง๋ฉด ๊ทธ ๊ฒ์๊ธ์ ์์ฑ์ ์ ๋ณด๋ฅผ ํ์์์ผ๋ฏ๋ก ์ง์์ง๋๋ก on_delete
์ค์ ์ ํด์ค๋๋ค.
๋ชจ๋ธ์ ์์ ํ์์ผ๋ migrateํด์ฃผ์ด์ผ ํฉ๋๋ค.
$ python manage.py makemirgations
$ python manage.py migrate