티스토리 뷰
[django 인스타그램] 게시글 컨텐츠, 모델링, ContentCreateView API, 이미지 업로드, templates, dropzone
지휘리릭 2020. 2. 11. 17:20
게시글에 대한 앱 'contents' 을 만든다. settings.py INSTALLED_APPS 에 추가하는 것도 잊지 않기
python manage.py startapp contents
contents / models.py
인스타그램에 게시하는 게시물들 즉 컨텐츠에 대한 모델을 생성한다.
생성시간, 수정시간에 대한 추상화모델을 BaseModel로 만든다.
from django.db import models
from django.contrib.auth.models import User
import os
import uuid
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add = True)
modified_at = models.DateTimeField(auto_now = True)
class Meta:
abstract = True
컨텐츠에 대한 모델 Content 을 생성한다.
BaseModel을 상속받기 때문에, 생성시간, 수정시간에 대한 값을 자동으로 가져올 수 있다.
user : 작성자는 User에서 ForeignKey 외래키로 가져온다.
text : 작성한 글은 TextField로 가져온다. 이미지에 대한 값은 따로 모델을 생성해야한다.
class Content(BaseModel):
user = models.ForeignKey(User, on_delete = models.CASCADE)
text = models.TextField(default = '')
class Meta:
ordering = ['-created_at']
verbose_name_plural = "컨텐츠"
컨텐츠에서 이미지에 대한 모델 Image 을 생성한다.
UPLOAD_PATH : 게시물에 올린 이미지가 저장되는 경로의 이름 설정
content : 작성자와 게시글에 대한 정보가 저장된 Content 모델을 가져와서 Image 모델에서 하나의 필드값 content 로 저장될 수 있도록 한다.
image : 게시물에 올린 이미지에 대해서 저장되는 경로를 uploade_to 값으로 설정한다. image_upload_to 라는 함수에서 저장 경로를 만들도록 설정했다.
order : 저장되는 이미지의 넘버링 1,2,3,4.....
image_upload_to : 고유 파일 이름을 생성하여 정해진 경로에 저장되도록 하는 함수
ext : 확장자가 들어가게 된다. 맨뒤에서부터 점이 나올때까지므로 jpg, png 와 같은 확장자이름이 저장된다.
"%s.%s" %(uuid.uuid4(), ext) : 16자리의 고유한 아이디가 생성된다.
Image 클래스에서 지정한 저장 경로 UPLOAD_PATH에 파일이 저장된다.
def image_upload_to(instance, filename):
ext = filename.split('.')[-1]
return os.path.join(instance.UPLOAD_PATH, "%s.%s" % (uuid.uuid4(), ext))
class Image(BaseModel):
UPLOAD_PATH = 'user-upload'
content = models.ForeignKey(Content, on_delete = models.CASCADE)
image = models.ImageField(upload_to = image_upload_to)
order = models.SmallIntegerField()
class Meta:
unique_together = ['content', 'order']
ordering = ['order']
아래와 같이 파일이 저장된 곳에서 가져오는 경로를 설정해야 이미지가 제대로 업로드된다.
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
templates / home.html
메인 화면을 구성하는 템플릿.
헤더 부분으로 상단 메뉴바를 구성한다.
html 코드가 끝나고 가장 아래에 script 태그에 logoutButton 을 클릭했을 때에 수행해야하는 액션을 javascript 코드로 구성한다.
상단바에는 홈화면, 친구목록, 로그아웃 각각의 페이지로 이동할 수 있도록 메뉴바를 만든다.
그 아래에는 현재 로그인한 사용자의 이름을 출력한다.
여기까지하면 아래와 같이 메뉴 상단바가 완성된다.
{% extends 'base.html' %}
{% block head %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dropzone@5.5.1/dist/dropzone.min.css">
{% endblock %}
{% block body %}
<!--Header-->
<nav class = "fast-nav navbar navbar-expand-lg navbar-light bg-white" style = "width : 1000px; margin : 0 auto;">
<a class = "navbar-brand text-success col-sm-8" href = "{% url 'home' %}"><b>jiheestagram</b></a>
<div class = "collapse navbar-collapse col-sm-4" id = "navbarText" style = "display:inline-block !important; text-align:right;">
<span class = "navbar-text col-sm-3">
<a href = "{%url 'relation' %}">친구 목록</a>
</span>
<span class = "navbar-text col-sm-3">
<a href = "javascript:void(0);" id = "logoutButton">로그아웃</a>
</span>
</div>
</nav>
<!--username-->
<div style = "width:600px; margin:0 auto;">
<br><br>
<h2 class = "text-success">{{request.user}}</h2><br>
</div>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"> </script>
<script src="https://cdn.jsdelivr.net/npm/dropzone@5.5.1/dist/dropzone.min.js"></script>
그 다음에는, 게시글 작성 폼에 대한 코드를 작성한다.
dropzone 이라는 기능을 사용하여 이미지를 업로드한다.
작성하기 버튼을 눌렀을 때에 수행해야하는 동작은 script 태그에서 설정한다.
게시글 작성 폼은 'apis_content_create' 의 경로로 전달된다.
<br>
<!--Create Content-->
<div id = "formContainer" style = "width:600px; margin:0 auto;">
<form action = "{% url 'apis_content_create' %}" class = "dropzone" id = "uploader" style = "background-color:whitesmoke;">
<div class = "dz-message" data-dz-message><span>드래그 드랍하거나 클릭하여 이미지 선택</span></div>
<div class = "fallback"><input name = "file" type = "file"></div>
</form>
<div class = "form-group">
<label>아래에 글을 입력해주세요.</label>
<textarea class = "form-control rounded-0" id = "text" rows = "3"></textarea>
</div>
<div style = "text-align: right;">
<button type = "button" class = "btn btn-success" id = "upload">작성하기</button>
</div>
</div>
dropzone 에 대한 javascript 코드
작성하기 버튼의 id 인 upload 가 클릭되었을 때에 dropZone 기능이 실행된다.
<script>
Dropzone.autoDiscover = false;
var dropZoneUploader = new Dropzone('form#uploader', {
init: function(){
var dropzone = this;
$('#upload').click(function() {
dropZoneUploader.processQueue();
});
dropzone.on("sending", function(file, xhr, formData){
formData.append("text", $('#text').val());
});
},
parallelUploads : 10,
autoProcessQueue : false,
type : 'POST',
success : function(){
location.reload();
toastr.success("<h3>success</h3>");
},
error : function(e) {
console.log(e)
alert('오류가 발생했습니다. 다시 시도해주세요.');
},
acceptedFiles : ".jpeg, .jpg, .png, .gif",
uploadMultiple : true,
});
</script>
메뉴 상단바에 있는 로그아웃 버튼을 클릭했을 때의 액션으로, logout 경로로 이동하여 로그아웃을 수행하고 다시 로그인 페이지로 이동하게끔 한다.
이 때, 로그아웃 경로에서 앞에도 '/' 를 붙여야한다.
그냥 'apis/user/logout/' 으로 해버리면 현재 경로에 'apis/user/logout/' 가 더해진 경로로 이동하게 된다. 생각치도 못하게 로그아웃 기능이 실행되지 않아, 한참을 헤맸다.
<script>
$(document).ready(function(){
$('#logoutButton').click(function(){
$.get('/apis/user/logout/', {}, function(){
window.location = "{% url 'login' %}";
});
});
});
</script>
실행 결과
apis / views.py
왜 컨텐츠 생성 로직을 contents / views.py 경로에 만들지 않는 것일까?
게시글 생성 API 에 대한 로직이기 때문에 JSON 형태의 데이터로 주고받으려면 BaseView를 상속받아야하기 때문이다.
그래서 그냥 from contents.models import Content 로 필요한 모델만 가져오면 된다.
home.html 에서 게시글 작성폼을 ContentCreateView에서 넘겨받았다.
text : POST 요청된 폼에서 'text' 값을 가져와서 strip으로 양끝 공백을 없앤다.
content : 사용자와 텍스트 필드에 대한 Content 객체를 생성하여 content에 저장한다.
업로드한 이미지를 request.FILES.values으로 불러와 enumerate 함수를 사용하여 인덱스와 파일명을 나눈다.
위에서 저장한 content, 이미지, 인덱스 넘버에 대한 Image 객체를 생성한다.
@method_decorator(login_required, name = "dispatch")
class ContentCreateView(BaseView):
def post(self, request):
text = request.POST.get('text', '').strip()
content = Content.objects.create(user = request.user, text = text)
for idx, file in enumerate(request.FILES.values()):
Image.objects.create(content = content, image = file, order = idx)
return self.response({})
실행 결과
게시글을 작성한다.
작성하기 버튼 누르기 전
dropzone 기능이 제대로 실행된다.
작성하기 버튼 누른 후
ContentCreateView 진행 중
아래와 같이 idx 와 file 값이 저장된다.
게시글 생성 완료
이미지 업로드도 잘 되고 텍스트 작성자 모두 제대로 저장된다.
'django project > 인스타그램' 카테고리의 다른 글
- Total
- Today
- Yesterday
- django pythoneverywhere
- Firebase 데이터베이스 추천
- django 로그인접근
- UITableViewController Not Working
- CellForRowAt Not Called
- cleaned_data
- 실시간 데이터베이스
- 데이터베이스 추천
- pythonanywhere배포
- ModelForm Form 차이
- pythonanywhere배포방법
- iOS 데이터베이스
- iOS 화면 안나옴
- 까만 화면
- django 개발일지
- 장고 태그달기
- CellForRowAt 호출안됨
- django 게시판
- django clean
- django 태그
- iOS UITableView 출력안됨
- iOS 검은 화면
- 테이블출력안됨
- Django
- 장고 게시판
- 알파벳 카운팅
- 웹 배포
- django tag
- Realtime Database
- python 웹 배포
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |