Python 부트캠프(멀티잇 데이터 분석&엔지니어링 캠프) 에서 배운 django수업을 기반으로 직접 2023년 8월경 다른 블로그에 작성한 글을 가져왔습니다.
posts>models.py
class Comment(models.Model):
content = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
# 1:N 연결
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
python manage.py makemigrations
python manage.py migrate
postsforms.py
from .models import Post , Comment # models.py에 있는 class2가지
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content',) # 하나만 입력시에도 , 를 꼭 붙혀야!
postsviews.py
from .forms import PostForm, CommentForm #모델폼 만든거 추가
def index(request):
# posts = Post.objects.all()
# 모든 값을 보여줘
posts = Post.objects.all().order_by('-id')
# id값(작성순서)를 역순으로 보여줘
# 최신작성된 거 먼저 보여줘
comment_form = CommentForm()
context = {
'posts' : posts,
'comment_form' : comment_form
}
_card.html
맨 위
{% load bootstrap5 %}
맨 하단
<div class="card-footer">
<form action="" method="POST">
{% csrf_token %}
{% bootstrap_form comment_form %}
<input type="submit">
</form>
</div>


_card.html
<form action="{% ulr 'posts:comment_create' post_id=post.id %}" method="POST">
post_id = 변수화
post.id 는
post가 가지고 있는 id 번호
postsurls.py : 경로 생성
path('<int:post_id>/comments/create/', views.comment_create, name='comment_create'),
postsviews.py : 경로를 실행할 기능
작성자가 comment_form에 작성된 상태로 입력되면,
그 입력한 데이터를 처리
def comment_create(request, post_id):
comment_form = CommentForm(request.POST)
# 작성자가 댓글칸에 입력값(request.POST)을 CommentForm빈 폼에 기재한 것을
# comment_form이라고 한다.
if comment_form.is_valid():
comment = comment_form.save(commit=False)
# commit 은 데이터베이스에 저장하는 단위중 하나
# commit=False는 저장하기전까지만 해!
# comment에는 로그인유저 (댓글작성자) 와 작성한내용이 담긴다.
comment.user = request.user
# 그리고 그 유저가 작성한 내용
post = Post.objects.get(id=post_id)
comment.post = post
comment.save()
return redirect('posts:index')
이제 로그인된 상황에서 댓글작성해보자
로그아웃된 상태에서 작성하려면, error나온다.
Anonymouse 어쩌구

제출 누르면 db로 보내지긴 한다. 아직 저장값을 돌려받진 않는다!
_card.html
제일 하단
<hr>
{% for comment in post.comment_set.all %}
<!--커맨트가 가지고있는 모든 집합들을 하나하나-->
<li>{{comment.user}}:{{comment.content}}</li>
{% endfor %}
</div>
</div>


애초에 로그인하지않으면, 댓글창 안보이고
애초에 로그인하지 않으면, 실행할수 없도록 해보자.
_card.html
<div class="card-footer">
{% if user.is_authenticated %}
<form action="{% url 'posts:comment_create' post_id=post.id %}" method="POST">
{% csrf_token %}
{% bootstrap_form comment_form %}
<input type="submit">
</form>
</div>
<hr>
{% endif %}
views.py
from django.contrib.auth.decorators import login_required
@login_required
def create(request):
@login_required
def comment_create(request, post_id):
M의 데이터가 저장되어있는 테이블
N의 데이터가 저장되어있는 테이블그리고 M기준으로 N을 가지고있는 연결된 테이블 하나 더 추가
M:N이라는 폴더 생성후 VS코드로 들어가자
python -m venv venv
source venv/bin/activate
pip install django
django-admin startproject MN .
django-admin startapp movies
settings.py
INSTALLED_APPS = [
"movies",
]
models.py
from django.db import models
# Create your models here.
class Actor(models.Model):
pass
class Movie(models.Model):
pass
이제 각자 가지고있는걸 추가해주자
class Actor(models.Model):
name = models.CharField(max_length=100)
class Movie(models.Model):
title = models.CharField(max_length=100)
db접근
python manage.py makemigrations
python manage.py migrate
python manage.py shell
>>> from movies.models import Actor
>>> a = Actor()
a를 누르면
>>> a
<Actor: Actor object (None)>
'정우성'을 a 에 저장해보자
>>> a = Actor(name='정우성')
>>> a.save()
>>> a
<Actor: Actor object (1)>
movies에도 뭔가 넣어보자
from movies.models import Movies
m = Movie(title='더킹')
m.save()
m
나갈땐
>>> exit()
이렇게 하나하나 입력하기 귀찮아서 외부 라이브러리 이용해보자.
설치
pip install django-seed
pip install psycopg2-binary # 알아서설치안되니까 이것도
settins.py
INSTALLED_APPS = [
"django_seed",
]
10개의 가상데이터를 입력해주자
python manage.py seed movies --number=10

python manage.py shell
>>from movies.models import Actor, Movie

models.py
class Movie(models.Model):
title = models.CharField(max_length=100)
actors = models.ManyToManyField(Actor)
db접근
python manage.py makemigrations
python manage.py migrate
다시 shell
python manage.py shell
>>> from movies.models import Actor, Movie
>>> a1 = Actor.objects.get(id=1)
>>> m1 = Movie.objects.get(id=1)
>>> a1
<Actor: Actor object (1)>
>>> m1
<Movie: Movie object (1)>
>>> m1.actors
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x7fb9c8272100>
>>> m1.actors.all()
<QuerySet []>
>>> m1.actors.add(a1)
>>> m1.actors.all()
<QuerySet [<Actor: Actor object (1)>]>

| 배우 name | 영화 title |
|---|---|
| A | 1 |
| B | 2 |
| C | 3 |
| D | 4 |
>>> m2 = Movie.objects.get(id=2)
>>> m2.actors
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x7fb9c8272760>
>>> a1 = Actor.objects.get(id=1)
>>> m2.actors.add(a1)


models.py
class Movie(models.Model):
title = models.CharField(max_length=100)
actors = models.ManyToManyField(Actor, related_name='movies')
모델링 수정되었으니
python manage.py makemigrations
python manage.py migrate
models.py
from django.db import models
# Create your models here.
class Actor(models.Model):
name = models.CharField(max_length=100)
# movie_set = 생성됨
class Movie(models.Model):
title = models.CharField(max_length=100)
actors = models.ManyToManyField(Actor, related_name='movies')
# related_name : 기본적으로 관계설정을 하는 위치(Movie)의
# 반대편 모델(Actor)에 생기는 기본 컬럼 이름 (Movie_set)
# 을 movies로 변경
models.py
class Actor(models.Model):
name = models.CharField(max_length=100)
# movie_set = 생성되나, 아래 related_name = movies로 된 순간
# movies
# 로 이름만 바뀐다.
class Movie(models.Model):
title = models.CharField(max_length=100)
actors = models.ManyToManyField(Actor, related_name='movies')
# related_name : 기본적으로 관계설정을 하는 위치(Movie)의
# 반대편 모델(Actor)에 생기는 기본 컬럼 이름 (Movie_set)
# 을 movies로 변경
다시 shell
python manage.py shell
>>> from movies.models import Actor, Movie
>>> a1 = Actor.objects.get(id=1)
>>> a1.movies.all()
<QuerySet [<Movie: Movie object (1)>, <Movie: Movie object (2)>]>
>>> m1 = Movie.objects.get(id=1)
>>> m1.actors.all()
<QuerySet [<Actor: Actor object (1)>]>
>>>

hospital이라는 새로운 앱을 만들자
django-admin startapp hospital
그리고 settings.py에 해당app이름 추가
models.py
from django.db import models
# Create your models here.
class Doctor(models.Model):
name = models.CharField(max_length=10)
class Patient(models.Model):
name = models.CharField(max_length=10)
class Reservation(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
date = models.DateField()

models.py
from django.db import models
# Create your models here.
class Doctor(models.Model):
name = models.CharField(max_length=10)
# reservation_set 이 생김 (ForeignKey로 인해서)
class Patient(models.Model):
name = models.CharField(max_length=10)
# reservation_set 이 생김 (ForeignKey로 인해서)
doctors = models.ManyToManyField(Doctor, through='Reservation')
class Reservation(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
db접근
python manage.py makemigrations
python manage.py migrate
10개의 가상데이터를 넣어보자
python manage.py seed hospital --number=10
다시 shell
python manage.py shell
>>> from hospital.models import Doctor, Patient, Reservation
어떤 유저가 좋아요를 눌렀는지 user_id
유저가 좋아요 누른 게시글은 뭔지 post_id
그리고 좋아요 취소하면 다 삭제될수 있는지
posts의 models.py
accounts의 models.py
postsmodels.py
class Post(models.Model):
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# image = models.ImageField(upload_to='image/%Y/%m')
# 그리고 이걸 추가
image = ResizedImageField(
size=[500,500],
crop=['middle', 'center'],
upload_to='image/%Y/%m',
)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
# user 추가함으로서, accounts의 Modes.py와 1:N 연결
# user_id가 생성된거다.
like_users = models.ManyToManyField(settings.AUTH_USER_MODEL)
error 발생
지금 user , like_user 둘다
accounts의 models.py 에서 class User 를 불러옴으로써, post_set 으로 둘 다 쓰고 있어서,
에러
postsmodels.py
하나를 이름 바꿔준다.
like_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='like_posts')
하나가 삭제되면 끊기니께...
하나가 삭제되도 또하나가 있을테니께,...

python manage.py makemigrations
python manage.py migrate
하단의 Install 의 CDN 코드의 링크

basem.html
하단 link 추가
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
</head>
_card.html : 버튼 디자인 생성
<a href="{% url 'posts:like' post_id=post.id %}">
<i class="bi bi-heart"></i>
</a>
postsurls.py
path('<int:post_id>/like/',views.like, name='like'),
views.py : 버튼 기능 구현
@login_required # 로그인해야만 작동
def like(request, post_id):
# 조아요 버튼 누른 유저 = 로그인한사람
user = request.user
post = Post.objects.get(id=post_id)
# post 내가 좋아요 누른 게시글
# like_users 좋아요 누른 사람
post.like_users.add(user)
# post.like_posts.add(post)
# 사실 이 두 개는 동일하다.
# 유저에 게시글을 넣어주나,
# 게시글에 유저를 넣어주나,
# 그냥 연결 연결
return redirect('posts:index')



좋아요 버튼을 눌렀을때 error나면 안됌
postsviews.py
@login_required # 로그인해야만 작동
def like(request, post_id):
# 좋아요 버튼을 누른 유저
user = request.user
post = Post.objects.get(id=post_id)
# 좋아요 버튼을 누른경우,
# 이 유저가, 좋아요누른사람들 목록에 있나요?
if post in user.like_posts.all():
post.like_users.remove(user)
# user.like_posts.remove(post)와 같다.
# 좋아요 버튼을 아직 안누른 경우,
else:
post.like_users.add(user)
# user.like_posts.add(post)와 같다.
return redirect('posts:index')
M:N 은 remove와 add 사용 후 save() 안해도 됨
새로운 테이블을 따로 만들어 저장하기 때문에
save를 안해도 자동저장된다.
_card.html
<div class="card-body">
<!--<h5 class="card-title">Card title</h5>-->
<a href="{% url 'posts:like' post_id=post.id %}">
<!--좋아요 눌렀으면 하트색상변경-->
{% if post in user.like_posts.all %}
<i class="bi bi-heart-fill"></i>
<!--한번더 누르면 좋아요 취소-->
{% else %}
<i class="bi bi-heart"></i>
{% endif %}
</a>




또는
<div class="card-body">
<!--<h5 class="card-title">Card title</h5>-->
<a href="{% url 'posts:like' post_id=post.id %}">
<!--좋아요 눌렀으면 하트색상변경-->
{% if post in user.like_posts.all %}
<i class="bi bi-heart-fill"></i>
<!--한번더 누르면 좋아요 취소-->
{% else %}
<i class="bi bi-heart"></i>
{% endif %}
</a>{{post.like_users.all|length}}명이 좋아합니다.

bootstrap의 text를 이용해서 좀 더 글자 꾸미기
_card.html
<a href="{% url 'posts:like' post_id=post.id %}" class="text-reset text-decoration-none" >
<i class="bi bi-heart-fill" style="color: orchid;"></i>


좋아요 누르면, 최신게시물로 이동되서 좀 짜증
근데 이건 JavaScript코드에 대해 배워야함
이건 다음주에 개선
accountsmodels.py
class User(AbstractUser):
profile_image = ResizedImageField(
size=[500,500],
crop=['middle','center'],
upload_to='profile',
)
# post_set = 생성
# like_posts = 생성
followings = models.ManyToManyField('self', related_name='followers', symmetrical=False)
# 여기서 self 는 자기자신 user이긴한데, user라고 적으면 자기자신재귀가 빠져서 self라고
# 여기서 makemigration,migrate 작업하게되면 user_set이 db 컬럼으로 생성되는데,
# 우리는 헷갈리니까 자동생성되는 이 이름 수정할거야. followers 로!
# symmetrical 은 대칭이란 뜻으로,
# 유저 <---둘다팔로우---> 유저 로 쌍방향이 아닌,
# 유저 <--한사람만팔로우-- 유저 로 한방향으로 !
python manage.py makemigrations
python manage.py migrate
profile.html
<!--user는 로그인 유저와
user_info는 보고있는 프로필페이지 유저
가 다르다면, -->
{% if user != user_info%}
<div class="col-4"><a href="">팔로우</a></div>
{% endif %}

내 프로필페이지를 보고있자니,
팔로우 버튼이 보여지지 않는다.
나니까!
나를 어떻게 팔로우해!!
<!--user는 로그인 유저와
user_info는 보고있는 프로필페이지 유저
가 다르다면, -->
{% if user != user_info%}
<div class="col-4">
<a href="" class="btn btn-secondary btn-sm">팔로우</a>
</div>
{% endif %}
<!--user는 로그인 유저와
user_info는 보고있는 프로필페이지 유저
가 다르다면, -->
{% if user != user_info%}
<div class="col-4">
<a href="{% url 'accounts:follow' username=user_info.username %}" class="btn btn-secondary btn-sm">팔로우</a>
</div>
{% endif %}
accountsurls.py
path('<str:username>/follow',views.follow,name='follow'),
accountsviews.py
from django.contrib.auth.decorators import login_required
@login_required # 로그인한사람만 보여지게
def follow(request,username):
User = get_user_model()
me = request.user # 현재 로그인한사람(나자신)
# you를 하려면 get_user_model()을 윗칸에다 불러와서 저정해줘야함
you = User.objects.get(username=username)
# 지금 프로필사진 누른 계정
# 팔로잉이 이미 되어있는 경우
# 내가 그 계정의 팔로워들 목록에 있나요?
# follower 는 따르는 사람 목록
# following 은 따르는 사람
if you in me.followings.all():
# if me in you.followers.all() : 와 같다.
me.followings.remove(you)
else:
me.followings.add(you)
return redirect('accounts:profile', username=username)
profile.html
{% if user != user_info%}
<div class="col-4">
{% if user in user_info.followers.all %} <!--if user_info in user.이하 동일-->
<a href="{% url 'accounts:follow' username=user_info.username %}" class="btn btn-primary btn-sm">팔로잉</a>
{% else %}
<a href="{% url 'accounts:follow' username=user_info.username %}" class="btn btn-secondary btn-sm">팔로우</a>
{% endif %}
</div>
{% endif %}
내 계정일때, 내 프로필 페이지
내 계정일때, 다른사람 프로필 페이지
그리고 다른사람 프로필페이지[팔로우]누르면 -> [팔로잉]-> [팔로우]
현재 1:N 과 M:N 형성됨
profile.html
<div class="row">
<div class="col">게시물 {{user_info.post_set.all|length}} </div>
<div class="col">팔로워 {{user.info.followers.all|length}}</div>
<div class="col">팔로우 {{user_info.followings.all|length}}</div>
</div>


postsforms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
#fields = '__all__'
exclude = ('user','like_users',)
create 누르게되면