[0530] TIL 34일차

nikevapormax·2022년 5월 30일
0

TIL

목록 보기
32/116

🙍‍♂️ 헷갈리는 개념 정리

🙅‍♂️ render

render(request, template_name, context=None, ...)

  • requesttemplate_name(html 파일명) 은 필수작성 사항
  • 즉 render는 화면에 html 파일을 띄우는 것이라 생각하면 됨!
  • context : 화면에 원하는 인자를 띄워줌.
    • 딕셔너리 형태로 사용하며 {html에서 사용할 이름 : views.py에서의 변수명} 과 같이 작성

🙅‍♂️ redirect

redirect(url 주소, permanent=False, **args, ***kwargs)

  • 단지 주어진 url로 이동하는 것이므로(상대 / 절대 모두 가능) render와 같이 원하는 값들은 넘기지 못한다.

🙋‍♂️ 차이점

  • 진짜 단순하게 생각해서 render는 html 파일로 이동하고, redirect는 url을 통해 이동하는 것이다.
  • 하지만 django에서 url을 통해 이동한다는 것은 생각보다 고려할 것이 많다 생각한다.
    • url을 쓴다는 것은 urls.py에 url이 등록되어 있어야 한다는 점
    • urls.py에 url이 등록되어 있다면 이와 맞물린 함수가 있다는 점
    • 해당 함수는 views.py에서 선언이 되어 있어야 한다는 점
    • 또 views.py의 해당 함수 안에서 render를 할 것인지 redirect를 할 것인지 결정할 수 있다는 점

😂 Django 4주차

😭 데이터베이스 관계

🥲 one-to-many

  • 간단하게 생각하자면 한 명의 글쓴이가 여러 개의 글을 보유하고 있을 수는 있지만, 하나의 글이 여러 명의 글쓴이를 보유할 수 없는 것이다.
  • 우리의 mySpartaSns에서는 모델을 생성할 때 사용한 ForeignKey가 이에 해당한다. 아래의 코드를 한 번 보도록 하겠다.
class UserModel(AbstractUser):
    class Meta:
        db_table = "my_user"

    bio = models.TextField(max_length=500, blank=True)


class TweetModel(models.Model):
    class Meta:
        db_table = "tweet"

    author = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    content = models.CharField(max_length=256)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


class TweetComment(models.Model):
    class Meta:
        db_table = "comment"
    tweet = models.ForeignKey(TweetModel, on_delete=models.CASCADE)
    author = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    comment = models.CharField(max_length=256)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
  • my_user 모델은 사용자를 정의하는 모델인데, 지금 보면 tweetcomment 모델에서 모두 author라는 변수명으로 my_user의 모든 내용을 가져다 쓰고 있다.
  • 여기서 사용자가 여러 개의 게시글과 여러 개의 댓글을 달게 되면 한 명의 사용자가 다수의 게시글과 댓글을 보유하게 된다.
  • 이 때, one-to-many 관계가 성립된다.
  • tweet이랑 comment 모델도 one-to-many 관계가 성립된다!

🥲 one-to-one

  • 이 관계는 위의 관계와 좀 다르게 딱 하나의 관계만 가질 수 있다.
  • 어떤 두 개의 모델이 서로 관계를 맺으면서 중복되지 않는 값을 발생시키는 것이다.
  • 우리가 만들고 있는 sns 모델에서 찾아보자면 프로필 기능인것 같다. 물론 요즘에는 멀티 프로필이 가능하지만, 결국 하나의 아이디에 하나의 프로필인 것은 똑같으니 적당한 예시라고 생각한다.
  • OneToOneField를 사용해 작성하게 된다.
class UserModel(AbstractUser):
	class Meta:
        db_table = "my_user"

    bio = models.TextField(max_length=500, blank=True)

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    user_pk = models.IntegerField(blank=True)
    nickname = models.CharField(max_length=200, blank=True)
    point = models.IntegerField(default=0)
    phone = models.CharField(max_length=200, blank=True)

🥲 many-to-many

  • 두 개의 모델이 서로 n개씩의 관계를 가질 수 있다.
  • 아래의 예시처럼 피자를 생각해보면, 재료 입장에서 생각해보면 재료들이 여러 피자에 들어가게 되고, 반대로 피자의 입장에서 생각해보면 피자들에 여러 재료들이 들어오게 된다.
  • ManyToManyField를 사용한다.
  • 물론 둘 다 서로 n개씩이라면서 왜 한 쪽에만 ManyToManyField을 쓰냐고 생각할 수 있다. django에서는 한 쪽에만 입력해주면 알아서 다 해주니 걱정할 필요 없다.
class MyTopping(models.Model):
    topping_name = models.CharField(max_length=100)

class MyPizza(models.Model):
    pizza_name = models.CharField(max_length=100)
    pizza_topping = models.ManyToManyField(MyTopping)

😭 many-to-many 연습

- 모델 등록

  • many-to-many를 연습하기 위해 restaurant app을 생성하였다.
  • 먼저 터미널을 통해서 app을 만들도록 하겠다.
$ django-admin startapp restaurant
  • 그리고 mySpartaSns/settings.py로 가서 내가 설치한 app이 무엇인지 django에게 알려주도록 하겠다.
#### mySpartaSns/settings.py ####

# 상단 생략

INSTALLED_APPS = [
    # 우리가 생성한 app 추가
    'user',
    'tweet',
    'restaurant',
    
    # django의 기본 app
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

# 하단 생략
  • restaurant/models.py에 실습에 사용할 모델들을 만들도록 하겠다.
# restaurant/models.py
from django.db import models


class MyTopping(models.Model):
    class Meta:
        db_table = "my_topping"

    def __str__(self):
        return self.topping_name

    topping_name = models.CharField(max_length=100)


class MyPizza(models.Model):
    class Meta:
        db_table = "my_pizza"

    def __str__(self):
        return self.pizza_name

    pizza_name = models.CharField(max_length=100)
    pizza_topping = models.ManyToManyField(MyTopping)
  • 그리고 해당 모델들을 데이터베이스에 적용시키도록 하겠다.
$ python manage.py makemigrations
$ python manage.py migrate

  • 데이터베이스 창에서 생성된 데이터베이스를 확인해보도록 하겠다.
  • 이제 위의 데이터베이스를 편하게 확인하기 위해서 resaturant/admin.py에 등록하도록 하겠다.
#### resaturant/admin.py ####

from django.contrib import admin
from .models import MyPizza, MyTopping


admin.site.register(MyPizza)
admin.site.register(MyTopping)
  • 방금 생성한 app까지 다룰 수 있는 슈퍼계정을 다시 만들도록 하자.
$ python manage.py createsuperuser
  • 그리고 서버로 들어가 로그인을 하도록 하겠다.
    http://127.0.0.1:8000/admin
  • 그리고 방금 생성한 app에 들어가 토핑을 하나 추가해 봤다.
  • 그런데 에러가 발생한 것을 볼 수 있다.
  • 트러블슈팅 고고~~
    • 파이참으로 돌아가서 원래 있었던 db.sqlite3 삭제
    • 이제 만들어진 모든 app으로 들어가 migrations 폴더에서 __init__.py 파일을 제외한 다른 2 개의 파일을 전부 삭제
    • 데이터베이스 창에 나와있는 데이터베이스도 모두 다 삭제
    • 이렇게 지우는 이유는 우리가 django의 auth_user를 끌어다 쓰면서 관리자에 대한 제대로된 세팅이 이루어지지 않았기 때문이며, 그래서 지우고 다시 세팅을 진행

- 데이터베이스 재설정

  • 이제 초심으로 돌아가서 다시 db를 생성하면 된다.
$ python manage.py makemigrations
$ python manage.py migrate


  • 이러면 db.sqlite3가 다시 생기게 되고, 이거를 오른쪽의 데이터베이스 창에 드래그해주면 다시 생성이 된다.
  • 이제 다시 슈퍼계정을 만들어주자.
$ python manage.py createsuperuser
  • admin 페이지에 로그인을 해보면 화면에 db가 성공적으로 반영된 것을 볼 수 있다.
  • 그리고 다시 토핑을 추가해보면 아주 잘 되는 것을 볼 수 있다.
  • 토핑을 좀 더 추가하고, My pizza로 가서 피자를 만들어 보도록 하겠다.
  • 성공적으로 저장되었다.

😭 django shell 활용

  • django를 실행하지 않고도 기능들을 사용할 수 있도록 도와주는 도구

- django shell 실행

$ python manage.py shell

- restaurant app의 모델 활용

  • 우리가 admin 페이지에서 넣은 피자를 확인해보도록 하겠다. (현재 django shell 실행중이며, 터미널에서 작업하는 거임!)
  • 피자의 입장에서 자신에게 들어가 있는 모든 토핑 불러오기
    • 1 : restaurant app에 있는 모델 중 MyTopping, MyPizza 사용
    • 2 : MyPizza 안에 있는 모든 object를 불러옴
    • 3 : MyPizza 중 이름이 domino인 것을 불러옴
    • 4 : 이름이 papajones인 피자에 추가되어 있는 topping들을 전부 불러옴
1 >>> from restaurant.models import MyTopping, MyPizza
2 >>> MyPizza.objects.all()
      <QuerySet [<MyPizza: domino>, <MyPizza: pizzahut>, <MyPizza: papajones>]>
3 >>> MyPizza.objects.get(pizza_name='domino')
      <MyPizza: domino>
4 >>> MyPizza.objects.get(pizza_name='papajones').pizza_topping.all()
      <QuerySet [<MyTopping: cheese>, <MyTopping: pepparoni>, <MyTopping: onion>]>
  • 이제 토핑의 입장에서 본인이 들어간 모든 피자 불러오기
    • 1 : restaurant app에 있는 모델 중 MyTopping, MyPizza 사용
    • 2 : MyTopping 안에 있는 모든 object를 불러옴
    • 3 : MyTopping 중 이름이 cheese인 것을 불러옴
    • 4 : MyToppingcheese가 포함된 모든 피자 불러옴
    • 5 : MyToppingpepparoni가 포함된 모든 피자 불러옴
1 >>> from restaurant.models import MyTopping, MyPizza
2 >>> MyTopping.objects.all()
      <QuerySet [<MyTopping: cheese>, <MyTopping: pepparoni>, <MyTopping: onion>, <MyTopping: cow>, <MyTopping: chicken>, <MyTopping: shirimp>, <MyTopping: olive>]>
3 >>> MyTopping.objects.get(topping_name='cheeze')
      <MyTopping: cheese>
4 >>> MyTopping.objects.get(topping_name='cheese').mypizza_set.all()
      <QuerySet [<MyPizza: domino>, <MyPizza: pizzahut>, <MyPizza: papajones>]>
5 >>> MyTopping.objects.get(topping_name='pepparoni').mypizza_set.all()
      <QuerySet [<MyPizza: pizzahut>, <MyPizza: papajones>]>

😭 팔로우/팔로잉 기능

  • 위에서 실습했던 many-to-many 관계를 사용해서 작성해보자.

- 모델 수정

  • 우선 우리는 follow에 필요한 모델로 기존 모델을 수정해주어야 한다.
#### user/models.py ####

from django.db import models
# django에서 기본적으로 제공하는 auth_user와 연동되는 클래스인 AbstractUser 사용
from django.contrib.auth.models import AbstractUser
# django가 관리하는 곳(메인 app)에 있는 settings를 불러옴
from django.conf import settings

# 내가 생성한 user model
class UserModel(AbstractUser):
    # 데이터베이스에 반영되는 model의 이름 작성
    class Meta:
        db_table = "my_user"

    # auth_user에 있는 내용들을 다 지워줌
    # 상태 메세지
    bio = models.CharField(max_length=256, default='')

    # follow 모델
    # 내가 팔로우하는 사람은 다른 모델이 아닌 사용자 모델에 있는 사용자이기 때문에 AUTH_USER_MODEL을 가져다 씀
    # AUTH_USER_MODEL : 우리가 튜닝한 유저 모델이고 djangoProject/settings.py의 가장 아래부분에 우리가 추가했엄
    # 내가 팔로우하는 사람들의 명단이 follow 안에 들어가는 것이고, 그 사람들 입장에서는 내가 followee가 된다.
    # UserModel.followee : UserModel을 팔로우하는 사람들을 불러줌
    # UserModel.follow : 내가 팔로우하는 사람들을 불러줌
    follow = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='followee')

- 데이터베이스 등록

  • 모델이 수정된 사항을 django에게 알려주어야 한다.
$ python manage.py makemigrations
$ python manage.py migrate

  • 데이터베이스에 등록을 하고 확인을 해보면 테이블이 여러 개 생긴 것을 알 수 있다. many-to-many 관계의 경우 참조할 것이 많다보니 자동적으로 생기는 것이므로 신경쓸 필요 없다.

- admin 계정으로 접속해 follow 적용

  • 우리가 만든 회원가입 기능으로 user를 많이 만들어 놓고, admin 계정으로 접속해 superuser에게 3명의 follow를 걸어보았다.

- user/views.py 수정

  • follow와 관련된 함수들을 추가해주었다.
@login_required
def user_view(request):
    if request.method == 'GET':
        # exclude : 해당하는 데이터에서 조건에 맞는 데이터를 제거해주는 함수
        # 사용자를 불러오기, exclude와 request.user.username 를 사용해서 '로그인 한 사용자'를 제외하기
        user_list = UserModel.objects.all().exclude(username=request.user.username)
        return render(request, 'user/user_list.html', {'user_list': user_list})


@login_required
def user_follow(request, id):
    # 나(로그인한 사용자)
    me = request.user
    # 내가 팔로우하려고 방금 누른 사람
    click_user = UserModel.objects.get(id=id)
    # 팔로우를 당한 사람(click_user)도 UserModel의 정보를 가져와 쓰기 때문에 아래와 같이 쓸 수 있다.
    # 만약 click_user를 팔로우하는 사람의 명단에 내가 있다면
    if me in click_user.followee.all():
        # 나를 명단에서 제외해줘(팔로우가 된 상태에서 팔로우를 또 누르면 팔로우가 취소되니까)
        click_user.followee.remove(request.user)
    else:
        # 내가 명단에 없다면 나를 팔로우하는 사람들의 명단에 넣어줘
        click_user.followee.add(request.user)
    return redirect('/user')

- user/urls.py 수정

from django.urls import path
from . import views

urlpatterns = [
    path('sign-up/', views.sign_up_view, name='sign-up'),
    path('sign-in/', views.sign_in_view, name='sign-in'),
    path('logout/', views.logout, name='logout'),
    path('user/', views.user_view, name='user-list'),
    path('user/follow/<int:id>', views.user_follow, name='user-follow'),
]

- templates/user/user_list.html 생성

  • 내가 팔로우하는 사람들의 수 또는 나를 팔로우하는 사람들의 수 등의 정보를 보여주는 페이지
<!-- templates/user/user_list.html -->
{% extends 'base.html' %}
{% block title %}
    사용자 리스트
{% endblock %}

{% block content %}
    <div class="container timeline-container">
        <div class="row">
            <!-- 왼쪽 컬럼 -->
            <div class="col-md-3">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">{{ user.username }}</h5>
                        <p class="card-text"> {{ user.bio }}</p>

                    </div>
                </div>
            </div>
            <!-- 오른 쪽 컬럼-->
            <div class="col-md-7">
                <div class="row">
                    <div class="alert alert-success" role="alert">
                        나를 팔로우 하는 사람 수 : {{ user.followee.count }} 명 / 내가 팔로우 하는 사람 수 : {{ user.follow.count }} 명
                    </div>
                </div>
                <div class="row">
                    <!-- 사용자 리스트 반복문 -->
                    {% for ul in user_list %}
                        <div class="card">
                            <div class="card-body">
                                <h5 class="card-title">{{ ul.username }}</h5>
                                <h6 class="card-subtitle mb-2 text-muted">{{ ul.email }}</h6>
                                <p class="card-text">
                                    {{ ul.bio }}
                                </p>
                                <p class="card-text">
                                    팔로잉 {{ ul.follow.count }} 명 / 팔로워 {{ ul.followee.count }} 명
                                </p>
                                {% if ul in user.follow.all %}
                                    <a href="/user/follow/{{ ul.id }}" class="card-link">[팔로우 취소]</a>
                                {% else %}
                                    <a href="/user/follow/{{ ul.id }}" class="card-link">[팔로우]</a>
                                {% endif %}
                            </div>
                        </div>
                        <hr>
                    {% endfor %}
                </div>
            </div>
            <div class="col-md-2"></div>
        </div>
    </div>
{% endblock %}

- templates/base.html 수정

  • 상단 네비게이션 바의 친구 부분에 /user url을 걸어주었다.
<!-- templates/base.html -->

<!-- 상단 생략 -->

<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container">
        <a class="navbar-brand" href="/">SpartaSNS</a>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" href="/user"> 친구 <span class="sr-only"></span></a>
                </li>
            </ul>
        </div>


<!-- 하단 생략 -->

- 친구를 눌러 화면에 들어가 팔로잉 상태 확인

  • 위에서 작업한 내용을 확인하기 위해 직접 로그인해 들어가 보았다.
  • 이전에 admin 페이지에서 내가 팔로우하는 사람 셋을 선택하였었다.
  • 아래의 페이지 사진을 보면 팔로우가 잘 되어 있는 것을 알 수 있다.
profile
https://github.com/nikevapormax

0개의 댓글