[0531] TIL 35일차

nikevapormax·2022년 5월 30일
0

TIL

목록 보기
33/116

😂 Django 5주차

😭 섬세한 리터치(회원가입 및 로그인)

- 회원가입 시 에러 발생 오류 수정

  • 회원가입 시 아무 내용도 입력하지 않고 회원가입 버튼을 누르게 되면 바로 에러 페이지가 나오게 된다.
  • 에러 페이지가 아닌 회원가입 페이지가 다시 나오도록 수정해 사용자가 회원가입에 집중할 수 있도록 유도해야 겠다는 생각을 했다.

- 에러의 원인 찾기

  • 회원가입에 대한 동작을 담당하는 파일인 user/views.py에서 회원가입을 담당하는 함수인 sign_up_view를 검사해 보았다.

    • 현재 아래 부분의 코드때문에 에러가 나고 있었다.

      username = request.POST.get('username', None)
       password = request.POST.get('password', None)
       password2 = request.POST.get('password2', None)
       bio = request.POST.get('bio', None)
    • username의 경우만 하더라도, 빈 칸으로는 절대 저장될 수가 없다. 그러나 우리는 빈 칸을 허용해주고 있었다. 그러므로 해당 부분을 포함해 아래 부분에 틀린 구간에 대한 에러 메세지를 같이 작성해 주었다.

           elif request.method == 'POST':
           username = request.POST.get('username', '')
           password = request.POST.get('password', '')
           password2 = request.POST.get('password2', '')
           bio = request.POST.get('bio', '')
      
           # 만약 이 둘이 같지 않다면
           if password != password2:
               # 다시 회원가입 화면을 보여준다.
               # 패스워드가 같지 않다고 알람!
               return render(request, 'user/signup.html', {'error': '비밀번호를 확인해 주세요!!'})
           else:
               if username == '' or password == '':
                   return render(request, 'user/signup.html', {'error': '사용자 이름과 비밀번호는 필수입니다!!'})
      
               # 회원가입을 시도한 사용자의 아이디가 중복되지 않는지 확인해야 됨!!!
               not_new_user = get_user_model().objects.filter(username=username)
      
               if not_new_user:
                   return render(request, 'user/signup.html', {'error': '사용자가 존재합니다.'})
               else:
                   # create_user 함수를 사용해 이전의 5줄짜리 코드를 한 줄로 축소시킴
                   UserModel.objects.create_user(username=username, password=password, bio=bio)
      
           # 회원가입이 마무리되었으니 redirect를 써서 로그인 페이지로 보내주자!!
           return redirect('/sign-in')
  • templates/user/signup.html을 에러 메세지를 나타낼 수 있도록 수정하였다.

<div class="form-group mt-2 mb-2">
    <label for="bio">나를 한마디로</label>
    <input type="text" class="form-control" id="bio" name="bio">
</div>
<!-- 에러 메세지를 보여줄 수 있도록 해주는 조건문 -->
{% if error %}
     <div class="alert alert-danger" role="alert">
          {{ error }}
     </div>
{% endif %}
<hr>
<div style="float: right">
     <button type="submit" class="btn btn-primary">회원가입</button>
     <a href="/sign-in" class="btn btn-secondary">로그인 페이지로</a>
</div>
  • 이제 다시 화면으로 돌아가 빈 값에 회원가입 키를 눌러보도록 하겠다.
  • 이제 화면도 넘어가지 않고, 어떤 부분에서 문제가 생겼는지를 알 수 있어 좀 더 사용자 친화적인 서비스가 되었다고 생각한다.

- 로그인 시 사용자에게 피드백 제공

  • 현재까지 우리는 로그인을 하다가 아이디 혹은 비밀번호가 틀리게 되면 그냥 화면이 띡하고 새로고침이 되었다.
  • 나야 내가 만든거니까 뭐가 잘못된거같다고 유추를 하면서 고치지만, 실제 다른 사용자의 입장에서는 뭐하자는거야?라는 반응이 나올 수 있을거 같다는 생각이 들었다.
  • 따라서 이에 대한 피드백을 주는 방법을 작성하기로 했다.

- 에러의 원인 찾기

  • 현재 보이는 코드에서 틀린 부분들을 수정하도록 하겠다.
def sign_in_view(request):
    if request.method == 'POST':
        # 사용자가 화면에서 입력한 값들을 가져와서 각 변수에 저장
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)

        # authenticate : 사용자가 입력한 비밀번호와 암호화된 비밀번호가 일치하는지, 사용자와 관련있는 비밀번혼지도 검사
        me = auth.authenticate(request, username=username, password=password)

        # 이제는 me 값에 모든 정보가 담겨있기 때문에 이 값이 있는지 없는지만 판단하면 됨
        if me is not None:
            # 사용자를 로그인 시켜주면 된다.
            auth.login(request, me)
            # 여기로 들어오면 기본 url을 가진 곳으로 가게 된다.
            # 그곳이 바로 tweet/urls.py에 있는 views.home을 실행시키는 부분이다.
            return redirect('/')
        else:
            # 로그인에 실패하면 다시 로그인 시도를 할 수 있게 로그인 페이지로 보내줌
            return redirect('/sign-in')
  • 먼저 회원가입과 마찬가지로 username과 password를 받아오는 값에 있는 None을 없애고 ''로 채워주었다. 그리고 에러 메세지를 돌려줘야 하므로 사용자가 로그인 시도에 실패하게 되었을 때 url로 redirect하는 방법을 render로 바꾸었다.
    if request.method == 'POST':
        # 사용자가 화면에서 입력한 값들을 가져와서 각 변수에 저장
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')

        # authenticate : 사용자가 입력한 비밀번호와 암호화된 비밀번호가 일치하는지, 사용자와 관련있는 비밀번혼지도 검사
        me = auth.authenticate(request, username=username, password=password)

        # 이제는 me 값에 모든 정보가 담겨있기 때문에 이 값이 있는지 없는지만 판단하면 됨
        if me is not None:
            # 사용자를 로그인 시켜주면 된다.
            auth.login(request, me)
            # 여기로 들어오면 기본 url을 가진 곳으로 가게 된다.
            # 그곳이 바로 tweet/urls.py에 있는 views.home을 실행시키는 부분이다.
            return redirect('/')
        else:
            # 로그인에 실패하면 다시 로그인 시도를 할 수 있게 로그인 페이지로 보내줌
            return render(request, 'user/signin.html', {'error': '사용자 이름 혹은 비밀번호를 확인해주세요!'})
  • templates/user/signin.html에도 에러 메세지를 넣을 수 있는 부분을 만들어주도록 하겠다.
<!-- 상단 생략 -->
 <div class="form-group mt-2 mb-2">
      <label for="password">비밀번호</label>
      <input type="password" class="form-control" id="password" name="password">
 </div>
 <hr>
 {% if error %}
      <div class="alert alert-danger" role="alert">
           {{ error }}
      </div>
 {% endif %}
  <div style="float: right">
       <button type="submit" class="btn btn-primary">로그인</button>
       <a href="/sign-up" class="btn btn-secondary">회원가입 페이지로</a>
  </div>
<!-- 하단 생략 -->
  • 이제 화면으로 돌아가 확인해보도록 하겠다.

😭 섬세한 리터치(nav 바 및 글쓰기)

- 글쓰기

  • 현재 우리의 서비스는 글에 아무런 내용이 없어도 등록이 되고 있다.

  • 이 현상을 수정해보도록 하겠다. 먼저 글쓰기와 관련된 곳을 찾아보니 tweet/views.py가 있었다.

  • tweet/views.pytweet 함수를 통해 우리는 글을 작성하고 있었으며, 글을 작성하는 것은 POST 방식의 통신이기 때문에 POST 쪽을 아래와 같이 수정하기로 하였다.

    • redirect를 render로 수정해 에러 메세지를 프론트로 보낼 수 있도록 하였고, 동시에 원래 작성되어 있던 글의 내용은 보여져야 하니 해당 내용도 같이 보내주었다.
    • my_tweet 변수를 다시 설정해 5줄이었던 저장 코드를 2줄로 축소하였다.
def tweet(request):
    # 상단 GET 방식 생략
 
    elif request.method == 'POST':
        # 지금 로그인하고 있는 사용자의 정보 전체를 들고 온다.
        user = request.user
        content = request.POST.get('my-content', '')

        if content == '':
            # 글쓰는거에 오류가 나더라도 이전에 보여주었던 내용들은 보여줘야 하기 때문에 all_tweet 씀
            all_tweet = TweetModel.objects.all().order_by('-created_at')
            return render(request, 'tweet/home.html', {'error': '글은 공백일 수 없습니다.', 'tweet': all_tweet})
        else:
            my_tweet = TweetModel.objects.create(author=user, content=content)
            my_tweet.save()
            return redirect('/tweet')
  • 그 다음 에러 메세지를 표시해 줄 templates/tweet/home.py를 수정해 주었다.
<!-- 상단 생략 -->

 <form action="/tweet/" method="post">
      <!-- 암호화 정보 전송 -->
      {% csrf_token %}
      {% if error %}
           <div class="alert alert-danger" role="alert">
                 {{ error }}
           </div>
      {% endif %}
      <div class="form-group mb-2">
           <textarea class="form-control" style="resize: none" name='my-content' id="my-content"></textarea>
      </div>
      <button type="submit" class="btn btn-primary" style="float:right;">작성하기</button>
 </form>
  • 이제 화면으로 돌아가 확인을 해보도록 하겠다.

- 상단 네비게이션 바 '친구' 버튼

  • 상단 네이게이션 바는 templates/base.html에 있다. 따라서 해당 문서의 수정을 통해 로그인을 하지 않은 경우에는 해당 버튼이 보이지 않도록 조치하도록 하겠다.
<!-- 상단 생략 -->

<div class="container">
        <a class="navbar-brand" href="/">SpartaSNS</a>
        {% if user.is_authenticated %}
            <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>
        {% endif %}

<!-- 하단 생략 -->
  • 다시 화면으로 돌아가 로그인을 하지 않았을 때와 로그인을 했을 때를 서로 비교해 보겠다.

    • 로그인 안함

    • 로그인 함


😭 게시글에 태그 달기

- django taggit 다운로드

  • 터미널에서 아래의 명령어를 통해 taggit을 다운로드 받는다.
$ pip install django-taggit
$ pip install django-taggit-templatetags2

- mySpartaSns/settings.py에게 알려주기

  • 위의 모듈을 다운받은 사실을 django에게 알려주어 에러가 나지 않도록 한다.

# 상단 생략

INSTALLED_APPS = [
    # 우리가 생성한 app 추가
    'user',
    'tweet',
    'restaurant',
    'taggit.apps.TaggitAppConfig',
    'taggit_templatetags2',

    # django의 기본 app
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

TAGGIT_CASE_INSENSITIVE = True
TAGGIT_LIMIT = 50

# 하단 생략

- TweetModel에 taggit 적용


#### tweet.models.py ####

# 태그를 추가할 수 있게 해주는 모듈
from taggit.managers import TaggableManager

# 게시글 모델
class TweetModel(models.Model):
    class Meta:
        db_table = "tweet"

    # 외래키를 사용해 다른 모델의 요소를 사용한다.
    author = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    content = models.CharField(max_length=256)
    # blank=True : 태그가 없어도 괜찮다라고 알려주는 것. 없으면 무조건 해야함
    tags = TaggableManager(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
# 하단 생략

- TweetModel에서 추가된 부분 db에 반영

$ python manage.py makemigrations 
$ python manage.py migrate

- tweet/views.py 수정

  • 우리는 글을 입력할 때 태그를 같이 입력할 것이기 때문에 tweet 함수를 수정해주도록 하겠다.
from django.views.generic import ListView, TemplateView

# 상단 생략

def tweet(request):
    # 화면을 보여주는 것이니까 GET 메서드
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            # 모든 트윗의 내용을 불러오는데 최신순으로 불러오기 위해서 시간의 역순을 해준다.
            all_tweet = TweetModel.objects.all().order_by('-created_at')
            # {'tweet': all_tweet}을 사용해서 화면에 우리의 트윗 데이터를 다 넘겨줬다.
            return render(request, 'tweet/home.html', {'tweet': all_tweet})
        else:
            return redirect('/sign-in')
    elif request.method == 'POST':
        # 지금 로그인하고 있는 사용자의 정보 전체를 들고 온다.
        user = request.user
        content = request.POST.get('my-content', '')
        # 태그에 대한 데이터들을 받아옴. 그리고 콤마를 기준으로 태그들을 구분함
        tags = request.POST.get('tag', '').split(',')

        if content == '':
            # 글쓰는거에 오류가 나더라도 이전에 보여주었던 내용들은 보여줘야 하기 때문에 all_tweet 씀
            all_tweet = TweetModel.objects.all().order_by('-created_at')
            return render(request, 'tweet/home.html', {'error': '글은 공백일 수 없습니다.', 'tweet': all_tweet})
        else:
            my_tweet = TweetModel.objects.create(author=user, content=content)
            # 태그들이 하나 이상으로 들어올 것이기 때문에 for문을 사용해서 각 태그들에 대한 처리를 진행
            for tag in tags:
                # 태그에 공백이 있다면 그 공백을 다 없애서 tag 변수에 저장
                tag = tag.strip()
                # 만약에 tag의 내용이 있다면 TweetModel.tags에 저장을 진행한다.
                # my_tweet은 뭐야? TweetModel의 테이블 명(Meta로 정한거)
                if tag != '':
                    my_tweet.tags.add(tag)
            my_tweet.save()
            return redirect('/tweet')
            
# 하단 생략           
 
class TagCloudTV(TemplateView):
    template_name = 'taggit/tag_cloud_view.html'


class TaggedObjectLV(ListView):
    template_name = 'taggit/tag_with_post.html'
    model = TweetModel

    def get_queryset(self):
        return TweetModel.objects.filter(tags__name=self.kwargs.get('tag'))

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['tagname'] = self.kwargs['tag']
        return context

- tweet/urls.py 수정

#### tweet/urls.py ####


from django.urls import path
from . import views

urlpatterns = [
    # 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
    path('', views.home, name='home'),
    # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
    path('tweet/', views.tweet, name='tweet'),
    # 게시글 삭제
    path('tweet/delete/<int:id>', views.delete_tweet, name='delete-tweet'),
    # 상세 페이지로 이동
    path('tweet/<int:id>', views.tweet_detail, name='tweet-detail'),
    # 댓글 달기
    path('tweet/comment/<int:id>', views.write_comment, name='write-comment'),
    # 댓글 삭제
    path('tweet/comment/delete/<int:id>', views.delete_comment, name='delete-comment'),
    path('tag/', views.TagCloudTV.as_view(), name='tag_cloud'),
    path('tag/<str:tag>/', views.TaggedObjectLV.as_view(), name='tagged_object_list'),
]

- templates/tweet/home.html 수정


<!-- 상단 생략 -->
<div class="form-group mb-2">
     <textarea class="form-control" style="resize: none" name='my-content' id="my-content"></textarea>
     <div class="mt-3 row">
          <label for="tag"class="col-sm-2 col-form-label">이 글의 태그</label>
          <div class="col-sm-10">
               <input type="text" class="form-control" name="tag" id="tag" placeholder="콤마(,)로 구분 해 주세요">
          </div>
     </div>
</div>

<!-- 하단 생략 -->

<div class="media-body">
    <h5 class="mt-0">{{ tw.content }}</h5>
    {% if tw.tags.all %}
         {% for tag in tw.tags.all %}
              <a style="text-decoration: none" href="{% url 'tagged_object_list' tag.name %}">
              <span class="badge rounded-pill bg-success">
                   {{ tag.name }}
              </span>
              </a>
         {% endfor %}
         -<a style="text-decoration: none" href="{% url 'tag_cloud' %}">TagCloud</a>
     {% endif %}
</div>

- templates/tweet/tweet_detial.html 수정

<div class="media-bod">
    <h5 class="mt-0">{{ tweet.content }}</h5>
    {% if tweet.tags.all %}
        {% for tag in tweet.tags.all %}
            <a style="text-decoration: none" href="{% url 'tagged_object_list' tag.name %}">
                <span class="badge rounded-pill bg-success">
                      {{ tag.name }}
                </span>
            </a>
         {% endfor %}
         -<a style="text-decoration: none" href="{% url 'tag_cloud' %}">TagCloud</a>
    {% endif %}
 </div>

- templates에 태그 관련 폴더 및 파일 생성

  • 위와 같이 폴더 및 파일을 세팅하고 내용을 작성하였다.

- 화면 확인

  • 갓장고,,

😭 Github에 코드 올리기

  • 올리기 전 진행해야 할 필수 사항
    • db.sqlite3 삭제
    • 각 폴더의 migrations 폴더에 __init__.py만 남기고 지우기
    • mySpartaSns의 settings.py를 아래와 같이 수정
      DEBUG = False
       ALLOWED_HOSTS = ["*"]
profile
https://github.com/nikevapormax

0개의 댓글