회원가입에 대한 동작을 담당하는 파일인 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')
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>
<!-- 하단 생략 -->
현재 우리의 서비스는 글에 아무런 내용이 없어도 등록이 되고 있다.
이 현상을 수정해보도록 하겠다. 먼저 글쓰기와 관련된 곳을 찾아보니 tweet/views.py
가 있었다.
tweet/views.py
의 tweet
함수를 통해 우리는 글을 작성하고 있었으며, 글을 작성하는 것은 POST
방식의 통신이기 때문에 POST
쪽을 아래와 같이 수정하기로 하였다.
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 %}
<!-- 하단 생략 -->
다시 화면으로 돌아가 로그인을 하지 않았을 때와 로그인을 했을 때를 서로 비교해 보겠다.
로그인 안함
로그인 함
$ pip install django-taggit
$ pip install django-taggit-templatetags2
# 상단 생략
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
# 하단 생략
#### 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)
# 하단 생략
$ python manage.py makemigrations
$ python manage.py migrate
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 ####
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'),
]
<!-- 상단 생략 -->
<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>
<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>
db.sqlite3
삭제__init__.py
만 남기고 지우기settings.py
를 아래와 같이 수정DEBUG = False
ALLOWED_HOSTS = ["*"]